(python) Comprehension, Generator / iterator VS iterable / itertools
List Comprehension
- 확실히 일반 for문 보다 속도 우위가 있다.
- [python] 프로파일링(profiling)
- reduce 사용한 코드와 비교해서 더 깔끔한 것으로 선택하면 될 듯. map filter도 마찬가지
- 보통 reduce를 사용해야 하는 경우라면 list comprehension이 더 깔끔한 경우가 많기는 하다.
- 순서가 보장된다.https://stackoverflow.com/questions/1286167/is-the-order-of-results-coming-from-a-list-comprehension-guaranteed
result
를 a
리스트의 요소들 중 짝수인 요소들만 골라 \*3
한 값들로 채우려면
1
result = [num \* 3 for num in a if num % 2 == 0]
for
문은 위에서 부터 걸린다.
1
2
[표현식 for 항목1 in 반복가능객체1 if 조건1
for 항목2 in 반복가능객체2 if 조건2]
Dictionary Comprehension, Set Comprehension
1
2
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 에 의해 만들어진 iterator 를 generator 라고 부른다.
generator는 next(generator)
할 때 마다 yield
까지 수행된 결과를 반환 하게 된다. 다시 next()
를 호출하게 되면 yield 이후 부터 진행하여 다시 yield까지 수행한 결과를 반환 한다. 그래서 반복문 안에 yield var
를 넣어야한다. generator가 yield
하게 되거나, 함수가 끝나면 자동으로 StopIteration이 발생한다. generator는 내부에 return
문이 없어야 한다.
1
2
3
4
5
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
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
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
for item in itertools.chain([1, 2], ('a', 'b')):
print(item)
=======
1
2
a
b
이거랑 반대로 [1, 2], (‘a’, ‘b’)를 (1, ‘a’)와 (2, ‘b’)로 반환받고 싶다면 zip
을 써야한다.