이 장에서는 이미 배운 것들에 관하여 좀 더 자세하게 기술합니다. 그리고 새로운 것들도 몇가지 추가로 설명합니다.
리스트 데이터 유형은 몇 가지 메쏘드가 더 있습니다. 다음은 리스트 객체가 가진 메쏘드를 모두 나타낸 목록입니다:
| x) |
a[len(a):] = [x].
| L) |
a[len(a):] = L.
| i, x) |
a.insert(0, x)는 리스트의 맨 앞에 삽입되고, a.insert(len(a), x)는 a.append(x)과 동등하다.
| x) |
| [i]) |
a.pop()은 리스트에서 가장 마지막 원소를 제거하여 돌려준다. (메쏘드 서명에서 i를 둘러싼 각 괄호는 매개변수가 선택적이라는 뜻이지, 그 위치에 각 괄호를 타자해야 한다는 뜻이 아니다. 이 표기법은 파이썬 라이브러리 참조서에 자주 보인다.)
| x) |
| x) |
| ) |
| ) |
리스트 메쏘드는 다음과 같이 자주 사용됩니다:
>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.25), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
다음의 리스트 메소드로 아주 쉽게 리스트를 스택처럼 사용할 수 있습니다. 스택은 가장 나중에 추가된 원소가 제일 먼저 열람됩니다 (``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]
리스트를 큐처럼 편리하게 사용할 수도 있습니다. 규는 제일 먼저 추가된 원소가 가장 먼저 열람됩니다 (``first-in,
first-out''). 큐의 뒤에 원소를 추가하려면, append()를 사용합니다. 큐의 앞에서 원소를 열람하려면, 지표를 0 으로 하여 pop()을 사용합니다. 예를 들어:
>>> queue = ["Eric", "John", "Michael"]
>>> queue.append("Terry") # Terry 도착
>>> queue.append("Graham") # Graham 도착
>>> queue.pop(0)
'Eric'
>>> queue.pop(0)
'John'
>>> queue
['Michael', 'Terry', 'Graham']
리스트와 함께 사용하면 아주 유용한 내장 함수가 세가지 있습니다: filter(), map(), 그리고 reduce()가 바로 그것입니다.
"filter(함수, sequence)"는 sequence에서 함수(item)이 참인 원소들로 구성된 연속열을 돌려줍니다. 연속열(sequence)이 문자열(string) 또는 터플(tuple)이라면, 그 결과도 유형이 같습니다; 그렇지 않으면, 언제나 그 유형은 리스트(list)입니다. 예를 들어, 소수를 계산하려면:
>>> def f(x): return x % 2 != 0 and x % 3 != 0 ... >>> filter(f, range(2, 25)) [5, 7, 11, 13, 17, 19, 23]
"map(함수, sequence)"은 연속열의 각 원소들에 대하여함수(item)을 호출하고 그 결과 값을 리스트로 돌려줍니다. 예를 들어, 입방체를 계산하려면:
>>> def cube(x): return x*x*x ... >>> map(cube, range(1, 11)) [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
연속열을 여럿 건네도 됩니다; 그러면 이 함수는 연속열의 개수만큼 인자를 가집니다. 그리고 각 연속열로부터 상응하는 원소를 가지고 호출됩니다 (그렇지 않고 어떤 연속열이 또다른 연속열보다 작으면 None입니다). 예를 들어:
>>> seq = range(8) >>> def add(x, y): return x+y ... >>> map(add, seq, seq) [0, 2, 4, 6, 8, 10, 12, 14]
"reduce(함수, sequence)"는 연속열에서 첫 두 원소에 대하여 이진함수 함수을 호출하고, 다음 그 결과와 다음 원소에 대하여, 이런식으로 마지막까지 호출하여 구성한 값 하나를 돌려줍니다. 예를 들어, 1에서부터 10까지 숫자의 합을 계산하려면:
>>> def add(x,y): return x+y ... >>> reduce(add, range(1, 11)) 55
연속열에 원소가 하나 뿐이라면, 그 값이 반환됩니다; 연속열이 비여 있다면, 예외가 일어납니다.
시작 값을 지시하기 위하여 세 번째 인자를 건넬 수 있습니다. 이 경우 시작 값은 빈 연속열에 대하여 반환되며, 그 시작 값과 첫 원소에 처음으로 이 함수가 적용되고, 다음 그 결과와 다음 원소가 적용되고, 등등 이런식으로 끝까지 적용됩니다. 예를 들어,
>>> def sum(seq): ... def add(x,y): return x+y ... return reduce(add, seq, 0) ... >>> sum(range(1, 11)) 55 >>> sum([]) 0
이 예제의 sum() 정의를 사용하지 마세요: 왜냐하면 숫자를 합계를 내는 일은 흔한 요구라서, 내장 함수 sum(sequence)으로 이미 제공되며, 정확하게 이와 똑 같이 작동합니다.
파이썬 2.3에 새로 도입된 특징.
리스트 통합은 map(), filter() 그리고/또는 lambda의 사용에 의지하지 않고 리스트를 만드는 간결한 방법입니다. 리스트 통합으로 정의하는 것이 그런 구성자를 사용하여 구축된 리스트보다 보통 더 명료합니다. 각 리스트 통합은 뒤에 for 절이 따르고 다음 0개 이상의 for나 if절이 따르는 표현식으로 구성됩니다. 그 결과는 그 다음에 따르는 for와 if의 문맥 안에서 표현식을 평가한 결과가 담긴 리스트입니다. 표현식이 터플로 평가된다면, 반드시 반괄호로 둘러 싸야 합니다.
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]
>>> [3*x for x in vec if x > 3]
[12, 18]
>>> [3*x for x in vec if x < 2]
[]
>>> [[x,x**2] for x in vec]
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec] # 에러- 터플에는 반괄호가 요구됨
File "<stdin>", line 1, in ?
[x, x**2 for x in vec]
^
SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec]
[(2, 4), (4, 16), (6, 36)]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
>>> [vec1[i]*vec2[i] for i in range(len(vec1))]
[8, 12, -54]
리스트 통합은 map() 보다 훨씬 더 유연합니다. 그리고 복잡한 표현식과 내포된 함수에 적용할 수 있습니다:
>>> [str(round(355/113.0, i)) for i in range(1,6)] ['3.1', '3.14', '3.142', '3.1416', '3.14159']
리스트로부터 그의 값 대신에 그의 지표가 주어진 원소를 제거하는 방법이 있습니다: 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에 관하여 다른 사용법을 알아 보겠습니다.
앞에서 리스트와 문자열이 예를 들어, 지표화와 조각썰기 연산 같이 공통점이 많다는 것을 보았습니다. 이 두 유형은 sequence 데이터 유형의 한 예입니다. 파이썬은 진화중인 언이이기 때문에, 혹 다른 연속열 유형이 추가될 수 있습니다. 또다른 표준 연속열 데이터 유형이 있습니다: 터플(tuple)이 바로 그것입니다.
터플은 쉼표로 분리된 값들로 구성됩니다. 예를 들면:
>>> t = 12345, 54321, 'hello!' >>> t[0] 12345 >>> t (12345, 54321, 'hello!') >>> # Tuples may be nested: ... u = t, (1, 2, 3, 4, 5) >>> u ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
보시다시피, 터플은 언제나 괄호로 둘러 싸여 출력됩니다. 그래서 내포된 터플도 정확하게 이해됩니다; (그 터플이 더 큰 표현식의 일부라면) 어쨌든 반괄호가 필요하지만 반괄호를 싸지 않고도 입력할 수 있습니다.
터플은 사용하는 곳이 많습니다. 예를 들어: (x, y) 좌표쌍, 데이터베이스의 사원 기록 등등. 문자열처럼 터플도 변경불가능합니다: 터플의 개별 원소에 할당할 수 없습니다 (그렇지만, 조각썰기와 결합과 똑 같은 효과를 대부분 흉내낼 수 있습니다). 리스트같이 변경가능 객체가 담긴 터플을 만드는 것도 가능합니다.
특별한 문제는 터플의 구성에 원소가 0개 또는 1개 담겨 있을 경우입니다: 구문에 따로 군더더기를 붙여서 이에 적응하여야 합니다. 빈 터플은 빈 반괄호 쌍으로 구성됩니다; 원소가 한 개인 터플은 값 다음에 쉽표를 붙여서 구성합니다 (한 개의 값을 반괄호로 둘러 싸는 정도로는 충분하지 못합니다). 보기 좋지는 않지만, 효과적입니다. 예를 들어:
>>> empty = ()
>>> singleton = 'hello', # <-- 뒤따르는 쉼표에 주목
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
다음 t = 12345, 54321, 'hello!' 서술문은 터플 꾸리기(tuple packing)의 예입니다: 값 12345와 54321 그리고 'hello!'는 한 터플 안에 함께 꾸려 넣어져 있습니다. 반대 연산도 가능합니다:
>>> x, y, z = t
그에 적절하게 이를 이른바 연속열 풀기(sequence unpacking)라고 부릅니다. 연속열 풀기는 왼쪽에 있는 리스트의 값들이 연속열의 길이와 같은 개수만큼 있어야 합니다. 다중 할당은 실제로는 그냥 터플 꾸리기와 연속열 풀기의 조합일 뿐입니다!
여기에 약간의 비대칭성이 있습니다: 여러 값들을 꾸리면 언제나 터플이 생성됩니다. 그리고 풀기는 어떤 연속열에도 작동합니다.
파이썬은 또한 집합(집합)에 대한 데이터 유형도 포함되어 있습니다. 집합은 중복 원소가 없는 순서없는 집단입니다. 기본적인 사용법으로는 구성원 존재 테스트와 중복 원소 제거하기가 있습니다. 집합 객체는 합과 곱 그리고 차와 대칭 차 같은 수학 연산도 지원합니다.
다음은 간단한 예입니다:
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> fruit = set(basket) # 중복없는 집합 만들기
>>> fruit
set(['orange', 'pear', 'apple', 'banana'])
>>> 'orange' in fruit # 신속한 구성원 존재 유무 테스트
True
>>> 'crabgrass' in fruit
False
>>> # 두 단어로부터 유일한 글자들에 대한 집합 연산 보여주기
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # a의 유일한 글자들
set(['a', 'r', 'b', 'c', 'd'])
>>> a - b # a에는 있지만 b에는 없는 글자들
set(['r', 'd', 'b'])
>>> a | b # a 또는 b에 있는 글자들
set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
>>> a & b # a와 b에 있는 글자들
set(['a', 'c'])
>>> a ^ b # a와 b에 있지만 둘 모두에 있는 글자들 제외
set(['r', 'd', 'b', 'm', 'z', 'l'])
파이썬에 내장 된 또다른 유용한 데이터 유형은 사전(dictionary)입니다. 사전은 종종 다른 언에서는 ``연관 메모리'' 또는 ``연관 배열''이라고 합니다. 숫자의 범위로 지표가 붙는 연속열과는 다르게, 사전은 keys로 지표가 붙습니다. 키는 변경 불능형 유형이면 무엇이든 됩니다; 문자열과 숫자는 언제나 키가 될 수 있습니다. 터플은 안에 문자열이나 숫자 또는 터플만 있다면 키로 사용이 가능합니다; 터플 안에 간접적이든 직접적이든 변경 가능 객체가 들어 있다면, 키로 사용할 수 없습니다. 리스트를 키로 사용할 수 없습니다. 리스트는 제자리에서 변경이 될 수 있기 때문입니다. 지표 할당이나 조각썰기 할당 또는 append()와 extend() 메쏘드를 사용하여 변경할 수 있기 때문입니다.
사전을 순서없는 (한 사전 안에서) 키가 유일해야 하는 key: value 쌍의 집합으로 생각하는게 제일 좋습니다. 한 쌍의 활괄호는 빈 사전을 만듭니다: {}. 활괄호 안에 키:값 쌍을 쉼표로 갈라 넣으면 그 사전에 키:값 쌍이 처음 추가됩니다; 이것은 사전이 출력되는 방식이기도 합니다.
사전의 주 연산은 키로 값을 저장하고 주어진 키로 값을 추출하는 것입니다. del로 키:값 쌍을 삭제할 수도 있습니다. 이미 사용중인 키를 사용하여 저장하면, 그 키와 연관된 예전 값은 없어집니다. 존재하지 않는 키로 값을 추출하면 에러입니다.
사전 객체의 keys() 메쏘드는 사전에 사용된 키들을 모두 임의의 순서로 담은 리스트를 돌려줍니다 (정렬하고 싶으면, 그 키 리스트에 sort() 메쏘드를 적용하면 됩니다). 키가 사전에 있는지 알아 보려면 사전의 has_key() 메쏘드나 in 키워드를 사용하면 됩니다.
다음은 사전을 사용한 예입니다:
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> tel.has_key('guido')
True
>>> 'guido' in tel
True
dict() 구성자는 키-값 쌍이 터플로 저장된 리스트로부터 직접 사전을 구축합니다. 쌍이 일정한 패턴이 있다면, 리스트 통합으로 간결하게 키-값 리스트를 지정할 수 있습니다.
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> dict([(x, x**2) for x in (2, 4, 6)]) # 리스트 통합을 사용한다
{2: 4, 4: 16, 6: 36}
나중에 이 자습서에서 발생자 표현식에 관하여 배우게 됩니다. 키-값 쌍을 dict() 구성자에 제공하려면 발생자가 훨씬 더 알맞습니다.
키가 단순히 문자열이라면, 키워드 인자를 사용하여 짝을 지정하는 것이 더 편리한 경우가 많습니다:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
사전을 순회할 때, iteritems() 메쏘드를 사용하면 키와 그에 상응하는 값을 동시에 열람할 수 있습니다.
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.iteritems():
... 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 %s? It is %s.' % (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(xrange(1,10,2)): ... print i ... 9 7 5 3 1
정렬된 순서로 연속열을 순회하려면, sorted() 함수를 사용하면 됩니다. 이 함수는 소스는 그대로 둔 채로 새로 정렬된 리스트를 돌려줍니다.
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] >>> for f in sorted(set(basket)): ... print f ... apple banana orange pear
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 프로그래머라면 이 때문에 불평하겠지만, C 프로그램에서 자주 마주하는 일반적인 문제를 피할 수 있습니다: 표현식 안에서 ==를 넣어야 할 곳에 =를 타자하는 일이 종종 있습니다.
연속열 객체는 같은 연속열 유형을 가진 다른 객체와 비교할 수 있습니다. 사전 편찬(lexicographical) 순서로 비교됩니다: 먼저 앞 두 원소가 비교되고, 다르면 비교의 결과가 결정됩니다; 같으면, 다음 두 원소가 비교되고 등등 두 연속열 하나가 소진할 때까지 계속됩니다. 비교될 두 원소가 같은 유형을 가진 연속열이라면, 사전편찬 순서로 비교가 재귀적으로 수행됩니다. 두 연속열의 원소가 모두 같으면, 그 연속열은 동일하다고 간주됩니다. 한 연속열이 다른 연속열의 최초 부분-연속열이라면, 짧은 연속열이 더 작은 연속열입니다. 문자열이라면 각 문자에 대하여 사전편찬 순서로 ASCII 순서를 사용합니다. 유형이 같은 연속열 사이를 비교하는 몇가지 예는 다음과 같습니다:
(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)
유형이 다른 객체를 비교하는 것도 합법적임에 주목하세요. 결과는 일관성 있게 결정되지만 비교 논리는 임의적입니다: 유형은 그 이름으로 순서가 결정됩니다. 그리하여, 리스트는 언제나 문자열보다 작고, 문자열은 언제나 터플보다 작습니다. 등등. 5.1 혼합 수치 유형은 숫치로 비교됩니다. 그래서 0은 0.0과 같습니다. 등등.