-
ClosureDynamicPL/Python 2019. 11. 7. 17:09
1. Overview
Closure can be consist of a function plus an extended scope that contains the free variables.
2. Description
2.1 Free Variable and Inner Function
Functions defined inside another function can access the outer (nonlocal) variables
def outer(): x = [1, 2, 3] print('outer:', hex(id(x))) def inner(): print('inner:', hex(id(x))) print(x) return inner fn = outer() fn()
x is a free variable in inner. It is bound to the variable x in outer this happens when outer runs that mean inner function is created. This the closure. When we return inner, we are actually "returning" the closure.
We can assign that return value to a variable fn. When we called fn at that time python determined the value of x in the extended scope. But notice that outer had finished running before we called fn. Its scope was gone.
2.2 Python Cells and Multi-scoped variables
def outer(): x = 'python' def inner(): print(x) return inner
Here the value of x is shared between two scopes
- outer
- closure
The label x is in two different scopes but always reference the same value. Python does this by creating a cell as an intermediary object. In effect, both variables x (in outer and inner), point to the same cell. When requesting the value of the variable, Python will "double-hop" to get to the final value
2.3 Closure
You can think of the closure as a function plus an extended scope that contains the free variables. The free variable's value is the object the cell points to so that could change over time. Every time the function in the closure is called and the free variable is referenced. Python looks up the cell object, and then whatever the cell is pointing to
2.4 Multiple Instances of Closures
Every time we run a function, a new scope is created. If that function generates a closure, a new closure is created every time as well.
2.5 Shared extended scopes
adders = [] for n in range(1,4): adders.append(lambda x: x + n) adders[0](10) # 13 adders[1](10) # 13 adders[2](10) # 13
n = 1: the free variable in the lambda is n, and it is bound to the "n" we created in the loop
n = 2: the free variable in the lambda is n, and it is bound to the (same) n we created in the loop
n = 3: the free variable in the lambda is n, and it is bound to the (same) n we created in the loop
Python does not evaluate the free variable n until the adders[i] function is called. Since all three functions in adders are bound to the same n, by the time we call adders[0], the value of n is 3(the last iteration of the loop set n of 3)
3. Example
3.1 Introspection 1
3.2 Introspection 2
3.3 Nested Closures
def incrementer(n): # inner + n is a closure def inner(start): current = start # inc + current + n is a closure def inc(): a = 10 # local var nonlocal current current += n return current return inc return inner # inner fn = incrementer(2) fn.__code__.co_freevars # 'n', n = 2 # inc inc_2 = fn(100) inc_2.__code__.co_freevars # 'current, 'n', current = 100, n = 2 # call inc inc_2() # 102, current = 102, n = 2 inc_2() # 104, current = 104, n = 2
4. References
https://en.wikipedia.org/wiki/Closure_(computer_programming)
'DynamicPL > Python' 카테고리의 다른 글
Context manager (0) 2019.11.09 Decorators (0) 2019.11.07 First-Class Object and High-Order function (0) 2019.11.02 Slice (0) 2019.10.28 Sequence (0) 2019.10.26