22. 약한참조, 반복자, 발생자




1 약한 참조


1-1 약한 참조의 정의

  • 약한 참조 (Weak Reference)
    • 레퍼런스 카운트로 고려되지 않는 참조

1-2 약한 참조의 필요성

inheritance

  • 1) 레퍼런스 카운트가 증가되지 않으므로 순환 참조가 방지된다.
    • 순환 참조 (Cyclic Reference)
      • 서로 다른 객체들 사이에 참조 방식이 순환 형태로 연결되는 방식
      • 독립적으로 존재하지만 순환 참조되는 서로 다른 객체 그룹은 쓰레기 수집이 안된다.
        • 주기적으로 순환 참조를 조사하여 쓰레기 수집하는 기능이 있지만, CPU 자원 낭비가 심하다.
        • 이러한 쓰레기 수집 빈도가 낮으면 순환 참조되는 많은 객체들이 메모리를 쓸데없이 점유하게 됨
  • 2) 다양한 인스턴스들 사이에서 공유되는 객체에 대한 일종의 케시(Cache)를 만드는 데 활용된다.

1-3 약한 참조 모듈

1) weakref.ref(o)

  • weakref 모듈의 ref(o) 함수

    • 객체 o에 대한 약한 참조를 생성한다.
    • 해당 객체가 메모리에 정상적으로 남아 있는지 조사한다.
      • 객체가 메모리에 남아 있지 않으면 None을 반환한다.
  • 약한 참조로 부터 실제 객체를 참조하는 방법

    • 약한 참조 객체에 함수형태 호출
In [1]:
import sys
import weakref # weakref 모듈 임포트
class C:
    pass
c = C() # 클래스 C의 인스턴스 생성 
c.a = 1 # 인스턴스 c에 테스트용 값 설정 
print("refcount -", sys.getrefcount(c)) # 객체 c의 레퍼런스 카운트 조회
print()

d = c # 일반적인 레퍼런스 카운트 증가 방법
print("refcount -", sys.getrefcount(c)) # 객체 c의 레퍼런스 카운트 조회
print()

r = weakref.ref(c) # 약한 참조 객체 r 생성
print("refcount -", sys.getrefcount(c)) # 객체 c의 레퍼런스 카운트 조회 --> 카운트 불변
print()
refcount - 2

refcount - 3

refcount - 3

In [2]:
print(r) # 약한 참조(weakref) 객체
print(r()) # 약한 참조로 부터 실제 객체를 참조하는 방법: 약한 참조 객체에 함수형태로 호출
print(c)
print(r().a) # 약한 참조를 이용한 실제 객체 멤버 참조
print()

del c # 객체 제거
del d
print(r()) # None을 리턴한다
print(r().a) # 속성도 참조할 수 없다
<weakref at 0x1084895e8; to 'C' at 0x1083f0f28>
<__main__.C object at 0x1083f0f28>
<__main__.C object at 0x1083f0f28>
1

None
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-a0bedb17ac14> in <module>()
      8 del d
      9 print(r()) # None을 리턴한다
---> 10 print(r().a) # 속성도 참조할 수 없다

AttributeError: 'NoneType' object has no attribute 'a'
  • 내장 자료형 객체 (리스트, 튜플, 사전 등)에 대해서는 약한 참조를 만들 수 없다.
In [3]:
d = {'one': 1, 'two': 2}
wd = weakref.ref(d)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-b2a48d12fd2b> in <module>()
      1 d = {'one': 1, 'two': 2}
----> 2 wd = weakref.ref(d)

TypeError: cannot create weak reference to 'dict' object

2) weakref.proxy(o)

  • weakref의 proxy(o)는 객체 o에 대한 약한 참조 프록시를 생성한다.
    • 프록시를 이용하면 함수 형식을 사용하지 않아도 실제 객체를 바로 참조할 수 있다.
    • ref(o) 함수보다 더 선호되는 함수
In [4]:
import sys
import weakref
class C:
    pass

c = C()
c.a = 2
print("refcount -", sys.getrefcount(c)) # 객체 c의 레퍼런스 카운트 조회
p = weakref.proxy(c) # 프록시 객체를 만든다
print("refcount -", sys.getrefcount(c)) # 객체 c의 레퍼런스 카운트 조회 --> 카운트 불변
print( )
print(p)
print(c)
print(p.a)
refcount - 2
refcount - 2

