티스토리 뷰

 

 본 포스팅은 코딩을 처음 배우시는 입문자 분들께는 적절하지 않은 포스팅일 수 있습니다.

 

 개발에 필요한 최소한의 내용만 정리해서 포스팅합니다.

 

 

 


Summary 📜


  • Generator와 yield에 대한 이해 

 

 

 

yield를 알기에 앞서... 🤔


 파이썬에는 yield라는 키워드가 있다. yield를 찾다 보면, generator라는 개념도 나오고, 이해가 잘 안 될 수 있다. 그래서 yield 키워드를 이해하기 위해 Generator라는 개념을 먼저 알고 이해하자.

 

 

Generator란? 🤔


 제네레이터(Generator)란 메모리를 효율적으로 사용하면서 반복을 수행하도록 돕는 객체(iterator 라고도 한다.)이다. 이게 무슨 말인가? 우리가 지금까지 수행했던 반복문은 효율적이지 않다는 것일까? 아래의 예제를 보자.

 

 

def list_number(number):
    result = []
    for i in range(1,number+1):
        result.append(i)
    return result

print(list_number(10))#길이가 10인 리스트가 출력됨 [1,2,...,10]

 위와 같이 반복이 존재하는 구간은 반복문을 활용해서, 사용해왔다. 그러나, 만약에 number가 몇십억이라면 어떨까? 몇십억의 반복문을 수행하고, 심지어 반환 값을 위해 받아야 하는 리스트도 몇십억짜리 리스트를 반환해야 한다. Generator는 이러한 부분을 조금 더 효율적으로 접근하고자 고안되었다.  그렇다면 'Generator를 쓰는 것이 왜 효율적인 것인가?'에 대해 궁금할 수 있다. 왜 효율적이라고 하는지 알아보자.

 

Iterable(반복 가능한) 특징 🔗 


 Generator 객체는 내부적으로 __iter__() 함수를 가지고 있다. __iter__()함수는 set, list, tuple 등 다양한 객체에서 가지고 있는데, __iter__() 함수가 있다는 것은 반복문을 통해 요소를 추출할 수 있음을 의미한다. 이를 반복 가능한 객체(iterable)하다고 말하는데, generator 객체 또한 반복 가능한 객체이다. 추가적으로  __next__() 함수가 존재하는데, 이를 통해 아래 그림과 같은 형식으로 요소를 하나씩 불러올 수 있다. 직접 호출할 수도 있지만, __iter__() 함수를 통해 내부적으로 __next__() 함수를 호출하며, 마지막 요소에 도달할 때까지 진행한다.

코딩 도장에서 표현한 generator 구성

 즉, 미리 요소를 리스트에 만들어 놓고, 꺼내는 것이 아니라, __next__() 함수를 통해서 필요할 때마다 접근하여 요소를 빼낸다. 여기서 의문점이 존재할 수 있다. '특정 위치를 어떻게 기억하지 ?' 필요할 때마다, 내부적으로 필요한 요소에 접근하기 위해서는 현재 상태를 기억해야 한다. 그것을 표현하기 위한 키워드가 'yield'다.

 

 

yield 란? 🤔


 yield를 표현하기 위해 정말 오래 돌아왔다. yield라는 영단어는 '양보하다'의 뜻이 있다. yield 키워드는 해당 키워드 라인을 실행하고 함수를 호출한 쪽으로 프로그램의 제어를 넘겨준다. 예제를 통해 알아보자.

 

def yield_test():
    for i in range(5):
        yield i
        print(i,'번째 호출!')

print(type(yield_test())) # <class 'generator'>

#CaseA. __next__() 함수를 통해 출력
t = yield_test()
print(t.__next__()) # 0
print(t.__next__()) # 0 번째 호출! 1
print(t.__next__()) # 1 번째 호출! 2
print(t.__next__()) # 2 번째 호출! 3
print(t.__next__()) # 3 번째 호출! 4
#print(t.__next__()) #Error

#for문을 이용하여 출력
print("<-->")
for k in yield_test():
    print(k)

#CaseB. result
# 0
# 0 번째 호출!
# 1
# 1 번째 호출!
# 2
# 2 번째 호출!
# 3
# 3 번째 호출!
# 4
# 4 번째 호출!

   CaseA를 보자. __next__() 함수가 실행되면, yield 키워드의 i인 0을 넘겨주고 멈춘다. 다시 __next__()가 실행되면, yield의 바로 밑 키워드인 print(i,'번째 호출')이 출력되고, 다시 반복문을 돌아서 yield 키워드를 만나 i를 반환하게 된다.(CaseB번과 비교해보았을 때, 직접 next 함수를 통해 실행하면 마지막 print부분이 출력이 안 되는 부분도 발견한 것 같다. 내부적으로 약간 차이가 있는 것으로 생각됨.) 핵심은 yield 키워드에서 함수가 끝나지 않은 상태로 대기하고 있는 것이다. 이렇게 yield 키워드를 통해 generator 객체를 만든다면, 필요할 때 마다 해당 객체를 통해 요소를 반환할 수 있다. 함수를 완전 실행시키는 것이 아니라, 일부를 실행시키고 일시 정지하기 때문에,  함수의 재사용성이 높아진다. 또한 전체를 메모리에 저장할 필요가 없다는 점에서 굉장히 효율적이다.

 

 

사용법 🔎


 yield 키워드는 함수에서 사용한다. yield 키워드 자체가 generator 객체를 만들기 위한 함수 내에서 필요한 실행 키워드 이므로 함수 내에서 사용한다. 함수 내에서 요소에 해당하는 부분을 yield 키워드 라인에 넣으면 된다.

위 코드에서는 0부터 5까지 숫자를 반환하는 함수를 만들었기 때문에, i가 yield 키워드 라인에 들어갔다.

 

 

 

정리 📦


  • yield 키워드는 호출한쪽으로 프로그램 제어를 넘겨주는 키워드이다.
  • 함수 내에서 사용한며,generator객체를 생성한다.
  • generator 객체는  iterator(반복성) 객체이다.
  • 해당 키워드는 필요할 때 마다 요소를 출력하므로, 메모리 관점에서 효율적이다. 

 

 

결론 🔑


 제어를 양보한다 라는 표현만 가지고는 완전히 이해하기 어려웠는데, 포스팅을 쓰면서, yield 키워드의 작동방식을 정확하게 이해하게 된 것 같다. 최근 selenium을 배우고 있는데, yield 키워드를 이용해 요긴하게 요소를 가져오고 있다. 익숙해지니, 오히려 더 쓰기 편한 것 같기도 하다. 익숙하지 않은 개념이라, 약간 난해 할 수 있지만, 써보면 generator와 iterator, iterable의 개념에 대해서도 쉽게 이해할 수 있게 될 것이다.

 

 

References 📑


 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함