# Everything is an Object

• In other languages, function is just an executable codes, but in Python everything is an object. So function is a type of object.
• In python, a function is an object, moreover, a function can be an argument in another function or a function can be a result returned from another function
def test():
pass
a = test
print(type(a))

<class 'function'>


## 1.1 What is a closure?

In a function, we can return a result which is another function, see below:

def curve_pre():
def curve():
pass
# we can't invoke curve() on the global level
curve()

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

3         pass
4 # we can't invoke curve() on the global level
----> 5 curve()

NameError: name 'curve' is not defined

def curve_pre():
def curve():
print("This is a function")
pass
return curve
f = curve_pre()
f()

This is a function

# define a curve function
def curve_pre():
a = 25
def curve(x):
return a * x ** 2
return curve

f = curve_pre()
print(f(2))

100

a = 10
f = curve_pre()
# note global a will not affect the result, as f will take regional a = 25
print(f(2))

100


Closure = function + environmental variables, so closure will not be affected by the external varialbes.

Closure’s definition has something to do with the environmental variables. If there’s no local variable, then closure will be none; however if there is local varialbe, closure will be defined.

# In this case, because a was not defined within the function, so it will use global variable a
a = 25
# define a curve function
def curve_pre():
def curve(x):
return a * x ** 2
return curve
a = 10
f = curve_pre()
# note global a will not affect the result, as f will take regional a = 25
print(f(2))
print(f.__closure__)

40
None


Closure is stored in __closure__

# define a curve function
def curve_pre():
a = 25
def curve(x):
return a * x ** 2
return curve
f = curve_pre()
print(f(2))
print(f.__closure__)
print(f.__closure__[0].cell_contents)

100
(<cell at 0x0000022EF0B8D798: int object at 0x000000005F075F30>,)
25


## 1.2 What’s the meaning of using a closure?

A closure saves both the function and environment variables. Function alone will not guarantee the result will be consistent.

def f1():
a = 10
def f2():
a = 20 # a is an environmental variable that won't affect global var
print(a)
print(a)
f2()
print(a)

f1()

10
20
10

def f1():
a = 10
def f2():
a = 20 # a is an environmental variable that won't affect global var
return(a**2)
return f2

f = f1()
f()

400

print(f)
print("~~~~~~~~~~~~~~~~~")
print(f.__closure__)

<function f1.<locals>.f2 at 0x0000022EF0B67AE8>
~~~~~~~~~~~~~~~~~
None

def f1():
a = 10
def f2():
# remove a assignment, a will have connection with the environment variable
# If a is a regional var, it will not be a closure
return(a**2)
return f2

f = f1()
f()
print(f)
print("~~~~~~~~~~~~~~~~~")
print(f.__closure__)

<function f1.<locals>.f2 at 0x0000022EF0B67D90>
~~~~~~~~~~~~~~~~~
(<cell at 0x0000022EF0B43D98: int object at 0x000000005F075D50>,)


So a cannot be a regional variable that has assignment, it needs to have reference or connection with the environment variables

## 1.3 An Example with Closure

Closure is relevant with functional programming

origin = 0

def go(step):
new_pos = origin + step
origin = new_pos # origin is a local variable and it will not look for it externally
return origin

print(go(2)) # 2
print(go(3)) # 5
print(go(6)) # 11

---------------------------------------------------------------------------

UnboundLocalError                         Traceback (most recent call last)

<ipython-input-115-f664e25bc942> in <module>
6     return origin
7
----> 8 print(go(2)) # 2
9 print(go(3)) # 5
10 print(go(6)) # 11

<ipython-input-115-f664e25bc942> in go(step)
2
3 def go(step):
----> 4     new_pos = origin + step
5     origin = new_pos # origin is a local variable and it will not look for it externally
6     return origin

UnboundLocalError: local variable 'origin' referenced before assignment

origin = 0

def go(step):
new_pos = origin + step
# origin = new_pos
return origin

print(go(2)) # 2
print(go(3)) # 5
print(go(6)) # 11

0
0
0


Without using closure, we need to use global variable. Try to avoid changing/accessing global var.

origin = 0

def go(step):
global origin
new_pos = origin + step
origin = new_pos # origin is a local variable and it will not look for it externally
return origin

print(go(2)) # 2
print(go(3)) # 5
print(go(6)) # 11

2
5
11


With closure, we can use nonlocal to forcibly let python know that an environment variable is not a local var. Closure stores the state of pos after revoking internal function each time.

origin = 0

def factory(pos):
def go(step):
nonlocal pos # pos is not a local var
new_pos = pos + step
pos = new_pos
return new_pos
return go

tourist = factory(origin)

print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
print(tourist(2)) # 2
print(tourist.__closure__[0].cell_contents)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
print(tourist(3)) # 5
print(tourist.__closure__[0].cell_contents)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
print(tourist(6)) # 11
print(tourist.__closure__[0].cell_contents)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
print(origin) # note that global var origin was not changed

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13
13
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16
16
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22
22
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0