<__main__.C object at 0x10847ce10>
<__main__.C object at 0x10847ce10>
2
In [5]:
import weakref
class C:
    pass

c = C() # 참조할 객체 생성
r = weakref.ref(c) # weakref 생성
p = weakref.proxy(c) # weakref 프록시 생성
print(weakref.getweakrefcount(c)) # weakref 개수 조회
print(weakref.getweakrefs(c)) # weakref 목록 조회

#python2.x에서의 결과
# 2
# [<weakref at 0x10de07c58; to 'instance' at 0x10de06e60>, <weakproxy at 0x10de07ba8 to instance at 0x10de06e60>]
2
[<weakref at 0x108620048; to 'C' at 0x10861e550>, <weakproxy at 0x1084895e8 to C at 0x10861e550>]

1-4 약한 사전

  • 약한 사전 (Weak Dictionary)
    • 사전의 키(key)나 값(value)으로 다른 객체들에 대한 약한 참조를 지니는 사전
    • 주로 다른 객체들에 대한 캐시(Cache)로 활용
    • 일반적인 사전과의 차이점
      • 키(key)나 값(value)으로 사용되는 객체는 약한 참조를 지닌다.
      • 실제 객체가 삭제되면 자동적으로 약한 사전에 있는 (키, 값)의 쌍도 삭제된다.
      • 즉, 실제 객체가 사라지면 캐시역할을 하는 약한 사전에서도 해당 아이템이 제거되므로 효율적인 객체 소멸 관리가 가능하다.
  • weakref 모듈의 WeakValueDictionary 클래스
    • weakref 모듈의 WeakValueDictionary 클래스의 생성자는 약한 사전을 생성한다.
In [8]:
import weakref
class C:
    pass

c = C()
c.a = 4
d = weakref.WeakValueDictionary() # WeakValueDictionary 객체 생성
print(d)

d[1] = c # 실제 객체에 대한 약한 참조 아이템 생성
print(list(d.items())) # 사전 내용 확인
print(d[1].a) # 실제 객체의 속성 참조

del c # 실제 객체 삭제
print(list(d.items())) # 약한 사전에 해당 객체 아이템도 제거되어 있음

#python2.x에서의 결과
# <WeakValueDictionary at 4526484584>
# [(1, <__main__.C instance at 0x10dccad40>)]
# 4
# []
<WeakValueDictionary at 0x10847ce80>
[(1, <__main__.C object at 0x10861f048>)]
4
[]
  • 일반 사전을 통하여 동일한 예제를 수행하면 마지막에 해당 객체를 삭제해도 일반 사전 내에서는 여전히 존재함
In [9]:
class C:
    pass

c = C()
c.a = 4
d = {} # 일반 사전 객체 생성
print(d)

d[1] = c # 실제 객체에 대한 일반 참조 아이템 생성
print(list(d.items())) # 사전 내용 확인
print(d[1].a) # 실제 객체의 속성 참조

del c # 객체 삭제 (사전에 해당 객체의 레퍼런스가 있으므로 객체는 실제로 메모리 해제되지 않음)
print(list(d.items())) # 일반 사전에 해당 객체 아이템이 여전히 남아 있음
{}
[(1, <__main__.C object at 0x10861f048>)]
4
[(1, <__main__.C object at 0x10861f048>)]

2 반복자 (Iterator)


2-1 반복자 (Iterlator) 객체

  • 반복자 객체

    • 내부적으로 __next__(self)를 지니고 있는 객체
      • 내부적으로 지닌 Sequence 자료를 차례로 반환
      • 더 이상 넘겨줄 자료가 없을 때 StopIteration 예외를 발생시킴
    • next() 내장 함수에 대응됨
  • 임의의 객체에 대해 반복자 객체 생성 방법

    • iter(o) 내장 함수
      • 객체 o의 반복자 객체를 반환한다.
  • 반복자 객체의 메모리 효율성
    • 반복자가 원 객체의 원소들을 복사하여 지니고 있지 않다.
