Post

(python) Comprehension, Generator / iterator VS iterable / itertools

List Comprehension

resulta 리스트의 요소들 중 짝수인 요소들만 골라 \*3 한 값들로 채우려면

1
2
result = [num \* 3 for num in a if num % 2 == 0]

for문은 위에서 부터 걸린다.

1
2
3
[표현식 for 항목1 in 반복가능객체1 if 조건1
for 항목2 in 반복가능객체2 if 조건2]

Dictionary Comprehension, Set Comprehension
1
2
3
dict = {key\_expr : value\_expr for expr in iterable}
\_set = {entry[0] for entry in data}

list와 iterator의 차이

iterator는 next() 메소드로 데이터를 순차적으로 호출할 수 있는 객체를 말한다.

iterable은 그냥 for문 같은데 넣어서 반복할 수 있는 속성을 말한다.

그래서 list는 iterable하지만 iterator는 아니다. next()에 list를 인자로 넘기면 iterator가 아니라는 에러가 발생한다.

next()를 사용해 끝까지 순회하면 StopIteration이 발생한다.

iterator는 한번 사용하고 나면 소모되어 버린다.

list 보다는 iterator를 사용하는 편이 메모리 효율이 좋다.

Generator

iterator를 직접 구현하려면 \_\_iter\_\_\_\_next\_\_()를 구현해야 하고 raise StopIteration해야 하는 등 이래저래 귀찮다. 그래서 파이썬에서는generator function 을 사용해 쉽게iterator 를 구현할 수 있도록 지원한다.

generator function 에서는 yield를 이용해iterator를 리턴 하며, 이렇게generator function 에 의해 만들어진iteratorgenerator 라고 부른다.

generator는 next(generator) 할 때 마다 yield 까지 수행된 결과를 반환 하게 된다. 다시 next() 를 호출하게 되면 yield 이후 부터 진행하여 다시 yield까지 수행한 결과를 반환 한다. 그래서 반복문 안에 yield var를 넣어야한다. generator가 yield하게 되거나, 함수가 끝나면 자동으로 StopIteration이 발생한다. generator는 내부에 return 문이 없어야 한다.

1
2
3
4
5
6
def firstn(n):
num = 0
while num < n:
yield num
num += 1

Generator Expression

gen erator expression은 comprehension의 generator 버전이다. 괄호만 []대신 ()를 사용하면 된다. 따라서 list comprehension을 대체할 수 있다. list 대신 iterator를 사용하게 되므로 메모리 효율에서 이점이 있다.

1
2
3
l = [slow\_func(x) for x in range(1, 1000)]
g = (slow\_func(x) for x in range(1, 1000))

list comprehension의 경우 slow_func가 1000번 실행되어 리스트를 전부 채울 때 까지 저 문장에서 블록된다. 그러나 generator expression은 g의 next()가 호출될 때 마다(실제로 next()를 호출하든, for문의 iterator로 들어가서 자동으로 호출되든) slow_func()를 한번 씩 호출하게 된다. 즉 실제로 generator 값이 필요한 순간에 연산을 진행하게 된다.

itertools

accumulate() : 축적된 값을 계산한다. 기본적으로 합계를 계산하나, 두 번째 인자로 함수(또는 람다식)를 전달하여 다른 작업을 지시할 수 있다.

1
2
3
4
5
6
7
8
9
import itertools
for item in itertools.accumulate([1, 2, 3, 4], lambda a, b : a \* b):
print(item)
=======
1
2
6
24

chain() : iterable 여러개를 지정하면 앞에서부터 순차적으로 하나씩 꺼내준다. +연산자로 합치는건 타입이 같을 때만 되지만, 이건 달라도 상관 없다는 점, 그리고 iterator를 반환한다는 점이 다르다.

1
2
3
4
5
6
7
8
for item in itertools.chain([1, 2], ('a', 'b')):
print(item)
=======
1
2
a
b

이거랑 반대로 [1, 2], (‘a’, ‘b’)를 (1, ‘a’)와 (2, ‘b’)로 반환받고 싶다면 zip을 써야한다.

This post is licensed under CC BY 4.0 by the author.