I'm currently learning about closure and this code was presented:
def outer_func(x):
y = 4
def inner_func(z):
print(f"x = {x}, y = {y}, z = {z}")
return x + y + z
return inner_func
for i in range(3):
closure = outer_func(i)
print(f"closure({i+5}) = {closure(i+5)}")
I understand x is defined at the point outer_func is assigned to closure, and y is defined within the function each time. My question is how is z defined? Shouldn't the call to closure overwrite the value of x? How does python know to assign this new value to z?
My question is how is z defined?
z is a parameter of inner_func. It means it's always local to the inner_func function(local variable). When the outer_func function is called the body of it will be executed, which indeed first create inner_func and give you back a reference to it. Then whatever you pass to inner_func, it uses as the z. It's a normal argument passing.
Shouldn't the call to closure overwrite the value of x?
inner_func is your closure. It has access to it's enclosing scope which is outer_func. x is a parameter of outer_func, it gets it's value from executing the line:
closure = outer_func(i)
So whatever you pass to outer_func becomes available to the inner_func's body. (Closure mechanism)
How does python know to assign this new value to z?
I already answered this in the first part. In the line:
print(f"closure({i+5}) = {closure(i+5)}")
you are passing the value of z in closure(i+5) part.
Q: Now where does inner_func get y's value from? A: Again it's a closure, it has access to outer_func namespace. y is always defined when outer_func() is called. Nothing can change it's value in your code. That loop can only change x because it comes from outside. (argument)
Your question is:
I understand x is defined at the point outer_func is assigned to closure, and y is defined within the function each time. My question is how is z defined? Shouldn't the call to closure overwrite the value of x? How does python know to assign this new value to z?
Your code is:
def outer_func(x):
y = 4
def inner_func(z):
print(f"x = {x}, y = {y}, z = {z}")
return x + y + z
return inner_func
for i in range(3):
closure = outer_func(i)
print(f"closure({i+5}) = {closure(i+5)}")
The call to closure is effectively a call to inner_func (not outer_func) and the argument passed to closure will be assigned to z (not x).
Explanation:
Each execution of closure = outer_func(i) does the following:
call outer_func() thereby effectively assigning the passed argument i to the variable x in the scope of outer_func()
execute y = 4
leave outer_func() with a return value of type function whose value is inner_func
assign the return value of outer_func() (namely the function inner_func) to closure, with ongoing access to the variables in the containing scope of inner_func() (namely, the scope of outer_func()).
Each call to closure() within the for loop does the following:
call inner_func() thereby assigning the passed argument to z in the scope of inner_func()
execute the body of inner_func() where x and y have the values they had at the time of the call to outer_func() that created this copy of inner_func as a closure.
Related
Python
Can anyone help me to understand this code, I am new to Python, how does this function work?
def makeInc(x):
def inc(y):
return y + x
return inc
incOne = makeInc(1)
incFive = makeInc(5)
print(incOne(5)) # returns 6
print(incFive(5)) # returns 10
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.
What is happening here is that makeInc() returns a function handle pointing to specific implementation of inc(). So, calling makeInc(5) "replaces" the x in inc(y) to 5 and returns the callable handle of that function. This handle is saved in incFive. You can now call the function as defined (inc(y)). Since you set x=5 before, the result will be y+5.
This question already has an answer here:
How do the scoping rules work with classes?
(1 answer)
Closed 2 years ago.
Why does this function output 0 1?
Why is a class in a function behaving like a function? Why didn't I have to call A like a function?
How does the assignment statement after print() affect the values of x and y? When I assign value to x, x becomes 0 and if I assign a value to y, y becomes 0.
x = 0
y = 0
def f():
x = 1
y = 1
class A:
print(x,y)
x = 99
f()
From the Python 3.9 documentation :
If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block.
The line x = 99 in the class definition, makes x local to the class block.
In a class block,
References follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.
At the point the print(x,y) is executed, x is an unbound local variable. It's value is looked up in the global namespace, where x = 0.
When a name is used in a code block, it is resolved using the nearest enclosing scope.
When print(x, y) is executed, the value of y is looked up in the nearest enclosing scope, which is the body of def f(), where y = 1.
So print(x, y) outputs 0 1.
def func(x):
print "inside function" ,id(x)
x = 2
x = 50
print "outside function" ,id(x)
print 'Value of x before function call is', x
func(x)
print 'Value of x after function call is', x
output:
outside function 6486996
Value of x before function call is 50
inside function 6486996
Value of x after function call is 50
Assuming that id() gives memory location of object. Even though both are saving at the same location, if x value is changed in func(), it is not effected outside.
Ah, but the id(x) call in the function references the global x that was passed to the function, but the x = 2 creates a new local x. Try this:
def func(x):
print "inside function", id(x)
x = 2
print "still inside function", id(x)
x = 50
print "outside function" , id(x)
print 'Value of x before function call is', x
func(x)
print 'Value of x after function call is', x
typical output
outside function 168950596
Value of x before function call is 50
inside function 168950596
still inside function 168951172
Value of x after function call is 50
An assignment usually changes the binding between name and object (if you don't do sth like x = x, of course). It doesn't do any alterations on the object (which wouldn't work on ints anyway as they are immutable, but just as a side note)
So in this case your x inside the function points to the 50 object until you change it. Then it points to a different object. The object itself is not affected.
To point out what happens step by step:
outside x points to int object with value 50
function call: inside x points to same object
inside x is changed to point to different object, having value 2
return: outside x still points to 50.
if u want know more about it, i think u need understand basic python fully.
point about your question:
mutable object as parameter
The function gets a reference to that object and could mutate it, but if you re-bind the reference in the method, outer scope know nothing,after done, the outer reference would still point the original object.
immutable object as parameter
Still can't re-bind the outer reference, and can't even mutate this object.
update for comments: so u pass x(Integer immutable) to function call, you can't mutate this object. and u you re-bind the x refer in the function, outer scope know nothing,after done, the outer reference would still point the original integer 50 object.
When I run this code, I get this result:
15
15
I expect the output should be
15
17
but it is not. The question is: why?
def make_adder_and_setter(x):
def setter(n):
x = n
return (lambda y: x + y, setter)
myadder, mysetter = make_adder_and_setter(5)
print myadder(10)
mysetter(7)
print myadder(10)
You are setting a local variable x in the setter() function. Assignment to a name in a function marks it as a local, unless you specifically tell the Python compiler otherwise.
In Python 3, you can explicitly mark x as non-local using the nonlocal keyword:
def make_adder_and_setter(x):
def setter(n):
nonlocal x
x = n
return (lambda y: x + y, setter)
Now x is marked as a free variable and looked up in the surrounding scope instead when assigned to.
In Python 2 you cannot mark a Python local as such. The only other option you have is marking x as a global. You'll have to resort to tricks where you alter values contained by a mutable object that lives in the surrounding scope.
An attribute on the setter function would work, for example; setter is local to the make_adder_and_setter() scope, attributes on that object would be visible to anything that has access to setter:
def make_adder_and_setter(x):
def setter(n):
setter.x = n
setter.x = x
return (lambda y: setter.x + y, setter)
Another trick is to use a mutable container, such as a list:
def make_adder_and_setter(x):
x = [x]
def setter(n):
x[0] = n
return (lambda y: x[0] + y, setter)
In both cases you are not assigning to a local name anymore; the first example uses attribute assignment on the setter object, the second alters the x list, not assign to x itself.
Python 2.x has a syntax limitation that doesn't allow to capture a variable in read/write.
The reason is that if a variable is assigned in a function there are only two possibilities:
the variable is a global and has been declared so with global x
the variable is a local of the function
more specifically it's ruled out that the variable is a local of an enclosing function scope
This has been superseded in Python 3.x with the addition of nonlocal declaration. Your code would work as expected in Python 3 by changing it to
def make_adder_and_setter(x):
def setter(n):
nonlocal x
x = n
return (lambda y: x + y, setter)
The python 2.x runtime is able to handle read-write closed over variable at a bytecode level, however the limitation is in the syntax that the compiler accepts.
You can see a lisp compiler that generates python bytecode directly that creates an adder closure with read-write captured state at the end of this video. The compiler can generate bytecode for Python 2.x, Python 3.x or PyPy.
If you need closed-over mutable state in Python 2.x a trick is to use a list:
def make_adder_and_setter(x):
x = [x]
def setter(n):
x[0] = n
return (lambda y: x[0] + y, setter)
Your inner def setter(n) function defines its own local variable x. That hides the other x variable that was a parameter of make_adder_and_setter (makes a hole in the scope). So the setter function has no side effect. It just sets the value of an inner local variable and exits.
Maybe it will be clear for you if you try the code below. It does exactly the same thing, just uses the name z instead of x.
def make_adder_and_setter(x):
def setter(n):
z = n
return (lambda y: x + y, setter)
myadder, mysetter = make_adder_and_setter(5)
print myadder(10)
mysetter(7)
print myadder(10)
Let's say I have a function
def x():
print(20)
Now I want to assign the function to a variable called y, so that if I use the y it calls the function x again. if i simply do the assignment y = x(), it returns None.
You simply don't call the function.
>>> def x():
>>> print(20)
>>> y = x
>>> y()
20
The brackets tell Python that you are calling the function, so when you put them there, it calls the function and assigns y the value returned by x (which in this case is None).
When you assign a function to a variable you don't use the () but simply the name of the function.
In your case given def x(): ..., and variable silly_var you would do something like this:
silly_var = x
and then you can call the function either with
x()
or
silly_var()
when you perform y=x() you are actually assigning y to the result of calling the function object x and the function has a return value of None. Function calls in python are performed using (). To assign x to y so you can call y just like you would x you assign the function object x to y like y=x and call the function using y()
The syntax
def x():
print(20)
is basically the same as x = lambda: print(20) (there are some differences under the hood, but for most pratical purposes, the results the same).
The syntax
def y(t):
return t**2
is basically the same as y= lambda t: t**2. When you define a function, you're creating a variable that has the function as its value. In the first example, you're setting x to be the function lambda: print(20). So x now refers to that function. x() is not the function, it's the call of the function. In python, functions are simply a type of variable, and can generally be used like any other variable. For example:
def power_function(power):
return lambda x : x**power
power_function(3)(2)
This returns 8. power_function is a function that returns a function as output. When it's called on 3, it returns a function that cubes the input, so when that function is called on the input 2, it returns 8. You could do cube = power_function(3), and now cube(2) would return 8.
lambda should be useful for this case.
For example,
create function y=x+1
y=lambda x:x+1
call the function
y(1)
then return 2.
I don't know what is the value/usefulness of renaming a function and call it with the new name. But using a string as function name, e.g. obtained from the command line, has some value/usefulness:
import sys
fun = eval(sys.argv[1])
fun()
In the present case, fun = x.
def x():
print(20)
return 10
y = x
y()
print(y)
gives the output
20
<function x at 0x7fc548cd3040>
so it does not actually assign the value returned by x() to the variable y.