List Comprehension


`` result``를 `` a`` 리스트의 요소들 중 짝수인 요소들만 골라 `` *3`` 한 값들로 채우려면

```python

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

```


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

```python

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

        for 항목2 in 반복가능객체2 if 조건2]

```


Dictionary Comprehension, Set Comprehension

```python

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가 ``py yield``하게 되거나, 함수가 끝나면 자동으로 StopIteration이 발생한다.
generator는 내부에 ``py return`` 문이 없어야 한다.
```python
def firstn(n):
    num = 0
    while num < n:
        yield num
        num += 1
```

Generator Expression

generator expression은 comprehension의 generator 버전이다.
괄호만 []대신 ()를 사용하면 된다. 따라서 list comprehension을 대체할 수 있다.
list 대신 iterator를 사용하게 되므로 메모리 효율에서 이점이 있다.
```python
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()`` : 축적된 값을 계산한다. 기본적으로 합계를 계산하나, 두 번째 인자로 함수(또는 람다식)를 전달하여 다른 작업을 지시할 수 있다.
```python
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를 반환한다는 점이 다르다.
```python
for item in itertools.chain([1, 2], ('a', 'b')):
    print(item)
=======
1
2
a
b
```


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