루프, 컴프리헨션, 제너레이터
- 2.1 range 대신 enumerate
- 2.2 zip으로 병렬 이터레이션
- 2.3 for/while 뒤에 else 쓰지 말 것
- 2.4 루프 변수를 루프 밖에서 쓰지 말 것
- 2.5 방어적 이터레이션
- 2.6 이터레이션 중 컨테이너 변경 금지
- 2.7 any와 all로 쇼트서킷
- 2.8 itertools 활용
- 2.9 map/filter 대신 컴프리헨션
- 2.10 제어식은 3개 이하로
- 2.11 대입식으로 반복 방지
- 2.12 리스트 반환 대신 제너레이터
- 2.13 제너레이터 식으로 대용량 처리
- 2.14 yield from으로 제너레이터 합성
- 2.15 send 대신 인자 전달
- 2.16 throw 대신 클래스 기반 상태 전이
파이썬에서 데이터를 순회하고 변환하는 방법은 루프, 컴프리헨션, 제너레이터 세 축으로 나뉘는데, 각각의 경계를 아는 게 핵심이야.
range(len(flavors)) 같은 코드를 쓰는 순간 파이썬답지 않은 거거든. **enumerate**를 쓰면 (인덱스, 원소) 쌍이 나오니까 의도가 바로 보이고, 여러 리스트를 동시에 돌릴 때는 **zip**이 기본이야. 다만 zip은 가장 짧은 쪽이 끝나면 멈추니까, 길이 불일치를 잡으려면 zip(..., strict=True)를 쓰자. 이 두 도구만 제대로 써도 파이썬 루프의 절반은 해결돼.
반대로 절대 쓰면 안 되는 것도 있어. for ... else 문법이 대표적이지. else 블록이 루프가 break 없이 끝났을 때 실행되는 건데, 대부분의 개발자가 직관과 반대로 이해해. 읽는 사람을 혼란스럽게 만드니까 그냥 도우미 함수를 쓰는 게 낫지. 루프 변수를 루프 밖에서 쓰는 것도 위험해 — 파이썬에서 for 변수가 스코프에 남아있는 건 사실이지만, 루프가 한 번도 안 돌면 NameError가 나거든. 의도가 불분명한 코드는 언젠가 버그가 돼.
이터레이터는 한 번만 소비된다는 것도 꼭 기억해야 해. sum(numbers)로 이터레이터를 다 쓴 다음에 다시 순회하면 빈 결과가 나오는데, 에러도 안 나니까 찾기 어려운 버그야. 순회 중에 딕셔너리나 리스트를 수정하는 것도 금지 — 복사본을 만들어서 순회하거나 새 컨테이너에 결과를 쌓아야 해. any()와 all() 같은 쇼트서킷 함수, itertools의 chain, islice, groupby 같은 도구를 알면 직접 루프를 짜는 것보다 코드가 짧아지고 의도도 명확해지지.
루프를 넘어서면 컴프리헨션이 기다리고 있어. map/filter보다 읽기 쉽지. [x ** 2 for x in numbers if x % 2 == 0] — lambda 없이 한눈에 읽히잖아. 근데 for와 if를 여러 개 넣기 시작하면 순식간에 읽기 어려운 코드가 돼. 제어식이 3개를 넘으면 그냥 for 루프로 풀어 쓰는 게 낫다는 게 저자의 원칙이야. 컴프리헨션의 목적은 간결함이지 마법이 아니거든. **왈러스 연산자(:=)**로 컴프리헨션 안의 중복 계산을 제거할 수 있지만, 대입식 변수가 바깥 스코프로 누출된다는 점은 주의해야 해.
진짜 이 장에서 중요한 건 제너레이터야. 함수가 결과를 리스트에 모아서 반환하는 대신 yield를 쓰면 메모리가 고정돼 — 입력 크기와 무관하게. 제너레이터 식(소괄호 버전)은 체이닝도 가능해서 중간 리스트 없이 파이프라인을 만들 수 있고, 여러 제너레이터를 합성할 때는 yield from이 코드도 짧고 C 레벨 위임이라 성능도 좋아.
다만 제너레이터의 send()와 throw()는 쓰지 마. send는 제너레이터를 양방향으로 만들어서 코드 추론이 어려워지고, throw는 예외 처리까지 제너레이터에 떠넘겨서 복잡도가 급증하거든. 제너레이터는 단방향 데이터 생산자로만 쓰고, 복잡한 상태 관리가 필요하면 __iter__를 구현한 클래스를 쓰는 게 맞아.
정리
2장 읽고 기억할 거 세 가지:
- enumerate, zip, itertools가 파이썬 루프의 기본 도구다. 이터레이터는 한 번만 소비되고, 순회 중 컨테이너 변경은 금지다.
- 컴프리헨션은 제어식 3개까지만, 그 이상이면 for 루프가 낫다. 대입식으로 중복 계산은 제거하되 스코프 누출에 주의하자.
- 제너레이터는 단방향 데이터 생산자로만 쓰자. yield와 yield from으로 메모리 효율적인 파이프라인을 만들고, send/throw는 피하라.