In [2]:
L = [1,2,3]
# print(next(L)) <-- 에러 발생

I = iter(L)
print(I)

# Python2.7에서는 반복자 객체에 직접 next() 메소드 호출
# print(I.next())
# print(I.next())
# print(I.next())
# print(I.next())

# Python3에서는 next() 내장 함수 사용
print(next(I))
print(next(I))
print(next(I))
print(next(I))
<list_iterator object at 0x108753710>
1
2
3
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-2-a4db7260333f> in <module>()
     15 print(next(I))
     16 print(next(I))
---> 17 print(next(I))

StopIteration: 
  • 리스트 객체에 대해 일반적인 for ~ in 반복 문 사용예
In [3]:
def f(x):
    print(x + 1)

for x in [1,2,3]:
    f(x)
2
3
4
  • 리스트 객체에 반복자를 활용한 예
In [4]:
def f(x):
    print(x + 1)

t = iter([1,2,3])
while 1:
    try:
        #x = t.next() <-- Python2.7
        x = next(t)
    except StopIteration:
        break
    f(x)
2
3
4
  • for ~ in 구문에 반복자를 활용할 수 있다.
    • for 문이 돌때 마다 반복자 객체의 next() 함수가 자동으로 호출되어 순차적으로 각 객체에 접근 가능하다.
    • StopIteration이 발생하면 for ~ in 구문이 자동으로 멈춘다.
In [5]:
def f(x):
    print(x + 1)

t = iter([1,2,3])
for x in t:
    f(x)
2
3
4
In [6]:
def f(x):
    print(x + 1)

for x in iter([1,2,3]):
    f(x)
2
3
4
In [7]:
def f(x):
    print(x + 1)

for x in iter((1,2,3)):
    f(x)
2
3
4

2-2 클래스에 반복자 구현하기

  • 내장 함수 iter()에 대응되는 __iter__(self) 및 next()에 대응되는 __next__(self)의 구현
    • 객체 o에 iter()를 호출하면 자동으로 __iter__(self) 함수 호출
    • __iter__(self) 함수는 __next__(self) 함수를 지닌 반복자 객체를 반환
In [11]:
class Seq:
    def __init__(self, fname):
        self.file = open(fname)
        
    def __getitem__(self, n):
       if n == 10:
           raise StopIteration
       return n
    
s = Seq('readme.txt') 
for line in s: 
    print(line)
0
1
2
3
4
5
6
7
8
9
In [13]:
#python3.x
class Seq:
    def __init__(self, fname):
        self.file = open(fname)
        
    def __getitem__(self, n): # for ~ in 이 호출될 때 __next__() 함수에 의하여 가려짐. 
       if n == 10:
           raise StopIteration
       return n

    def __iter__(self):
        return self
    
    def __next__(self):
        line = self.file.readline() # 한 라인을 읽는다.
        if not line: 
            raise StopIteration     # 읽을 수 없으면 예외 발생
        return line                 # 읽은 라인을 리턴한다.
    
s = Seq('readme.txt')     # s 인스턴스가 next() 메소드를 지니고 있으므로 s 인스턴스 자체가 반복자임 
for line in s: # 우선 __iter__() 메소드를 호출하여 반복자를 얻고, 반복자에 대해서 for ~ in 구문에 의하여 __next__() 메소드가 호출됨
    print(line),

print()

print(Seq('readme.txt'))

# list() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. 
print(list(Seq('readme.txt')))  

# tuple() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. 
print(tuple(Seq('readme.txt'))) 
abc

def

ghi

<__main__.Seq object at 0x10875da20>
['abc\n', 'def\n', 'ghi']
('abc\n', 'def\n', 'ghi')
In [3]:
#python2.x
class Seq:
    def __init__(self, fname):
        self.file = open(fname)
    #def __getitem__(self, n):
    #    if n == 10:
    #        raise StopIteration
    #    return n
    def __iter__(self):
        return self
    def next(self):
        line = self.file.readline() # 한 라인을 읽는다.
        if not line: 
            raise StopIteration     # 읽을 수 없으면 예외 발생
        return line                 # 읽은 라인을 리턴한다.
    
