Closures¶
History¶
“A record storing a function together with an environment”
That is pretty much it!
Dates back to early functional languages
LISP
Scheme
Today¶
Also available in modern languages
Javascript
C++: explicit capturing in lambdas
C#
Definitely not unique to Python
Although Wikipedia starts out with Python examples
def
is a Statement¶
def
create a variable (the function’s name) that references a function object⟶ Functions are objects, just like integers are
def f():
pass
type(f)
function
Variable assignment (of function objects)
def f():
print('f called')
g = f # <--- assign one function to another
g() # <--- calls f (as g)
f called
def
can be used anywhere - e.g. inside another function⟶ local function definition - local variable
⟶ Functions can create and return other functions
Functions That Create Functions¶
A function that creates a function
def create_f():
def f():
print('inner f called')
return f
Create a function
inner = create_f()
inner
is a variable that references a function objectCall it:
inner()
inner f called
Inner Function Reaches Out To Global Scope¶
Variables in global scope can be accessed by “inner” functions
No surprise: global variables can be accessed by any function
No surprise: global variables have program lifetime
glob = 666
def create_f():
def f():
print('inner f called, glob is', glob)
return f
Calling the returned function reveals value of
glob
inner = create_f()
inner()
inner f called, glob is 666
And Intermediate Scope? ⟶ Closure¶
Semi global - variables in the creating scope
Those do not outlive the creating function
⟶ Closure
def create_f():
intermediate = 1 # <--- captured in closure of f
def f():
print('inner f called, intermediate =',
intermediate) # <--- used *after* f has been returned
return f
inner = create_f()
create_f()
is overintermediate
is long out of scopeBut is still alive in the closure
inner()
inner f called, intermediate = 1
A Less Theoretical “Use Case”¶
def create_print(msg): # <--- parameters are local variables to the callee
def p():
print(msg) # <--- local variable captured
return p
print_blah = create_print('blah')
print_something = create_print('something')
print_blah()
print_something()
blah
something
Scope Issues: Assignment to Global Scope (global
Keyword)¶
First assignment creates variable in local scope
The following is wrong! (At least if you want to assign to global
g
)
g = 1
def create_f():
def f():
g = 2 # <--- *local* variable created
print('inner f called, g =', g)
return f
inner = create_f()
inner()
print('global g =', g)
inner f called, g = 2
global g = 1
⟶ Global
g
still1
Fix:
global
keyword
g = 1
def create_f():
def f():
global g # <--- every mention of g means the *global* g
g = 2
print('inner f called, g =', g)
return f
inner = create_f()
inner()
print('global g =', g)
inner f called, g = 2
global g = 2
Scope Issues: Assignment to Intermediate Scope (nonlocal
Keyword)¶
And now, what about assignment to intermediate scope? To a variable in the closure?
Who does this?
Many non-obvious use cases, used to improve job security
def create():
intermediate = 1
def assign():
nonlocal intermediate # <--- that is the point! nonlocal!
intermediate = 2
print('assign: intermediate =', intermediate)
def check():
print('check: intermediate =', intermediate)
return assign, check
assign, check = create()
assign()
check()
assign: intermediate = 2
check: intermediate = 2