파이썬에서 관련 객체로 구성된 리스트를 많이 사용한다. 서로 다른 리스트를 병렬로 처리하기 위해서 파이썬에서는 zip이라는 내장함수를 제공한다. zip을 활용했을 때 리스트 컴프리헨션 및 enumerate를 사용했을 경우보다 더 간편하다는 점을 확인해보자.

이번 포스팅은 다음의 링크를 참조하였다.

리스트 컴프리헨션을 이용한 예제

  • 파생 리스트의 아이템과 소스 리스트의 아이템은 서로의 인덱스로 연관
  • 소스 리스트인 names의 길이만큼 순회하면 두 리스트를 병렬로 순회 가능
names = ["Cecilia", "Lise", "Marie"]
letters = [len(n) for n in names]

longest_name = None
max_letters = 0

for i in range(len(names)):
    count = letters[i]
    if count > max_letters:
        longest_name = names[i]
        max_letters = count

print(longest_name)

>>>
Cecilia

enumerate를 이용한 예제

  • 위의 예제는 루프의 인덱스 i로 배열에 접근하는 동작이 두 번 일어남
  • 따라서 전체 루프문이 별로 보기 안좋음
  • enumerate를 이용하면 약간 개선 가능
names = ["Cecilia", "Lise", "Marie"]
letters = [len(n) for n in names]

for i, name in enumerate(names):
    count = letters[i]
    if count > max_letters:
        longest_name = name
        max_letters = count

zip을 이용한 예제

  • zip은 지연 제너레이터로 이터레이터 두 개 이상을 감싸안음
  • zip은 각 이터레이터들로부터 다음 값을 담은 튜플을 반환함
  • zip을 이용한 예제
names = ["Cecilia", "Lise", "Marie"]
letters = [len(n) for n in names]

for name, count in zip(names, letters):
    if count > max_letters:
        longest_name = name
        max_letters = count

zip의 문제점

  • 파이썬2에서는 zip이 제너레이터가 아니라서 이터레이터를 완전히 순회하여 생성한 모든 튜플을 반환함
    • 따라서 메모리 문제가 발생할 수 있음
    • 메모리 문제로부터 자유롭기 위해서는 izip을 사용하여야 함
  • 입력 이터레이터들의 길이가 다르면 zip이 이상하게 동작함
names = ["Cecilia", "Lise", "Marie", "Rosalind"]    # names와 letters의 길이가 다른 상황
letters = [7, 4, 5]

for name, count in zip(names, letters):
    print(name)

>>>
Cecilia                                             # Rosalind의 결과는 출력되지 않음
Lise
Marie
  • 실행할 리스트의 길이가 같다고 확실한 수 있을 때만 zip을 사용
  • 그렇지 않은 경우는 itertoolszip_longest를 사용 (파이썬2는 izip_longest)
  • zip_longest 예제
from itertools import zip_longest

x = [1, 2, 3]
y = [4, 5, 6, 7]

zipped = zip_longest(x, y)
print(list(zipped))

zipped = zip_longest(x, y, fillvalue=0)     # fillvalue 인자에 값을 지정
print(list(zipped))

>>>
[(1, 4), (2, 5), (3, 6), (None, 7)]
[(1, 4), (2, 5), (3, 6), (0, 7)]

Go back to the List of Studies