s = Seq('readme.txt')     # s 인스턴스가 next() 메소드를 지니고 있으므로 s 인스턴스 자체가 반복자임 
for line in s: # 우선 __iter__() 메소드를 호출하여 반복자를 얻고, 반복자에 대해서 for ~ in 구문에 의하여 next() 메소드가 호출됨
    print(line),

print()

print(Seq('readme.txt'))

print(list(Seq('readme.txt')))  # list() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. 
print(tuple(Seq('readme.txt'))) # tuple() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. 
abc
def
ghi

<__main__.Seq instance at 0x10ddc5680>
['abc\n', 'def\n', 'ghi\n']
('abc\n', 'def\n', 'ghi\n')

2-3 사전의 반복자

  • 사전에 대해 for ~ in 구문은 키에 대해 반복한다.
In [15]:
d = {'one':1, 'two':2, 'three':3, 'four':4, 'five':5}
for key in d:
    print(key, d[key])
one 1
two 2
three 3
four 4
five 5
In [16]:
d = {'one':1, 'two':2, 'three':3, 'four':4, 'five':5}
for key in iter(d):
    print(key, d[key])
one 1
two 2
three 3
four 4
five 5
  • python2.x

    • d.iterkeys() 함수
      • 사전 d가 지닌 키에 대한 반복자 객체를 반환한다.
  • python3.x

    • iter(d) 또는 iter(d.keys()) 사용
In [26]:
print(type(d.keys()))
print(type(iter(d.keys())))
<class 'dict_keys'>
<class 'dict_keyiterator'>
In [35]:
next(d.keys())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-35-c8d50e7b8c9b> in <module>()
----> 1 next(d.keys())

TypeError: 'dict_keys' object is not an iterator
In [36]:
next(iter(d.keys()))
Out[36]:
'one'
In [28]:
#python3.x
for key in d.keys():   # 키에 대한 반복자, iter(d.keys()) 가 반환한 반복자에 대해 __next__(self) 함수가 순차적으로 불리워짐
    print(key, end=" ")

print()

for key in iter(d.keys()):   # 키에 대한 반복자, iter(d.keys()) 가 반환한 반복자에 대해 __next__(self) 함수가 순차적으로 불리워짐
    print(key, end=" ")
      
#python2.x
# for key in d.iterkeys():   # 키에 대한 반복자, d.iterkeys() 가 반환한 반복자에 대해 next() 함수가 순차적으로 불리워짐
#     print(key,)
one two three four five 
one two three four five 
In [22]:
keyset = iter(d)

print(next(keyset))     # 반복자 객체는 항상 next() 내장 함수에 값을 반환할 수 있음 (내부적으로 __next__(self) 호출)

for key in keyset:      # keyset 반복자에 대해 next() 메소드가 순차적으로 호출됨
    print(key, end=" ")
one
two three four five 
  • python2.x

    • d.itervalues() 함수
      • 사전 d가 지닌 값에 대한 반복자 객체를 반환한다.
  • python3.x

    • iter(d.values()) 사용
In [24]:
print(type(d.values()))
print(type(iter(d.values())))
<class 'dict_values'>
<class 'dict_valueiterator'>
In [30]:
#python3.x
for key in d.values():   # 키에 대한 반복자, iter(d.keys()) 가 반환한 반복자에 대해 __next__(self) 함수가 순차적으로 불리워짐
    print(key, end=" ")

print()

for key in iter(d.values()):   # 키에 대한 반복자, iter(d.keys()) 가 반환한 반복자에 대해 __next__(self) 함수가 순차적으로 불리워짐
    print(key, end=" ")

#python2.x
# for value in d.itervalues():    # 값에 대한 반복자
#     print(value,) 
1 2 3 4 5 
1 2 3 4 5 
  • python2.x

    • d.iteritems() 함수
      • 사전 d가 지닌 (키, 값)에 대한 반복자 객체를 반환한다.
  • python3.x

    • iter(d.items()) 사용
In [31]:
print(type(d.items()))
print(type(iter(d.items())))
<class 'dict_items'>
<class 'dict_itemiterator'>
In [32]:
#python3.x
for key, value in iter(d.items()):   # 키에 대한 반복자, iter(d.keys()) 가 반환한 반복자에 대해 __next__(self) 함수가 순차적으로 불리워짐
    print(key, value)

