Effective Python C1. Pythonic(8) - zip의 사용

2 분 소요

Introduction

파이썬에서 객체가 들어있는 리스트를 다수 다루는 경우가 많으며, 리스트 컴프리헨션을 사용하면 소스 list에서 새로운 list를 파생시키기 쉽다.

Best WAY 8 : 여러 이터레이터에 대해 나란히 루프를 수행하려면 zip을 사용하라.

두 리스트에서 원소를 동시에 가져오려는 경우, 인덱스를 쓸 필요없이 zip으로 동시에 가져올 수 있다. 아래 예제는 counts의 값이 가장 큰 name을 구하는 로직이다.

# 시각적 잡음이 않은 이터레이션. 
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
    
# enumerate를 썼으나, 아직 부족한 느낌.
for i, name in enumerate(names):
    count = counts[i]
    if count > max_count:
        longest_name = name
        max_count = count

# zip을 사용해 좀 더 깔끔하게 코드를 작성
for name, count in zip(names, counts):
    if count > max_count:
        longest_name = name
        max_count = count

zip은 자신이 감싼 이터레이터 원소를 하나씩 꺼낸다. 따라서 메모리를 다 소모해서 프로그램이 중단되는 위험도 없이 매우 긴 입력도 처리 가능하다 .

주의할 것은 zip이 감싼 이터레이터 원소의 길이가 각각 다를 때이다. 이 경우 출력이 가장 짧은 이터레이터 원소가 출력 길이의 기준이 된다.

name.append('LEE')
for name, count in zip(names, counts):
    print(name)

>>>
Cecilia
남궁민수

이를 방지하려면 길이가 긴 원소에 맞춰 출력 길이가 정해지는 zip_longest를 사용할 수 있다. 이때 길이가 짧은 원소의 경우 출력 길이를 넘어가면 None으로 채워진다. 단, 이 값은 fillvalue로 수정 가능하다.

import itertools

for name, count in itertools.zip_longest(names, counts):
    print(f"{name} : {count}")

>>>
Cecilia: 7
남궁민수: 4
LEE : None

Discussion

  • zip 함수로 나란히 여러 이터레이터를 이터레이션할 수 있다는 장점을 잘 살려서 코딩하자.

Summary

  • zip 내장 함수로 여러 이터레이터를 나란히 이터레이션할 수 있다.
  • zip은 튜플을 지연 계산하는 제너레이터를 만든다.

Reference

파이썬 코딩의 기술 제 2판. - 브렛 슬리킨 지음 / 오현석 옮김