5. 자료 구조¶
이 장에서는 여러분이 이미 배운 것들을 좀 더 자세히 설명하고, 몇 가지 새로운 것들을 덧붙입니다.
5.1. 리스트 더 보기¶
리스트 자료 형은 몇 가지 메서드들을 더 갖고 있습니다. 이것들이 리스트 객체의 모든 메서드 들입니다:
-
list.
append
(x) 리스트의 끝에 항목을 더합니다.
a[len(a):] = [x]
와 동등합니다.
-
list.
extend
(iterable) 리스트의 끝에 이터러블의 모든 항목을 덧붙여서 확장합니다.
a[len(a):] = iterable
와 동등합니다.
-
list.
insert
(i, x) 주어진 위치에 항목을 삽입합니다. 첫 번째 인자는 삽입되는 요소가 갖게 될 인덱스입니다. 그래서
a.insert(0, x)
는 리스트의 처음에 삽입하고,a.insert(len(a), x)
는a.append(x)
와 동등합니다.
-
list.
remove
(x) 리스트에서 값이 x 와 같은 첫 번째 항목을 삭제합니다. 그런 항목이 없으면
ValueError
를 일으킵니다.
-
list.
pop
([i]) 리스트에서 주어진 위치에 있는 항목을 삭제하고, 그 항목을 돌려줍니다. 인덱스를 지정하지 않으면,
a.pop()
은 리스트의 마지막 항목을 삭제하고 돌려줍니다. (메서드 시그니처에서 i 를 둘러싼 대괄호는 매개변수가 선택적임을 나타냅니다. 그 위치에 대괄호를 입력해야 한다는 뜻이 아닙니다. 이 표기법은 파이썬 라이브러리 레퍼런스에서 지주 등장합니다.)
-
list.
clear
() 리스트의 모든 항목을 삭제합니다.
del a[:]
와 동등합니다.
-
list.
index
(x[, start[, end]]) 리스트에 있는 항목 중 값이 x 와 같은 첫 번째 것의 0부터 시작하는 인덱스를 돌려줍니다. 그런 항목이 없으면
ValueError
를 일으킵니다.선택적인 인자 start 와 end 는 슬라이스 표기법처럼 해석되고, 검색을 리스트의 특별한 서브 시퀀스로 제한하는 데 사용됩니다. 돌려주는 인덱스는 start 인자가 아니라 전체 시퀀스의 시작을 기준으로 합니다.
-
list.
count
(x) 리스트에서 x 가 등장하는 횟수를 돌려줍니다.
-
list.
sort
(*, key=None, reverse=False) 리스트의 항목들을 제자리에서 정렬합니다 (인자들은 정렬 커스터마이제이션에 사용될 수 있습니다. 설명은
sorted()
를 보세요).
-
list.
reverse
() 리스트의 요소들을 제자리에서 뒤집습니다.
-
list.
copy
() 리스트의 얕은 사본을 돌려줍니다.
a[:]
와 동등합니다.
리스트 메서드 대부분을 사용하는 예:
>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4) # 위치 4에서부터 다음 banana 를 찾습니다
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'
아마도 여러분은 insert
, remove
, sort
같은 메서드들이 리스트를 수정할 뿐 반환 값이 출력되지 않는 것을 알아챘을 것입니다 -- 기본 None
을 돌려주고 있습니다. 1 이것은 파이썬에서 모든 가변 자료 구조들에 적용되는 설계 원리입니다.
아마도 여러분이 알아챘을 또 다른 사실은 모든 데이터를 정렬하거나 비교할 수는 없다는 것입니다. 예를 들어, 정수를 문자열과 비교할 수 없고 None을 다른 형과 비교할 수 없기 때문에 [None, 'hello', 10]
는 정렬되지 않습니다. 또한 정의된 대소 관계가 없는 형이 있습니다. 예를 들어, 3+4j < 5+7j
는 올바른 비교가 아닙니다.
5.1.1. 리스트를 스택으로 사용하기¶
리스트 메서드들은 리스트를 스택으로 사용하기 쉽게 만드는데, 마지막에 넣은 요소가 처음으로 꺼내지는 요소입니다 ("last-in, first-out"). 스택의 꼭대기에 항목을 넣으려면 append()
를 사용하세요. 스택의 꼭대기에서 값을 꺼내려면 명시적인 인덱스 없이 pop()
을 사용하세요. 예를 들어:
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
5.1.2. 리스트를 큐로 사용하기¶
리스트를 큐로 사용하는 것도 가능한데, 처음으로 넣은 요소가 처음으로 꺼내지는 요소입니다 ("first-in, first-out"); 하지만, 리스트는 이 목적에는 효율적이지 않습니다. 리스트의 끝에 덧붙이거나, 끝에서 꺼내는 것은 빠르지만, 리스트의 머리에 덧붙이거나 머리에서 꺼내는 것은 느립니다 (다른 요소들을 모두 한 칸씩 이동시켜야 하기 때문입니다).
큐를 구현하려면, 양 끝에서의 덧붙이기와 꺼내기가 모두 빠르도록 설계된 collections.deque
를 사용하세요. 예를 들어:
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry 도착
>>> queue.append("Graham") # Graham 도착
>>> queue.popleft() # 처음 도착한 사람이 이제 떠납니다
'Eric'
>>> queue.popleft() # 두번째 도착한 사람이 이제 떠납니다
'John'
>>> queue # 도착한 순서대로 남아있는 큐
deque(['Michael', 'Terry', 'Graham'])
5.1.3. 리스트 컴프리헨션¶
리스트 컴프리헨션은 리스트를 만드는 간결한 방법을 제공합니다. 흔한 용도는, 각 요소가 다른 시퀀스나 이터러블의 멤버들에 어떤 연산을 적용한 결과인 리스트를 만들거나, 어떤 조건을 만족하는 요소들로 구성된 서브 시퀀스를 만드는 것입니다.
예를 들어, 제곱수의 리스트를 만들고 싶다고 가정하자, 이런 식입니다:
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
이것은 x
라는 이름의 변수를 만들고 (또는 덮어쓰고) 루프가 종료된 후에도 남아있게 만든다는 것에 유의하세요. 어떤 부작용도 없이, 제곱수의 리스트를 이런 식으로 계산할 수 있습니다:
squares = list(map(lambda x: x**2, range(10)))
또는, 이렇게 할 수도 있습니다:
squares = [x**2 for x in range(10)]
이것이 더 간결하고 읽기 쉽습니다.
리스트 컴프리헨션은 표현식과 그 뒤를 따르는 for
절과 없거나 여러 개의 for
나 if
절들을 감싸는 대괄호로 구성됩니다. 그 결과는 새 리스트인데, for
와 if
절의 문맥에서 표현식의 값을 구해서 만들어집니다. 예를 들어, 이 리스트 컴프리헨션은 두 리스트의 요소들을 서로 같지 않은 것끼리 결합합니다:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
그리고, 이것은 다음과 동등합니다:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
두 코드 조각에서 for
와 if
문의 순서가 같음에 유의하세요.
표현식이 튜플이면 (즉 앞의 예에서 (x, y)
), 반드시 괄호로 둘러싸야 합니다.
>>> vec = [-4, -2, 0, 2, 4]
>>> # 값을 두배로 하여 새 리스트를 만듭니다
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # 음수를 제외하도록 리스트를 필터링합니다
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # 모든 요소에 함수를 적용합니다
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # 각 요소에 메서드를 호출합니다
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # (숫자, 제곱) 과 같은 2-튜플의 리스트를 만듭니다
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # 튜플은 괄호로 묶어야합니다, 그렇지 않으면 에러가 발생합니다
>>> [x, x**2 for x in range(6)]
File "<stdin>", line 1, in <module>
[x, x**2 for x in range(6)]
^
SyntaxError: invalid syntax
>>> # 두 개의 'for' 를 갖는 리스트 컴프리헨션으로 리스트를 평평하게 만듭니다
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
리스트 컴프리헨션은 복잡한 표현식과 중첩된 함수들을 포함할 수 있습니다:
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
5.1.4. 중첩된 리스트 컴프리헨션¶
리스트 컴프리헨션의 첫 표현식으로 임의의 표현식이 올 수 있는데, 다른 리스트 컴프리헨션도 가능합니다.
다음과 같은 길이가 4인 리스트 3개의 리스트로 구현된 3x4 행렬의 예를 봅시다:
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
다음 리스트 컴프리헨션은 행과 열을 전치 시킵니다:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
앞절에서 보았듯이, 중첩된 리스트 컴프리헨션은 뒤따르는 for
의 문맥에서 값이 구해집니다. 그래서 이 예는 다음과 동등합니다:
>>> transposed = []
>>> for i in range(4):
... transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
이것은 다시 다음과 같습니다:
>>> transposed = []
>>> for i in range(4):
... # 다음 3줄은 중첩된 리스트 컴프리헨션을 구현합니다
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
실제 세상에서는, 복잡한 흐름문보다 내장 함수들을 선호해야 합니다. 이 경우에는 zip()
함수가 제 역할을 할 수 있습니다:
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
이 줄에 나오는 애스터리스크에 대한 자세한 내용은 인자 목록 언 패킹 을 보세요.
5.2. del
문¶
리스트에서 값 대신에 인덱스를 사용해서 항목을 삭제하는 방법이 있습니다: del
문입니다. 이것은 값을 돌려주는 pop()
메서드와 다릅니다. del
문은 리스트에서 슬라이스를 삭제하거나 전체 리스트를 비우는 데도 사용될 수 있습니다 (앞에서 빈 리스트를 슬라이스에 대입해서 했던 일입니다). 예를 들어:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
del
는 변 자체를 삭제하는데에도 사용될 수 있습니다:
>>> del a
이후에 이름 a
를 참조하는 것은 에러입니다 (적어도 다른 값이 새로 대입되기 전까지). 뒤에서 del
의 다른 용도를 보게 됩니다.
5.3. 튜플과 시퀀스¶
리스트와 문자열이 인덱싱과 슬라이싱 연산과 같은 많은 성질을 공유함을 보았습니다. 이것들은 시퀀스 자료 형의 두 가지 예입니다 (시퀀스 형 --- list, tuple, range 를 보세요). 파이썬은 진화하는 언어이기 때문에, 다른 시퀀스 자료형이 추가될 수도 있습니다. 다른 표준 시퀀스 자료 형이 있습니다: 튜플 입니다.
튜플은 쉼표로 구분되는 여러 값으로 구성됩니다. 예를 들어:
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # 튜플은 중첩될 수 있습니다:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # 튜플은 불변입니다:
... t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # 하지만 가변 객체를 포함할 수 있습니다:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
여러분이 보듯이, 출력되는 튜플은 항상 괄호로 둘러싸입니다, 그래서 중첩된 튜플이 올바르게 해석됩니다; 종종 괄호가 필요하기는 하지만 (튜플이 더 큰 표현식의 일부일 때), 둘러싼 괄호와 함께 또는 없이 입력될 수 있습니다. 튜플의 개별 항목에 대입하는 것은 가능하지 않지만, 리스트 같은 가변 객체를 포함하는 튜플을 만들 수는 있습니다.
튜플이 리스트처럼 보인다 하더라도, 이것들은 다른 상황에서 다른 목적으로 사용됩니다. 튜플은 불변 이고, 보통 이질적인 요소들의 시퀀스를 포함합니다. 요소들은 언 패킹 (이 섹션의 뒤에 나온다) 이나 인덱싱 (또는 네임드 튜플
의 경우는 어트리뷰트로도) 으로 액세스합니다. 리스트는 가변 이고, 요소들은 보통 등질 적이고 리스트에 대한 이터레이션으로 액세스 됩니다.
특별한 문제는 비었거나 하나의 항목을 갖는 튜플을 만드는 것입니다: 이 경우를 수용하기 위해 문법은 추가적인 예외 사항을 갖고 있습니다. 빈 튜플은 빈 괄호 쌍으로 만들어집니다; 하나의 항목으로 구성된 튜플은 값 뒤에 쉼표를 붙여서 만듭니다 (값 하나를 괄호로 둘러싸기만 하는 것으로는 충분하지 않습니다). 추합니다, 하지만 효과적입니다. 예를 들어:
>>> empty = ()
>>> singleton = 'hello', # <-- 마지막 쉼표에 주의하세요
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
문장 t = 12345, 54321, 'hello!'
는 튜플 패킹 의 예입니다: 값 12345
, 54321
, 'hello!'
는 함께 튜플로 패킹 됩니다. 반대 연산 또한 가능합니다:
>>> x, y, z = t
이것은, 충분히 적절하게도, 시퀀스 언 패킹 이라고 불리고 오른쪽에 어떤 시퀀스가 와도 됩니다. 시퀀스 언 패킹은 등호의 좌변에 시퀀스에 있는 요소들과 같은 개수의 변수들이 올 것을 요구합니다. 다중 대입은 사실 튜플 패킹과 시퀀스 언 패킹의 조합일뿐이라는 것에 유의하세요.
5.4. 집합¶
파이썬은 집합 을 위한 자료 형도 포함합니다. 집합은 중복되는 요소가 없는 순서 없는 컬렉션입니다. 기본적인 용도는 멤버십 검사와 중복 엔트리 제거입니다. 집합 객체는 합집합, 교집합, 차집합, 대칭 차집합과 같은 수학적인 연산들도 지원합니다.
집합을 만들 때는 중괄호나 set()
함수를 사용할 수 있습니다. 주의사항: 빈 집합을 만들려면 set()
을 사용해야 합니다. {}
가 아닙니다; 후자는 빈 딕셔너리를 만드는데, 다음 섹션에서 다룹니다.
여기 간략한 실연이 있습니다:
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # 중복이 제거되었음을 보여줍니다
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # 빠른 멤버십 검사
True
>>> 'crabgrass' in basket
False
>>> # 두 단어의 고유한 글자들로 집합 연산 시연
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # a 의 고유한 글자들
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # a 에 있으나 b 에 없는 글자들
{'r', 'd', 'b'}
>>> a | b # a 나 b, 혹은 양쪽 모두에 있는 글자들
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # a 와 b 모두에 있는 글자들
{'a', 'c'}
>>> a ^ b # a 나 b 에 있지만 양쪽 모두에 있지는 않은 글자들
{'r', 'd', 'b', 'm', 'z', 'l'}
리스트 컴프리헨션 과 유사하게, 집합 컴프리헨션도 지원됩니다:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
5.5. 딕셔너리¶
파이썬에 내장된 또 하나의 쓸모있는 자료 형은 딕셔너리 입니다 (매핑 형 --- dict 를 보세요). 딕셔너리는 종종 다른 언어들에서 "연관 메모리(associative memories)" 나 "연관 배열(associative arrays)" 의 형태로 발견됩니다. 숫자들로 인덱싱되는 시퀀스와 달리, 딕셔너리는 키 로 인덱싱되는데, 모든 불변형을 사용할 수 있습니다; 문자열과 숫자들은 항상 키가 될 수 있습니다. 튜플이 문자열, 숫자, 튜플들만 포함하면, 키로 사용될 수 있습니다; 튜플이 직접적이나 간접적으로 가변 객체를 포함하면, 키로 사용될 수 없습니다. 리스트는 키로 사용할 수 없는데, 리스트는 인덱스 대입, 슬라이스 대입, append()
나 extend()
같은 메서드들로 값이 수정될 수 있기 때문입니다.
딕셔너리를 (한 딕셔너리 안에서) 키가 중복되지 않는다는 제약 조건을 가진 키: 값 쌍의 집합으로 생각하는 것이 최선입니다. 중괄호 쌍은 빈 딕셔너리를 만듭니다: {}
. 중괄호 안에 쉼표로 분리된 키:값 쌍들의 목록을 넣으면, 딕셔너리에 초기 키:값 쌍들을 제공합니다; 이것이 딕셔너리가 출력되는 방식이기도 합니다.
딕셔너리의 주 연산은 값을 키와 함께 저장하고 주어진 키로 값을 추출하는 것입니다. del
로 키:값 쌍을 삭제하는 것도 가능합니다. 이미 사용하고 있는 키로 저장하면, 그 키로 저장된 예전 값은 잊힙니다. 존재하지 않는 키로 값을 추출하는 것은 에러입니다.
딕셔러리에 list(d)
를 수행하면 딕셔너리에서 사용되고 있는 모든 키의 리스트를 삽입 순서대로 돌려줍니다 (정렬을 원하면 대신 sorted(d)
를 사용하면 됩니다). 하나의 키가 딕셔너리에 있는지 검사하려면, in
키워드들 사용하세요.
여기에 딕셔너리를 사용하는 조그마한 예가 있습니다:
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
dict()
생성자는 키-값 쌍들의 시퀀스로 부터 직접 딕셔너리를 구성합니다.
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}
이에 더해, 딕셔너리 컴프리헨션은 임의의 키와 값 표현식들로 부터 딕셔너리를 만드는데 사용될 수 있습니다:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
키가 간단한 문자열일 때, 때로 키워드 인자들을 사용해서 쌍을 지정하기가 쉽습니다:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
5.6. 루프 테크닉¶
딕셔너리로 루핑할 때, items()
메서드를 사용하면 키와 거기에 대응하는 값을 동시에 얻을 수 있습니다.
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
시퀀스를 루핑할 때, enumerate()
함수를 사용하면 위치 인덱스와 대응하는 값을 동시에 얻을 수 있습니다.
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print(i, v)
...
0 tic
1 tac
2 toe
둘이나 그 이상의 시퀀스를 동시에 루핑하려면, zip()
함수로 엔트리들의 쌍을 만들 수 있습니다.
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
시퀀스를 거꾸로 루핑하려면, 먼저 정방향으로 시퀀스를 지정한 다음에 reversed()
함수를 호출하세요.
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
정렬된 순서로 시퀀스를 루핑하려면, sorted()
함수를 사용해서 소스를 변경하지 않고도 정렬된 새 리스트를 받을 수 있습니다.
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for i in sorted(basket):
... print(i)
...
apple
apple
banana
orange
orange
pear
시퀀스에 대해 set()
을 사용하면 중복 요소를 제거합니다. 시퀀스에 대해 set()
과 sorted()
를 함께 사용하는 것은 시퀀스의 고유 한 요소를 정렬된 순서로 루핑하는 관용적 방법입니다.
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear
때로 루프를 돌고 있는 리스트를 변경하고픈 유혹을 느낍니다; 하지만, 종종, 대신 새 리스트를 만드는 것이 더 간단하고 더 안전합니다.
>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
... if not math.isnan(value):
... filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
5.7. 조건 더 보기¶
while
과 if
문에서 사용되는 조건에는 비교뿐만 아니라 모든 연산자를 사용할 수 있습니다.
비교 연산자 in
과 not in
은 값이 시퀀스에 있는지 (없는지) 검사합니다. 연산자 is
와 is not
은 두 객체가 진짜로 같은 객체인지 비교합니다. 모든 비교 연산자들은 같은 우선순위를 갖는데, 모든 산술 연산자들보다 낮습니다.
비교는 연쇄할 수 있습니다. 예를 들어, a < b == c
는, a
가 b
보다 작고, 동시에 b
가 c
와 같은지 검사합니다.
비교는 논리 연산자 and
와 or
를 사용해서 결합할 수 있고, 비교의 결과는 (또는 그 밖의 모든 논리 표현식은) not
으로 부정될 수 있습니다. 이것들은 비교 연산자보다 낮은 우선순위를 갖습니다. 이것 간에는 not
이 가장 높은 우선순위를 갖고, or
가 가장 낮습니다. 그래서 A and not B or C
는 (A and (not B)) or C
와 동등합니다. 여느 때처럼, 원하는 조합을 표현하기 위해 괄호를 사용할 수 있습니다.
논리 연산자 and
와 or
는 소위 단락-회로(short-circuit) 연산자입니다: 인자들은 왼쪽에서 오른쪽으로 값이 구해지고, 결과가 결정되자마자 값 구하기는 중단됩니다. 예를 들어, A
와 C
가 참이고 B
가 거짓이면, A and B and C
는 표현식 C
의 값을 구하지 않습니다. 논리값이 아닌 일반 값으로 사용될 때, 단락-회로 연산자의 반환 값은 마지막으로 값이 구해진 인자입니다.
비교의 결과나 다른 논리 표현식의 결과를 변수에 대입할 수 있습니다. 예를 들어,
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
파이썬에서, C와는 달리, 표현식 안에서의 대입은 바다코끼리 연산자 :=
를 사용하여 명시적으로 수행해야 합니다. C 프로그램에서 흔히 마주치는 부류의 문제들을 회피하도록 합니다: ==
를 사용할 표현식에 =
를 입력하는 실수.
5.8. 시퀀스와 다른 형들을 비교하기¶
시퀀스 객체들은 보통 같은 시퀀스 형의 다른 객체들과 비교될 수 있습니다. 비교는 사전식 순서를 사용합니다: 먼저 첫 두 항목을 비교해서 다르면 이것이 비교의 결과를 결정합니다; 같으면, 다음 두 항목을 비교하고, 이런 식으로 어느 한 시퀀스가 소진될 때까지 계속합니다. 만약 비교되는 두 항목 자체가 같은 형의 시퀀스면, 사전식 비교가 재귀적으로 수행됩니다. 두 시퀀스의 모든 항목이 같다고 비교되면, 시퀀스들은 같은 것으로 취급됩니다. 한 시퀀스가 다른 하나의 머리 부분 서브 시퀀스면, 짧은 시퀀스가 작은 것입니다. 문자열의 사전식 배열은 개별 문자들의 순서를 정하는데 유니코드 코드 포인트 숫자를 사용합니다. 같은 형의 시퀀스들 간의 비교의 몇 가지 예는 이렇습니다:
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
서로 다른 형의 객체들을 <
나 >
로 비교하는 것은, 그 객체들이 적절한 비교 메서드들을 갖고 있을 때만 허락된다는 것에 유의하세요. 예를 들어, 서로 다른 숫자 형들은 그들의 숫자 값에 따라 비교됩니다. 그래서 0은 0.0과 같고, 등등. 그렇지 않으면, 임의의 순서를 제공하는 대신, 인터프리터는 TypeError
를 일으킵니다.
각주
- 1
다른 언어들에서는 가변 객체를 돌려주기도 하는데,
d->insert("a")->remove("b")->sort();
와 같은 메서드 연쇄를 허락합니다.