Batter way08

매일 공부(ML)·2022년 2월 3일
0

여러 이터레이터에 대해 나란히 루프를 수행하려면 zip사용

  • 리스트 컴프리헨션을 사용하면 소스 list에서 새로운 list를 파생시키기 쉽습니다.
names = ['Cecilia', '남궁민수', '이세현']
counts = [len(n) for n in names

  • 만들어진 list의 각 원소는 소스 list에서 같은 인덱스 위치에 있는 원소와 관련이 있기에 두 리스트를 동시에 이터레이션할 경우 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는 바람직하지 못한 기능이 하나 있습니다.

    • 입력 이터레이터의 길이가 서로 다를 때 긴 이터레이터의 뒷부분을 버립니다.
    • 원소가 추가되어도 실제로 출력이 되진 않습니다.
    • 이러한 문제를 해결하려면 itertools내장 모듈에 들어 있는 zip_longest를 대신 사용하기

*문제

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함수 사용

profile
성장을 도울 아카이빙 블로그

0개의 댓글