본문 바로가기

Hacks

파이썬 내장 함수 zip 구현

개요

iterable한 객체들을 인덱스에 맞게 묶어주는 내장 함수인 zip.

이러한 파이썬의 유용한 내장 함수인 zip의 기능은 어떻게 구현된 것일까?

 

우선 zip의 기능을 눈으로 확인해보자.

 

zip의 기능

arr = [1,2,3,4,5]
names = ["Kim", "Lee", "Hong"]

zip_arr = [data for data in zip(arr, names)]
print(zip_arr)

# -> [(1, 'Kim'), (2, 'Lee'), (3, 'Hong')]

위와 같이 zip은 2개 이상의 itrerable한 객체를 인덱스에 맞게 묶어준다.

중요한 건, 객체끼리 길이가 맞지 않는다면 가장 짧은 길이를 가진 객체를 기준으로 묶이기 때문에 모든 데이터가 묶이지 않을 수 있으니 유의하자.

가장 긴 길이의 객체를 기준으로 묶고 싶다면, itertools에 zip_longest를 사용하면 된다!

 

zip의 구현

어떻게 zip이 구현된 건지 알아보자.

def my_zip(*iterables):
    obj = object()                              # 객체끼리 길이가 맞지 않을 때 헛바퀴를 돌릴 빈 객체 선언
    iterator = [iter(it) for it in iterables]   # 인자로 받은 객체를 탐색할 수 있도록 객체마다 iter를 붙여준다.
    # iterator = []								# 위의 comprehension은 이 세 줄과 같다.
    # for it in iterables:
    #     iterator.append(iter(it))
    
    while iterator:                             # 탐색할 객체가 있는 동안
        content = []                            # 인덱스에 맞게 묶은 데이터를 담을 배열을 선언한다.
        for it in iterator:                     # iterator에 담긴 객체들을 for문으로 탐색한다.
            element = next(it, obj)#중요!        # next돌리는데 중요한 건 데이터가 담긴 it뿐만 아니라 obj도 함께 탐색한다.
            if element == obj:                  # element에 담긴 데이터가 obj, 즉 비어있다면
                return                          # 탐색을 종료하고 함수를 종료한다.
            content.append(element)             # element에 데이터 값이 담겨있다면, 값을 반환하기 위해 배열에 데이터를 추가시킨다
        yield tuple(content)                    # generator 형태로 반환하기 위해서 한 차례의 인덱스 탐색이 종료될 때마다 yield로 반환한다.
print(list(my_zip(arr, names)))

내 생각에 zip 구현 코드에서 가장 중요한 부분은 "중요!"라고 표시해둔 저 줄이다.

처음에 이 코드를 봤을 때, 도대체 왜 가뜩이나 생소한 object 객체를 생성한 건지 이해가 안 됐다.

심지어 그냥 object 객체는 object 객체 그 자체로 비어있는 역할인데 왜 생성하나 싶었다.

 

그런데 답은 거기에 있었다. 비어있는 역할을 해준다는 게 zip함수에서는 매우 중요했다.

 

간단하게 저 줄의 기능을 말해보자면

it과 obj의 다음 데이터를 한데 묶어서 element에 값을 저장한다.

그렇지만 element에 저장되는 값은 it에서 나온 값과 같다.

obj는 it의 값이 다 떨어질 때까지 기다리는 것이다.

it의 모든 값이 content 배열에 담기면 그때 obj가 활약한다.

 

객체1이 길이가 3이고, 객체2가 길이가 2라고 해보자

객체1의 3번째 데이터를 content에 옮기는 데에는 성공한다.

하지만 객체2는 3번째 데이터가 없기 때문에 if element == obj, 즉 빈 데이터가 element에 있기 때문에

zip 값으로 yield되지 못하고 return되어 함수가 종료된다.

 

그래서 zip의 반환 값의 총 길이는 2가 되는 것이다.

 

yield와 object의 역할에 대해 배울 수 있는 아주 좋은 코드인 것 같다.