
실무에서 다루는 리스트는 혼자 다니는 법이 거의 없습니다. 사용자 이름 리스트가 있으면 그 사용자의 점수 리스트가 따로 있고, 컴프리헨션(comprehension)으로 원본 리스트를 가공해 파생 리스트를 하나 더 만드는 일도 흔합니다. 이렇게 만들어진 두 리스트는 "같은 인덱스가 같은 대상을 가리킨다"는 암묵적인 약속으로 묶여 있습니다.
문제는 이 약속을 지키는 책임이 전부 개발자에게 있다는 점입니다. 두 리스트를 인덱스로 오가며 순회하는 코드는 names[i]와 counts[i]가 계속 등장해 시선을 분산시키고, 어느 한쪽에만 원소를 추가하는 순간 짝이 어긋나기 시작합니다. 더 곤란한 것은 이런 어긋남이 예외가 아니라 잘못된 결과로 나타난다는 사실입니다.
names = ['세실리아', '리제', '마리엔']
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) # 세실리아
enumerate로 바꿔도 counts[i] 한 줄은 그대로 남습니다. 루프의 본질은 "이름과 길이를 짝지어 보는 것"인데, 코드는 여전히 인덱스를 중심으로 굴러가고 있습니다. 리스트가 셋으로 늘어나면 이 참조 줄도 함께 늘어납니다.
names = ['세실리아', '리제', '마리엔']
counts = [len(n) for n in names]
longest_name = None
max_count = 0
for name, count in zip(names, counts): # 짝을 지어 튜플로 넘겨줍니다
if count > max_count:
longest_name = name
max_count = count
print(longest_name) # 세실리아
인덱스 변수가 사라지면서 루프 본문이 실제로 하려던 일만 남았습니다. zip은 지연 계산 제너레이터(generator)라 원본을 복사하지 않고 각 이터레이터(iterator)에서 값을 하나씩만 꺼내 쓰므로, 아주 긴 입력에도 메모리 부담 없이 쓸 수 있습니다.
한 가지 함정은 있습니다. zip은 넘겨받은 이터레이터 중 가장 짧은 쪽이 끝나면 그 자리에서 조용히 멈춥니다.
from itertools import zip_longest
names = ['세실리아', '리제', '마리엔']
counts = [len(n) for n in names]
names.append('로절린드') # counts는 갱신하지 않았습니다
for name, _ in zip(names, counts):
print(name) # 로절린드가 경고 없이 빠집니다
for name, count in zip_longest(names, counts):
print(f'{name}: {count}') # 로절린드: None
길이가 같다고 확신할 수 있을 때만 zip을 쓰고, 그렇지 않다면 빠진 자리를 None으로 채워 주는 zip_longest를 쓰는 것이 안전합니다.
zip은 여러 이터레이터를 묶어 원소들의 튜플을 만들어 주는 내장 함수입니다.itertools.zip_longest를 사용해야 합니다.zip으로 바꿀 수 있는지 살펴봅니다.머신러닝 코드에서 예측값과 정답 레이블을 비교할 때 가장 자주 만나게 됩니다. for pred, label in zip(predictions, labels) 형태로 두면 인덱스 실수로 한 칸씩 밀려 정확도가 이상하게 나오는 사고를 막을 수 있습니다. 다만 배치 크기가 맞는지는 루프 전에 assert len(predictions) == len(labels)로 못 박아 두는 편이 좋습니다.
CSV 헤더와 데이터 행을 딕셔너리로 합칠 때도 유용합니다. dict(zip(header, row)) 한 줄이면 컬럼 순서가 바뀌어도 대응되는 코드가 되고, 컬럼 개수가 다른 불량 행을 잡아내야 한다면 zip_longest로 바꿔 None이 섞인 행만 걸러 내는 검증 로직을 만들 수 있습니다.