Why aren't python nested functions called closures?
A closure occurs when a function has access to a local variable from an enclosing scope that has finished its execution.
def make_printer(msg):
def printer():
print(msg)
return printer
printer = make_printer('Foo!')
printer()
When make_printer
is called, a new frame is put on the stack with the compiled code for the printer
function as a constant and the value of msg
as a local. It then creates and returns the function. Because the function printer
references the msg
variable, it is kept alive after the make_printer
function has returned.
So, if your nested functions don't
- access variables that are local to enclosing scopes,
- do so when they are executed outside of that scope,
then they are not closures.
Here's an example of a nested function which is not a closure.
def make_printer(msg):
def printer(msg=msg):
print(msg)
return printer
printer = make_printer("Foo!")
printer() #Output: Foo!
Here, we are binding the value to the default value of a parameter. This occurs when the function printer
is created and so no reference to the value of msg
external to printer
needs to be maintained after make_printer
returns. msg
is just a normal local variable of the function printer
in this context.
Can anybody explain about the closure of function in Python?
Higher-order functions
Functions like makeInc
that in turn, return another function are called higher order functions. Usually, functions are known to accept data as input and return data as output. With higher order functions, functions instead of data, either return code as output or accept code as input. This code is wrapped into a function. In Python, functions are first class citizens which means functions, just like data, can be passed around. For instance:
myvariable = print
Notice, how I have assigned print
to myvariable
and how I have dropped the parentheses after print
Functions without parentheses are called function objects. This means myvariable
now is just another name for print
:
print("Hello World!")
myvariable("Hello World!")
Both of the above statements do the exact same thing. What can be assigned to variables can also be returned from functions:
def myfunction():
return print
myfunction()("Hello World!");
Now let's look at your example:
def makeInc(x):
def inc(y):
return y + x
return inc
makeInc
is a function that accepts a parameter called x
. It then defines another nested inner function called inc
which takes in a parameter called y
. The thing about nested functions is that they have access to the variables of the enclosing function as well. Here, inc
is the inner function but it has access to x
which is a variable of the enclosing outer scope.
The last statement return inc
returns the inner function to the caller of makeInc
. What makeInc
essentially is doing, is creating a custom function based on the parameter it receives.
For instance:
x = makeInc(10)
makeInc
will first accept 10 and then return a function that takes in an argument y
and it increments y
by 10.
Here, x
is a function that takes in any argument y
and then increments it by 10:
x(42) # Returns 52
nonlocal
However, there is a caveat when using nested functions:
def outer():
x = 10
def inner():
x = 20
inner()
print(x) # prints 10
Here, you would assume that the last print
statement will print 20. But no! When you assign x = 20
in the inner function, it creates a new local variable called x
which is initialized to 20. The outer x
remains untouched. To modify the outer x
, use the nonlocal
keyword:
def outer():
x = 10
def inner():
nonlocal x = 20
inner()
print(x) # prints 20
If you are directly reading x
inside inner()
instead of assigning to it, you do not need nonlocal
.
Python closures, create new int with different ID for every nested function
The issue is that you're not really creating a closure, you're just giving it the same lambda each time.
Instead you can return a function that binds the value of i
:
from threading import Thread
from time import sleep
def call(time,f):
def caller():
sleep(time)
f()
Thread(target=caller).start()
def bind(i):
return lambda: print(i)
def func():
for i in range(1,6):
call(i, bind(i))
func()
bind
returns a function where the value of i
is "closed over", or as you appropriately called it, a closure.
Having a hard time understanding nested functions
The important thing here is that in Python, functions themselves are objects, too. Functions can return any type of object, so functions can in principle also return functions. And this is what echo
does.
So, the output of your function call echo(2)
is again a function and echo(2)("hello")
evaluates that function - with "hello"
as an input argument.
Maybe it is easier to understand that concept if you would split that call into two lines:
my_function_object = echo(2) # creates a new function
my_function_object("hello") # call that new function
EDIT
Perhaps this makes it clearer: If you spell out a function name without the brackets you are dealing with the function as an object. For example,
x = numpy.sqrt(4) # x is a number
y = numpy.sqrt # y is a function object
z = y(4) # z is a number
Next, if you look at the statement return echo_word
in the echo
function, you will notice that what is returned is the inner function (without any brackets). So it is a function object that is returned by echo
. You can check that also with print(echo(2))
Can you explain closures (as they relate to Python)?
Closure on closures
Objects are data with methods
attached, closures are functions with
data attached.
def make_counter():
i = 0
def counter(): # counter() is a closure
nonlocal i
i += 1
return i
return counter
c1 = make_counter()
c2 = make_counter()
print (c1(), c1(), c2(), c2())
# -> 1 2 1 2
Related Topics
Tensorflow Install Fails with "Compiletime Version 3.5 of Module Does Not Match Runtime Version 3.6"
Unicodeencodeerror: 'Charmap' Codec Can't Encode - Character Maps to <Undefined>, Print Function
Animated Sprite from Few Images
How to Hide the Console When I Use Os.System() or Subprocess.Call()
How to Create a Constant in Python
What Exactly Does the .Join() Method Do
How Are Tuples Unpacked in for Loops
How to Rotate an Image Around an Off Center Pivot in Pygame
How to Use Expect on Windows Without Installing Cygwin
Are Python Variables Pointers? or Else, What Are They
Remove Duplicate Dict in List in Python
Error: Pandas Hashtable Keyerror
Group by in Group by and Average
What Is the Reason for Performing a Double Fork When Creating a Daemon