# this is the enclosing/outer function
def my_enclosing(para1, para2):
# this is the closure function
def inner():
print(para1, para2)
# Note: The inner function is returned not called
return inner
## Initializing the closure function
my_closure = my_enclosing(10, 20)
# calling 'my_closure', which is just referring to 'inner()' as
# 'my_enclosing()' has returned it, so
my_closure() # 10, 20
# here 'para1' & 'para2' are free variables attached to 'inner()' function
# so even if 'my_enclosing()' is removed, 'inner()' can still access them
del my_enclosing
# now calling 'my_enclosing()' should raise 'NameError',
# Note: Comment the following line to execute the program further
my_enclosing() # NameError
# but 'my_closure()' should still have access to 'para1' & 'para2'
my_closure() # 10 20
# Is it magic?
## Example: Create a callable that prints maximum from all previous values
## Creating a callable using a class,
# Note: We'll learn more about classes in next chapter,
# you can comeback to this one later
class Make_maxer:
def __init__(self):
self.my_values = {10, 50, 80}
self.all_time_max = max(self.my_values)
def __call__(self, new_value):
self.my_values.add(new_value)
current_max = max(self.my_values)
if current_max > self.all_time_max:
print(f"Found new max: {current_max}")
self.all_time_max = current_max
# creating a instance of 'Make_maxer'
my_maxer = Make_maxer()
my_maxer(20)
my_maxer(95) # Found new max: 95
## Now creating the same with closures
def make_maxer():
# a closure can access this variables
my_values = {10, 50, 80}
all_time_max = max(my_values)
def maxer(new_value):
# Note: 'all_time_max' is nonlocal because we have its assignment below
nonlocal all_time_max
my_values.add(new_value)
current_max = max(my_values)
if current_max > all_time_max:
print(f"Found new max: {current_max}")
all_time_max = current_max
return maxer
## Creating closure object
my_maxer = make_maxer()
# Note: 'my_maxer()' is just referring to 'maxer()',
# checking its name
print(my_maxer.__name__) # maxer
# adding some values to maxer
my_maxer(42)
my_maxer(105) # Found new max: 105
## The free variable's names can be found in '__code__' attribute of a function
print(my_maxer.__code__.co_freevars) # ('all_time_max', 'my_values')
# their values are attached to the '__closure__' attribute
closure_values = my_maxer.__closure__
print(closure_values[0].cell_contents) # 105
print(closure_values[1].cell_contents) # {105, 10, 42, 80, 50}
# so not magic after all..