#python2.x
# for value in d.iteritems():    # 값에 대한 반복자
#     print(key, value,) 
one 1
two 2
three 3
four 4
five 5

2-4 파일 객체의 반복자

  • 파일 객체는 그 자체가 반복자임
    • next() 함수에 의해 각 라인이 순차적으로 읽혀짐
In [37]:
#python3.x
f = open('readme.txt')
print("next(f) - ", next(f))
for line in f:   # f.next() 가 순차적으로 호출됨
    print(line,  )  
next(f) -  abc

def

ghi
In [21]:
#python2.x
f = open('readme.txt')
print("f.next()", f.next())
for line in f:   # f.next() 가 순차적으로 호출됨
    print(line,  )  
f.next() 1: Hello World

2: Hello World
3: Hello World
4: Hello World
5: Hello World

3 발생자


3-1 발생자란?

  • 발생자(Generator)

    • (중단됨 시점부터) 재실행 가능한 함수
  • 아래 함수 f()는 자신의 인수 및 내부 로컬 변수로서 a, b, c, d를 지니고 있다.

    • 이러한 a, b, c, d 변수들은 함수가 종료되고 반환될 때 모두 사라진다.
  • 발생자는 f()와 같이 함수가 (임시로) 종료될 때 내부 로컬 변수가 메모리에서 해제되는 것을 막고 다시 함수가 호출 될 때 이전에 수행이 종료되었던 지점 부터 계속 수행이 가능하도록 구현된 함수이다.
In [39]:
def f(a, b):
    c = a * b
    d = a + b
    return c, d
  • yield 키워드
    • return 대신에 yield에 의해 값을 반환하는 함수는 발생자이다.
    • yield는 return과 유사하게 임의의 값을 반환하지만 함수의 실행 상태를 보존하면서 함수를 호출한 쪽으로 복귀시켜준다.
  • 발생자는 곧 반복자이다.
    • 즉, 발생자에게 next() 호출이 가능하다.
In [41]:
def generate_ints(N):
    for i in range(N):
        yield i
In [42]:
gen = generate_ints(3) # 발생자 객체를 얻는다. generate_ints() 함수에 대한 초기 스택 프레임이 만들어지나 실행은 중단되어 있는 상태임
print(gen)

# print(gen.next()) 
# print(gen.next()) 
# print(gen.next()) 
# print(gen.next()) 

print(next(gen)) # 발생자 객체는 반복자 인터페이스를 가진다. 발생자의 실행이 시작됨. yield에 의해 값 반환 후 실행이 중단됨
print(next(gen)) # 발생자 실행 재개. yield에 의해 값 반환 후 다시 중단
print(next(gen)) # 발생자 실행 재개. yield에 의해 값 반환 후 다시 중단
print(next(gen)) # 발생자 실행 재개. yield에 의해 더 이상 반환할 값이 없다면 StopIteration 예외를 던짐
<generator object generate_ints at 0x108724d00>
0
1
2
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-42-fef329084206> in <module>()
     10 print(next(gen)) # 발생자 실행 재개. yield에 의해 값 반환 후 다시 중단
     11 print(next(gen)) # 발생자 실행 재개. yield에 의해 값 반환 후 다시 중단
---> 12 print(next(gen)) # 발생자 실행 재개. yield에 의해 더 이상 반환할 값이 없다면 StopIteration 예외를 던짐

StopIteration: 
  • 위와 같은 세부 동작 방식을 이용하여, 다음과 같이 for ~ in 구문에 적용할 수 있다.
In [75]:
for i in generate_ints(5):
    print(i, end=" ")
0 1 2 3 4 
  • 발생자 함수와 일반 함수의 차이점
    • 일반 함수는 함수가 호출되면 그 함수 내부에 정의된 모든 일을 마치고 결과를 반환함
    • 발생자 함수는 함수 내에서 수행 중에 중간 결과 값을 반환할 수 있음
  • 발생자가 유용하게 사용되는 경우
    • 함수 처리의 중간 결과를 다른 코드에서 참조할 경우
      • 즉, 모든 결과를 한꺼번에 반환 받는 것이 아니라 함수 처리 중에 나온 중간 결과를 받아서 사용해야 할 경우
    • 시퀀스 자료형을 효율적으로 만들고자 하는 경우

