파이썬은 itertools, functools 모듈과 내장 클래스를 통해 이터러블을 순환하는 다양한 방법을 제공합니다. 이 중 개인적으로 많이 사용하는 기능을 모아 정리했습니다. itertools, functools은 이 글에 있는 것보다 더 많은 기능을 제공합니다. 또한 이 글에서 소개할 enuermate
, map
, filter
, zip
, product
은 이터레이터 입니다. 그리고 tee
함수의 반환값 또한 이터레이터 입니다. 즉 재사용이 불가능 합니다. reduce
는 이터러블이 아니지만 이터러블을 순환하며 특정 계산을 해줍니다.
1. enumerate, map, filter
이터러블을 순환할 때 몇번째 순환값인지 알고 싶을 때 enumerate
를 사용합니다. enumerate
는 이터러블의 순환값과 몇번째 값인지를 튜플로 묶어 반환합니다. 이터러블이 리스트라면 이 값은 인덱스입니다. map
은 이터러블의 순환값을 변경해 줍니다. map
은 변경된 순환값을 반환하는 함수를 인자로 받습니다. filter
는 이터러블을 순환할지 아닐지 선별할 수 있습니다. true
or false
값을 반환하는 함수를 인자로 받습니다. 아래는 예시입니다.
1
2
3
4
5
6
7
8
9
10
11
|
print('enumerate')
for i, num in enumerate('abc'):
print(i, num)
print('map')
for num in map(lambda x: x * 10, range(3)):
print(num)
print('filter')
for num in filter(lambda x: x != 1, range(3)):
print(num)
|
1
2
3
4
5
6
7
8
9
10
11
|
enumerate
0, 'a'
1, 'b'
2, 'c'
map
0
10
20
filter
0
2
|
2. zip, product
zip
은 여러 이터러블을 동시에 순환합니다. 모든 이터러블로 부터 이터레이터를 얻습니다. 그후, zip
의 __next__
메소드가 호출시 등록된 모든 이터레이터에 대해 __next__
메소드를 호출합니다. 그렇게 얻은 값은 튜플로 묶어 반환합니다. zip
을 생성시 전달값의 앞쪽에 있던 이터러블의 __next__
메소드를 먼저 호출합니다.
1
2
3
4
|
iter1 = range(3)
iter2 = 'abc'
for a, b in zip(iter1, iter2):
print(a, b)
|
1
2
3
|
0, 'a'
1, 'b'
2, 'c'
|
itertools 의 product
는 zip
과 반대로 순차적으로 순환합니다. 전달값 뒤쪽의 이터레이터를 먼저 순환합니다. 다중 반복문과 같은 기능을 하지만 약간 다릅니다. 다중반복문은 안쪽 반복문의 이터러블이 이터레이터일 경우 안쪽 반복문이 한번 순환다음부터 StopIteration
을 일으킵니다. 즉 안쪽반복문을 여러번 반복할 수 없습니다. 하지만 product
는 이터러블을 미리순환하여 값을 기억해 놓습니다. 그리고 미리 얻은 값을 순환합니다. 따라서 product
의 전달값으로 이터레이터를 사용해도 됩니다.
1
2
3
4
|
iter1 = range(3)
iter2 = 'abc3'
for a, b in product(iter1, iter2):
print(a, b)
|
1
2
3
4
5
6
7
8
9
|
0, 'a'
0, 'b'
0, 'c'
1, 'a'
1, 'b'
1, 'c'
2, 'a'
2, 'b'
2, 'c'
|
3. tee
이터레이터는 재사용이 불가능 합니다. 두 번 사용하고 싶다면 같은 내용의 이터레이터기 두 개 필요합니다. 그러나 한 이터레이터에서 나온 값을 기억한 후, 다른 이터레이터가 값을 요구할 때 미리 기억한 값을 주면 안될까요. 이러한 방식으로 여러 개의 이터레이터가 하나의 이터레이터에서 값을 구해오며 계산량이 줄어듭니다. itertools 의 tee
함수가 이러한 기능을 제공합니다. tee
함수는 이터러블에서 이러레이터를 얻은후, 그 이터레이터에서 값을 얻어오는 여러 개의 이터레이터를 만듭니다. 사용법은 다음과 같습니다. 두번째 인자는 만들 이터레이터의 수 입니다.
1
|
iter1, ter2, ter3 ... = tee(iterable, n)
|
이렇게 만들어진 이터레이터는 미리 얻어온 값이 있으면 기억한 값을, 얻어온 값이 없으면 원본 이터레이터를 순환시킵니다. 아래는 사용 예시입니다.
1
2
3
4
|
it1, it2 = tee(range(5), 2)
current = next(it1)
for current, prev in zip(it1, it2):
print(current, prev)
|
1
2
3
4
|
0, 1
1, 2
2, 3
3, 4
|
4. reduce
functools 모듈의 reduce
함수는 이터러블을 만들어 주지 않습니다. 대신 이터러블을 순환하며 순환값을 모두 더해주거나 곱해줄 수 있습니다. reduce
는 아래와 같이 sum
이나 product
같은 함수의 구현하기 좋습니다. 비슷한 기능을 하는 이터레이터가 필요하다면 itertools 의 accumulate
가 있습니다.
1
2
3
4
|
num = 5
sum = reduce(lambda s, x: s + x, range(num+1), 0)
product = reduce(lambda s, x: s * x, range(1, num+1), 1)
print(sum, product)
|
A. Tee 함수 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
class MyTee_:
def __init__(self, it, memory, counts, tee_count):
self.index = -1
self.it = it
self.memory = memory
self.counts = counts
self.tee_count = tee_count
self.tees = None
def __next__(self):
memory = self.memory
counts = self.countsㄴ it = self.it
self.index += 1
value, count = None, None
if len(memory) <= self.index:
value = next(it)
memory.append(value)
counts.append(1)
else:
value = memory[self.index]
count = counts[self.index] + 1
counts[self.index] = count
if count == self.tee_count:
memory.pop(0)
counts.pop(0)
for tee in self.tees: tee.index -= 1
return value
def __iter__(self):
return self
def MyTee(iterable, n):
it = iter(iterable)
memory = []
counts = []
tees = [MyTee_(it, memory, counts, n) for i in range(n)]
for tee in tees: tee.tees = tees
return tuple(tees)
a, b, c = MyTee([1,2,3,4,5,6], 3)
print(next(a))
print(next(a))
print(next(b))
print([*zip(a,b,c)])
print(next(b))
print(next(c))
print(next(c))
|
1
2
3
4
5
6
7
|
1
2
1
[(3, 2, 1), (4, 3, 2), (5, 4, 3), (6, 5, 4)]
6
5
6
|
1
2
3
4
5
6
7
8
9
|
from itertools import tee
a, b, c = tee([1,2,3,4,5,6], 3)
print(next(a))
print(next(a))
print(next(b))
print([*zip(a,b,c)])
print(next(b))
print(next(c))
print(next(c))
|
1
2
3
4
5
6
7
|
1
2
1
[(3, 2, 1), (4, 3, 2), (5, 4, 3), (6, 5, 4)]
6
5
6
|