generator와 recursion을 쓸 때는 yield from.

1 분 소요

1-line summary

  • generator 내에서 다시 generator를 가져오는 경우, 해당 generator로부터 모든 원소를 가져오고 싶을 때는 yield from을 사용하자.

python basic - generator

  • generator는 그냥 “데이터를 읽어들이는 흐름”이라고 생각하시면 됩니다. 데이터의 크기가 적을 때는 큰 문제가 안되지만, 데이터의 용량이 커지면서, 모든 데이터를 한 번에 메모리에 처박아두고 쓰기는 힘들어지죠. 이럴 때는, 그냥 “데이터를 읽어들이는 흐름”만을 기억해두고, 필요할 때 각 원소만을 읽어서 처리하고 메모리에서 없앱니다. 즉, 매우 가볍게 사용할 수 있죠.
  • 간단한 예는 다음과 같죠. 그냥 함수 내에 loop를 만들고 그 안에 yield를 사용해서 i라는 값을 리턴해줍니다. 긜고 다음에는 다시 해당 함수로 돌아가서 그 다음의 값을 리턴해주죠.
def not_generator(n):
    return [i for i in range(0, n)]

def generator(n):
    for i in range(0, n): 
        yield i 

# not generator(n): 
for x in not_generator(n):
    print(x)
# generator(n): 
for x in generator(n):
    print(x)
  • 또한 간단하게는 (i for i in range(0, 100))의 형태로 사용할 수도 있습니다.

recursion with generator

  • 사실, 간단하게 쓸 때는 문제가 없지만, 가끔 recursion과 generator를 합칠 때 약간 다른 점이 생깁니다.
  • 즉, “generator 내에서 다시 generator를 불러서 사용하는 경우”에는 yield가 아닌 yield from을 사용해야 하죠.
  • 저는 간단하게 “list 혹은 integer로 구성된 복잡한 list에 대해서 integer이면 integer를 리턴하고, list이면 내부의 원소를 하나씩 읽어서 리턴해주는 generator”를 만들어보려고 합니다.
  • 그래서 다음처럼 generator를 만들어줍니다.
def generator1(lst_of_lst): 
    # generator 
    for x in lst_of_lst: 
        if type(x)==list: 
            yield generator1(x)
        else: 
            yield x    
  • 하지만 아래에 대해서 실행해보면 원하는대로 실행되는 것이 아니고 중간 중간 generator가 return되는 것을 알 수 있죠.
a = [1, 
    [1, 2, [3]], 
    [[[[5]]]], 
    6, [7, [8]]
]
        
for x in generator1(a):
    print(x)
1
<generator object generator1 at 0x10ad33258>
<generator object generator1 at 0x10ad332b0>
6
<generator object generator1 at 0x10ad332b0>
  • 그냥 yield만으로는 generator로 리터되는 원소가 container일 때 안에 존재하는 값들을 모두 읽어서 처리해주지 않습니다.
  • 따라서, 이를 해결하려면 yield from을 사용하면 내부의 값까지 모두 읽어서 처리하게 되죠.

def generator1(lst_of_lst): 
    # generator 
    for x in lst_of_lst: 
        if type(x)==list: 
            yield from generator1(x)
        else: 
            yield x    

a = [1, 
    [1, 2, [3]], 
    [[[[5]]]], 
    6, [7, [8]]
]
        
for x in generator1(a):
    print(x)

wrap-up

  • 사실, 예전에 알았던 건데, recursion으로 뭘 풀다가, 잘 안되길래 다시 처리해보고 알았습니다 호호호.

reference

댓글남기기