← notes

Python - Iterators, Generators, Classic Coroutines

2024-09-28 · python

[!TIP] Source 전문가를 위한 파이썬(Fluent Python) 17장

Iterable, Iterator

반복형(Iterable)과 반복자(Iterator)

import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return f'Sentence({reprlib.repr(self.text)})'

    def __iter__(self):
        return SentenceIterator(self.words)  


class SentenceIterator:
    def __init__(self, words):
        self.words = words  
        self.index = 0  
    def __next__(self):
        try:
            word = self.words[self.index]  
        except IndexError:
            raise StopIteration() 
        self.index += 1  
        return word  
    def __iter__(self): 
        return self
s = Sentence("this is not good")
for w in s:
    print(w)  # OK
for w in s:
    print(w)  # 아무 것도 안 나옴 (index가 이미 끝에 있음)

it1 = iter(s)
it2 = iter(s)  # 같은 객체 리턴됨 → 상태 공유됨
next(it1)  # it2도 영향을 받음

Generator

Sentence에서 Iterator를 별도로 만들지 않고 __iter__를 다음과 같이 쓴다면?

def __iter__(self):
    for word in self.words:
        yield word
def __iter__(self):
    for match in RE_WORD.finditer(self.text):
        yield match.group()
def __iter__(self):
    return (match.group() for match in RE_WORD.finditer(self.text))

표준 라이브러리 제너레이터

Filtering

함수설명예시
itertools.compress(data, selectors)selectors가 True인 곳의 data만 반환compress('ABCDE', [1,0,1,0,1]) → A C E
itertools.dropwhile(pred, iterable)조건이 False가 되는 시점부터 모든 값 반환dropwhile(lambda x: x<3, [1,2,3,4]) → 3 4
itertools.filterfalse(pred, iterable)조건이 False인 값만 반환filterfalse(lambda x: x%2, range(5)) → 0 2 4
filter(pred, iterable)조건이 True인 값만 반환filter(lambda x: x>3, [1,4,5]) → 4 5
itertools.takewhile(pred, iterable)조건이 False가 되기 전까지 값 반환takewhile(lambda x: x<3, [1,2,3,4]) → 1 2

Mapping

함수설명예시
itertools.accumulate(iterable, func=operator.add)누적 계산 (합/곱/최대 등)accumulate([1,2,3]) → 1 3 6
enumerate(iterable, start=0)(인덱스, 값) 튜플 반환enumerate('abc') → (0, 'a'), (1, 'b')
map(func, iterable)각 요소에 함수 적용map(str.upper, ['a', 'b']) → 'A' 'B'
itertools.starmap(func, iterable_of_tuples)튜플을 언팩해서 함수에 적용starmap(pow, [(2,3),(3,2)]) → 8 9

Merging

함수설명예시
itertools.chain(*iterables)여러 iterable을 하나처럼 이어 붙임chain('AB', 'CD') → A B C D
itertools.product(*iterables, repeat=1)데카르트 곱product('AB', repeat=2) → AA AB BA BB
zip(a, b)같은 인덱스끼리 튜플 묶음 (짧은 쪽 기준)zip('AB', '12') → ('A','1'), ('B','2')
itertools.zip_longest(a, b, fillvalue=None)zip과 같지만 긴 쪽 기준, 없는 곳은 fillzip_longest('AB', '123', fillvalue='X') → ('A','1'), ('B','2'), (None,'3')

Expanding

함수설명예시
itertools.combinations(iterable, r)r개 조합 (순서 무관)combinations('ABC', 2) → AB AC BC
itertools.count(start=0, step=1)무한 증가 수열count(10, 2) → 10 12 14 ...
itertools.cycle(iterable)반복적으로 순환cycle('AB') → A B A B A ...
itertools.pairwise(iterable)(현재, 다음) 쌍 튜플 생성pairwise('ABCD') → (A,B), (B,C), (C,D)
itertools.permutations(iterable, r=None)r개 순열 (순서 중요)permutations('ABC', 2) → AB AC BA BC CA CB
itertools.repeat(elem, times=None)같은 값을 계속 반복repeat(10, 3) → 10 10 10

Rearranging

함수설명예시
itertools.groupby(iterable, key=...)인접한 값 기준 그룹핑groupby('AAABBB') → ('A', ['A','A','A']), ('B', ['B','B','B'])
reversed(seq)역순 반복자 (list, str 등 시퀀스만)reversed([1,2,3]) → 3 2 1
itertools.tee(iterable, n=2)반복 가능한 객체를 n개 복제a, b = tee(range(3)) → a, b 독립 사용 가능

Reduce

함수설명예시
all(iterable)모두 True여야 Trueall([1, 2, 3]) → True
any(iterable)하나라도 True면 Trueany([0, 0, 3]) → True
max(iterable) / min(...)최댓값 / 최솟값max([1, 5, 2]) → 5
functools.reduce(func, iterable[, initializer])누적 계산 후 최종값 하나 반환reduce(lambda x,y: x+y, [1,2,3]) → 6
sum(iterable)합계sum([1, 2, 3]) → 6

Classic Coroutines

def package_receiver():
    while True:
        package = yield 
        print(f"택배를 받았다: {package}")

receiver = package_receiver()
next(receiver) # 코루틴 가동 (yield까지 실행)
receiver.send("의류")  
receiver.send("전자기기")