(Closure in Wiki) In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment.[1] The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
(https://en.wikipedia.org/wiki/Closure_(computer_programming))
클로저 개념을 위한 기초 개념으로 변수 범위 (variable scope) 를 먼저 짚고 넘어가자
변수 범위 (variable scope)
전역변수를 함수 내에서 사용
# 전역변수 b를 함수 내에서 사용
b = 20
def func_v2(a):
print(a)
print(b)
func_v2(10)
# 10
# 20
전역변수 값을 함수 내에서 수정 : global keyword 사용
c = 30
def func_v3(a):
global c
print(a)
print(c)
c = 40
func_v3(10)
print(c)
# 10
# 30
# 40
Closure 정의
외부에서 호출된 함수 내 enclosed 된 scope의 값과 코드가 저장되어 이후에도 해당 상태에 다시 접근하여 계속 사용 가능한 개념이다. 일반적으로 우리가 아는 함수는 호출 및 실행을 하고 결과를 반환하면, 메모리에 함수 내의 내용이 남아있지 않지만, Closure는 결과값이 반환된 (코드와 값을 둘러싼 함수가 반환되기에) 후에도 계속 호출 및 새로운 인자를 넣어 재사용이 가능하다
Closure 개념을 배우기 전까지는, Class를 활용한 프로그래밍을 통해서만, 값을 누적 및 저장하고, 이를 재접근하거나 수정하는 기능이 가능하다고 생각했었다. 그런데 Closure을 활용하면 Class로만 가능하다고 생각했던 기능에 대해 함수만으로 프로그래밍이 가능하다
Closure 사용 이유
Closure 개념 이해를 위한 클래스 구현
아래 코드에서 Averager
의 객체인 averager_cls
의 call
함수가 실행된 후 종료되지만 해당 함수를 통해 self._series
에 값이 계속 누적 및 저장되고 있는 것을 알 수 있다. 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()
Closure 구현
위에서 Class로 구현했던 평균계산 기능을 Closure를 활용하여 구현해보자
# 클로저(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 # 함수자체를 return
avg_closure1 = closure_ex1()
print(avg_closure1(15)) # 15.0
print(avg_closure1(35)) # 25.0
print(avg_closure1(40)) # 30.0
print()
print()
Closure의 내부를 살펴보자
# function inspection
print(dir(avg_closure1)) # Closure 객체의 namespace의 모든 이름을 리스트로 출력 : __code__ 확인
print(dir(avg_closure1.__code__)) # compile된 함수의 body 객체 : co_freevars 확인
print(avg_closure1.__code__.co_freevars) # co_freevars 접근 : series 자유변수 확인
print(dir(avg_closure1.__closure__[0])) # Clousre 객체의 속성들 확인 : cell_contents 확인
print(avg_closure1.__closure__[0].cell_contents) # series 변수 값 확인
Outputs:
['__annotations__', '__call__', '__class__', '__closure__', '__code__', ....]
['__class__', '__delattr__', '__dir__' ,..., 'co_firstlineno', 'co_flags', 'co_freevars', ....]
('series',)
['__class__', '__delattr__', '__dir__' ,..., 'cell_contents']
[15, 35, 40]
Closure 사용 주의 : 자유변수에 값을 초기화하는 코드가 Closure 내부 함수에 존재할 때는 해당 자유변수를 곧바로 참조할 수 없어 에러가 발생한다.
# 잘못된 클로저 사용
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)) # 예외
이 때는 자유변수를 내부 함수 내에서도 참조하도록 nonlocal
keyword 사용하면 해결된다
# non local keyword 사용
def closure_ex3():
# Free variable
cnt = 0
total = 0
def averager(v):
nonlocal cnt, total
cnt += 1
total += v
return total / cnt
return averager
avg_closure3 = closure_ex3()
print(avg_closure3(15))
print(avg_closure3(35))
print(avg_closure3(40))
Futures 라이브러리와 파이썬 병렬성 구현 - GIL, concurrent.futures (0) | 2022.05.12 |
---|---|
코루틴 (Coroutine) (3) - 개념, 장점, 구현 (0) | 2022.05.11 |
코루틴 (Coroutine) (2) - 병행성과 Generator (0) | 2022.05.11 |
코루틴 (Coroutine) (1) - Iterator 와 Generator (0) | 2022.05.11 |
클로저 (Closure) (1) - 일급함수 (First-class function) (0) | 2022.05.06 |
댓글 영역