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
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'
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
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
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>]
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 []
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>)]
반복자 객체
임의의 객체에 대해 반복자 객체를 얻어오는 방법
집합적 객체 A --> iter(A) --> 반복자 객체 B 반환 --> next(B) --> 집합적 자료형 안의 내부 원소를 하나씩 반환
반복자 객체의 메모리 효율성
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 0x105e65978> 1 2 3
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-1-a4db7260333f> in <module> 15 print(next(I)) 16 print(next(I)) ---> 17 print(next(I)) StopIteration:
K = {1: "aaa", 2: "bbb", 3: "ccc"}
J = iter(K)
print(next(J))
print(next(J))
print(next(J))
1 2 3
t = iter([1, 2, 3])
while 1:
try:
x = next(t)
except StopIteration:
break
print(x)
1 2 3
def f(x):
print(x + 1)
for x in [1,2,3]:
f(x)
2 3 4
def f(x):
print(x + 1)
t = iter([1, 2, 3])
for x in t:
f(x)
2 3 4
def f(x):
print(x + 1)
for x in iter([1, 2, 3]):
f(x)
2 3 4
def f(x):
print(x + 1)
for x in iter((1, 2, 3)):
f(x)
2 3 4
class Seq:
def __getitem__(self, n):
if n == 10:
raise IndexError()
return n
s = Seq()
for line in s:
print(line)
0 1 2 3 4 5 6 7 8 9
class Seq:
def __init__(self, file):
self.file = open(file)
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 0x110e2a748> ['abc\n', 'def\n', 'ghi'] ('abc\n', 'def\n', 'ghi')
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 object at 0x110e2a780> ['abc\n', 'def\n', 'ghi'] ('abc\n', 'def\n', 'ghi')
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()를 매번 호출하여 각 원소를 얻어온다.
0 1 2 3 4 5 6 7 8 9 <__main__.Seq object at 0x110e07d68> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
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
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
python3.x
print(type(d.keys()))
print(type(iter(d.keys())))
<class 'dict_keys'> <class 'dict_keyiterator'>
next(d.keys())
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-17-c8d50e7b8c9b> in <module> ----> 1 next(d.keys()) TypeError: 'dict_keys' object is not an iterator
next(iter(d.keys()))
'one'
#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=" ")
one two three four five one two three four five
keyset = iter(d)
print(next(keyset)) # 반복자 객체는 항상 next() 내장 함수에 값을 반환할 수 있음 (내부적으로 __next__(self) 호출)
for key in keyset: # keyset 반복자에 대해 next() 메소드가 순차적으로 호출됨
print(key, end=" ")
one two three four five
print(type(d.values()))
print(type(iter(d.values())))
<class 'dict_values'> <class 'dict_valueiterator'>
#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=" ")
1 2 3 4 5 1 2 3 4 5
print(type(d.items()))
print(type(iter(d.items())))
<class 'dict_items'> <class 'dict_itemiterator'>
#python3.x
for key, value in iter(d.items()): # 키에 대한 반복자, iter(d.keys()) 가 반환한 반복자에 대해 __next__(self) 함수가 순차적으로 불리워짐
print(key, value)
one 1 two 2 three 3 four 4 five 5
#python3.x
f = open('readme.txt')
print("next(f) - ", next(f))
for line in f: # f.next() 가 순차적으로 호출됨
print(line)
next(f) - abc def ghi
발생자(Generator)
아래 함수 f()는 자신의 인수 및 내부 로컬 변수로서 a, b, c, d를 지니고 있다.
발생자는 f()와 같이 함수가 (임시로) 종료될 때 내부 로컬 변수가 메모리에서 해제되는 것을 막고 다시 함수가 호출 될 때 이전에 수행이 종료되었던 지점 부터 계속 수행이 가능하도록 구현된 함수이다.
def f(a, b):
c = a * b
d = a + b
return c, d
x, y = f(1, 2)
print(x, y)
2 3
yield 키워드
발생자는 곧 반복자이다.
def f(a, b):
c = a * b
d = a + b
yield c, d
g = f(1, 2)
x, y = next(g)
print(x, y)
x, y = next(g)
print(x, y)
2 3
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-32-79212a10ecdb> in <module> 9 print(x, y) 10 ---> 11 x, y = next(g) 12 print(x, y) StopIteration:
def f(a, b):
for _ in range(2):
c = a * b
d = a + b
yield c, d
g = f(1, 2)
x, y = next(g)
print(x, y)
x, y = next(g)
print(x, y)
2 3 2 3
def generate_ints(N):
for i in range(N):
yield i
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 0x105eec308> 0 1 2
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-29-0043ca97ed03> in <module> 10 print(next(gen)) # 발생자 실행 재개. yield에 의해 값 반환 후 다시 중단 11 print(next(gen)) # 발생자 실행 재개. yield에 의해 값 반환 후 다시 중단 ---> 12 print(next(gen)) # 발생자 실행 재개. yield에 의해 더 이상 반환할 값이 없다면 StopIteration 예외를 던짐 StopIteration:
for i in generate_ints(5):
print(i, end=" ")
0 1 2 3 4
발생자 함수와 일반 함수의 차이점
발생자가 유용하게 사용되는 경우
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]
list
a = (k for k in range(100) if k % 5 == 0)
print(a)
type(a)
<generator object <genexpr> at 0x105eec200>
generator
# 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
a = [1, 2, 3]
print(sum(a))
6
a = (k for k in range(100) if k % 5 == 0)
print(sum(a))
950
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
#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]
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일