3-2 발생자 구문

  • 리스트 내포(List Comprehension)
    • 리스트 객체의 새로운 생성
    • 메모리를 실제로 점유하면서 생성됨
In [45]:
a = [k for k in range(100) if k % 5 == 0]
print(a)
type(a)
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
Out[45]:
list
  • 리스트 내포 구문에 []가 아니라 () 사용
    • 리스트 대신에 발생자 생성
      • 즉, 처음부터 모든 원소가 생성되지 않고 필요한 시점에 각 원소가 만들어짐
      • 메모리를 보다 효율적으로 사용할 수 있음
In [46]:
a = (k for k in range(100) if k % 5 == 0)
print(a)
type(a)
<generator object <genexpr> at 0x108759678>
Out[46]:
generator
In [47]:
# print(a.next())
# print(a.next())
# print(a.next())

print(next(a))
print(next(a))
print(next(a))
for i in a:
    print(i, end=" ")
0
5
10
15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 
  • 아래 예는 sum 내장 함수에 발생자를 넣어줌
    • sum을 호출하는 시점에는 발생자가 아직 호출되기 직전이므로 각 원소들은 아직 존재하지 않는다.
    • sum 내부에서 발생자가 지니고 있는 next() 함수를 호출하여 각 원소들을 직접 만들어 활용한다.
    • 메모시 사용 효율이 높다.
In [48]:
a = [1, 2, 3]
print(sum(a))
6
In [49]:
a = (k for k in range(100) if k % 5 == 0)
print(sum(a))
950

3-3 발생자의 활용 예 1 - 피보나치 수열

In [43]:
def fibonacci(a = 1, b = 1):
    while 1:
        yield a
        a, b = b, a + b
        
for k in fibonacci():  # 발생자를 직접 for ~ in 구문에 활용
    if k > 100: 
        break
    print(k, end=" ")
1 1 2 3 5 8 13 21 34 55 89 

3-4 발생자의 활용 예 2 - 홀수 집합 만들기

  • 반복자를 활용한 예
In [45]:
#python3.x
class Odds:
    def __init__(self, limit = None): # 생성자 정의
        self.data = -1                # 초기 값
        self.limit = limit            # 한계 값
        
    def __iter__(self):               # Odds 객체의 반복자를 반환하는 특수 함수
        return self

    def __next__(self):                   # 반복자의 필수 함수
        self.data += 2
        if self.limit and self.limit <= self.data:
            raise StopIteration
        return self.data

for k in Odds(20):
    print(k, end=" ")
print()
print(list(Odds(20))) # list() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 __next__(self)를 매번 호출하여 각 원소를 얻어온다. 
1 3 5 7 9 11 13 15 17 19 
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
In [43]:
#python2.x
class Odds:
    def __init__(self, limit = None): # 생성자 정의
        self.data = -1                # 초기 값
        self.limit = limit            # 한계 값

    def __iter__(self):               # Odds 객체의 반복자를 반환하는 특수 함수
        return self
    
    def next(self):                   # 반복자의 필수 함수
        self.data += 2
        if self.limit and self.limit <= self.data:
            raise StopIteration
        return self.data

for k in Odds(20):
    print(k,)
print()
print(list(Odds(20))) # list() 내장 함수가 객체를 인수로 받으면 해당 객체의 반복자를 얻어와 next()를 매번 호출하여 각 원소를 얻어온다. 
1 3 5 7 9 11 13 15 17 19
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
  • 발생자를 활용한 가장 좋은 예
In [50]:
def odds(limit=None):
    k = 1
    while not limit or limit >= k:
        yield k
        k += 2
        
for k in odds(20):
    print(k, end=" ")
print()
print(list(odds(20))) # list() 내장 함수가 발생자를 인수로 받으면 해당 발생자의 next()를 매번 호출하여 각 원소를 얻어온다. 
1 3 5 7 9 11 13 15 17 19 
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

참고 문헌: 파이썬(열혈강의)(개정판 VER.2), 이강성, FreeLec, 2005년 8월 29일