names = ['Cecilia', '남궁민수', '이세현']
counts = [len(n) for n in names
longest_name = None
max_count = 0
for i in range(len(names)):
count = counts[i]
if count > max_count:
longest_name = names[i]
max_count = count
print(longest_name) # Ceilia
위와 같은 방식은 시각적 잡음이 많고 인덱스를 사용해서 names와 counts의 원소를 찾는 과정이기에 코드를 읽기 어렵게 합니다.
왜냐하면, 배열 인덱스 i를 사용해서 배열 원소를 가져오는 연산이 두 번 일어납니다.
그렇다고 Batter way07에서 enumerate를 사용하면 어떨까요?
나아지긴 했지만 이상적이진 않습니다.
for i name in enumerate(names):
count = counts[i]
if count > max_count:
longest_name = name
max_count = count
최고의 방법은 바로 zip입니다.
둘 이상의 이터레이터를 지연 계산 제너레이터 사용
각 이터레이터의 다음 값이 들어있는 튜플 반환
for문에서 바로 언패킹 가능
인덱스를 사용해서 리스트의 원소 접근보다 깔끔하다
for name, count in zip(names, counts):
if count > max_count:
longest_name = name
max_count = count
이터레이터 원소를 하나씩 소비하여 메모리를 다 소모해도 프로그램 중단없이 긴 입력을 처리할 수 있게 됩니다.
zip는 바람직하지 못한 기능이 하나 있습니다.
*문제
names.append('Rosalind')
for name, count in zip(names, counts):
print(name)
#결과
Ceilia
남궁민수
이세현
*해결
import itertools
for name, count in itertools.zip_longest(names, counts):
print(f'{name}: {count}')
#결과
Cecilia: 7
남궁민수:4
이세현:3
Rosalind: None #zip_longest가 존재하지 않는 값을 자신에게
#전달된 fillvalue로 대신한다. fillvalue는 None가 디폴트이다.
zip 내장 함수를 사용하여 여러 이터레이터를 나란히 이터레이션 가능
zip는 튜플을 지연 계산하는 제너레이터를 만들기에 아무리 긴 입력을 해도 사용 가능
입력 이터레이터의 길이가 서로 다르면 zip는 아무 경고 없이 가장 짧은 이터레이터 길이까지만 튜플을 내놓고 더 긴 이터레이터의 나머지 원소는 무시
가장 짧은 이터레이터에 맞춰 길이를 제한하지 않고 길이가 서로 다른 이터레이터에 대해 루프를 수행하여 itertools 내장 모듈의 zip_longest함수 사용