Closure(클로저) 사용 이유
- 서버 프로그래밍 -> 동시성(Concurrency)제어 -> 메모리 공간에 여러 자원이 접근 -> 교착상태(Dead Lock)
- 메모리를 공유하지 않고 메시지 전달로 처리하기 함
- 클로저는 공유하되 변경되지 않는(Immutable, Read Only) 적극적으로 사용 -> 함수형 프로그래밍
- 클로저는 불변자료구조 및 atom, STM -> 멀티스레드(Coroutine) 프로그래밍에 강점
클래스를 이용한 closure 구현
# 클래스 이용한 클로저 개념
class Averager():
def __init__(self):
self._series = []
def __call__(self, v): # 클래스를 함수처럼 호출 가능.
self._series.append(v)
print('inner >>> {} / {}'.format(self._series, len(self._series)))
return sum(self._series) / len(self._series)
# 인스턴스 생성
averager_cls = Averager()
# 누적
print(averager_cls(15))
print(averager_cls(35))
print(averager_cls(40))
print()
print(dir(averager_cls))
--------------------------------------------[result]
inner >>> [15] / 1
15.0
inner >>> [15, 35] / 2
25.0
inner >>> [15, 35, 40] / 3
30.0
['__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_series']
클로저 구현 예시
- 외부에서 호출된 함수의 변수값, 상태(레퍼런스) 복사후, 저장 -> 후에 접근(엑세스) 가능
# 클로저(Closure) 사용
def closure_ex1():
# Free variable
series = []
# 클로저 영역
def averager(v):
# series = [] # 주석 해제 후 확인
series.append(v)
print('inner >>> {} / {}'.format(series, len(series)))
return sum(series) / len(series)
return averager
avg_closure1 = closure_ex1()
print(avg_closure1)
print(avg_closure1(15))
print(avg_closure1(35))
print(avg_closure1(40))
--------------------------------------------[result]
<function closure_ex1.<locals>.averager at 0x000002AE9AA08B70>
inner >>> [15] / 1
15.0
inner >>> [15, 35] / 2
25.0
inner >>> [15, 35, 40] / 3
30.0
# function inspection
print(dir(avg_closure1))
print('***********************************************************')
print(dir(avg_closure1.__code__))
print('***********************************************************')
print(avg_closure1.__code__.co_freevars) # ('series',)
print('***********************************************************')
# 외부에서 호출된 함수의 변수값, 상태(레퍼런스) 복사후, 저장 -> 후에 접근(엑세스) 가능
print(dir(avg_closure1.__closure__[0]))
print('***********************************************************')
print(avg_closure1.__closure__[0].cell_contents)
--------------------------------------------[result]
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
***********************************************************
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
***********************************************************
('series',)
***********************************************************
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
***********************************************************
[15, 35, 40]
클로저를 잘못 구현한 예시
# 잘못된 클로저 사용
def closure_ex2():
# Free variable
cnt = 0
total = 0
def averager(v):
cnt += 1 # cnt = cnt + 1
total += v
return total / cnt
return averager
avg_closure2 = closure_ex2()
print(avg_closure2(15)) # 예외
--------------------------------------------[result]
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-46-655eb182c9e0> in <module>
14 avg_closure2 = closure_ex2()
15
---> 16 print(avg_closure2(15)) # 예외
<ipython-input-46-655eb182c9e0> in averager(v)
6
7 def averager(v):
----> 8 cnt += 1 # cnt = cnt + 1
9 total += v
10 return total / cnt
UnboundLocalError: local variable 'cnt' referenced before assignment
클로저를 정상 구현한 예시
# Nonlocal -> Free variable
def closure_ex3():
# Free variable
cnt = 0
total = 0
def averager(v):
nonlocal cnt, total # <== 이 부분이 중요함
cnt += 1
total += v
print('>>>', total, cnt)
return total / cnt
return averager
avg_closure3 = closure_ex3()
print(avg_closure3(15))
print(avg_closure3(35))
print(avg_closure3(40))
print()
print(avg_closure3.__code__.co_freevars) # ('series',)
# 외부에서 호출된 함수의 변수값, 상태(레퍼런스) 복사후, 저장 -> 후에 접근(엑세스) 가능
print(dir(avg_closure3.__closure__[0]))
print(len(avg_closure3.__closure__))
print(avg_closure3.__closure__[0].cell_contents)
print(avg_closure3.__closure__[1].cell_contents)
--------------------------------------------[result]
>>> 15 1
15.0
>>> 50 2
25.0
>>> 90 3
30.0
('cnt', 'total')
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
2
3
90
코루틴 예외 처리 만들기 - 강제 예외처리 발생시키기
def sum_coroutine():
try:
total = 0
while True:
x = (yield)
total += x
except RuntimeError as e:
print(e)
yield total # 코루틴 바깥으로 값 전달
co = sum_coroutine()
next(co)
for i in range(20):
co.send(i)
print(co.throw(RuntimeError, '예외로 코루틴 끝내기')) # 190, 코루틴의 except에서 yield로 전달받은 값
---------------------- [실행결과]
예외로 코루틴 끝내기
190
'Python > Intermediate' 카테고리의 다른 글
[Python] 병행성(Concurrency) - basic (0) | 2021.05.25 |
---|---|
[Python] Decorator (0) | 2021.05.25 |
[Python] 일급함수 (0) | 2021.05.24 |
[Python] 고급 - Dict 및 Set(2) (0) | 2021.05.24 |
[Python] 고급 - Dict 및 Set(1) (0) | 2021.05.23 |