파이썬에서는 관련된 객체가 들어 있는 리스트를 다수 다루는 경우가 자주 있다. 리스트 컴프리헨션을 사용하면 소스 list에서 새로운 list를 파생시키기 쉽다.
names = ['Cecilia', '남궁민수', 'John']
counts = [len(n) for n in names]
# 만들어진 name중 가장 긴 이름을 리턴하라
longest_name = None
max_count = 0
for i in range(len(names)):
count = counts[i]
if count > max_count:
longes_name = name[i]
max_count = count
print(longest_name)
>>>
Cecilia
문제는 이루프가 시각적으로 잡음이 많다는 것이다. 인덱스를 사용해 names와 counts의 원소를 찾는 과정이 코드를 읽기 어렵게 만든다. 배열 인덱스 i를 사용해 배열 원소를 가져오는 연산이 두번 일어난다.
names = ['Cecilia', '남궁민수', 'John']
counts = [len(n) for n in names]
# 만들어진 name중 가장 긴 이름을 리턴하라
longest_name = None
max_count = 0
for name, count in zip(names, countes):
if count > max_count:
longest_name = name
max_count = count
이렇게 만든 코드는 인덱스를 사용해 여러 리스트의 원소에 접근하는 코드보다 훨씬 깔끔하다.
zip은 자신이 감싼 이터레이터 원소를 하나씩 소비한다. 따라서 메모리를 다 소모해서 프로그램이 중단되는 위험 없이 아주 긴 입력도 처리할 수 있다.
zip의 주의사항
zip은 인자로 받은 이터레이터중 가장 짧은 이터레이터 길이에 맞춰서 반환한다.
names.append('Rosalind')
for name, count in zip(names, countes):
print(name)
>>>
Cecilia
남궁민수
John
새로 추가한 원소인 Rosalind
에 대한 출력이 없다. zip은 자신이 감싼 이터레이터중 어느 하나가 끝날 때까지 튜플을 내놓기 때문이다.
긴 이터레이터를 버릴떄가 바람직하지 않을 경우 itertoos.zip_longest
를 사용한다.
import itertoos
for name, count in itertools.zip(names, countes):
print(f"{name}: {count}")
>>>
Cecilia: 7
남궁민수: 4
John: 4
Rosalind: None # 존재하지 않는 이터레이터 원소는 null처리한다.
zip_longest는 존재하지 않는값을 자신에게 전달된 fillvalue로 대신한다. fillvalu의 기본값은 None이다.
Reference. Effective Python