def makeInc (x, step):
def next():
nonlocal x, step
x = x + step
return x
return next
x = makeInc (0, 1)
y = makeInc (0, 10)
x1=x()
x2=x()
y1=y()
y2=y()
print( x1, x2, y1, y2)
The output is 1 2 10 20.
I am not sure why it gives these outputs, can anyone explain it in detail? Thanks!
The function makeInc() is a "factory" that makes function objects and returns them. The nonlocal declaration makes the function "close over" a variable. Usually you would make an explicit variable and close over that; in this case, the nonlocal declaration is closing over an argument variable from makeInc().
If you want to learn about closures and nonlocal here are a few references:
http://www.mondoinfo.com/blog/C182263547/E20060918200706/index.html
Python nonlocal statement
So, makeInc() makes a function object that has a start value and a "step" by which the start value will be incremented. Once you have made the custom function object, the final code calls the functions and gets the incrementing values.
Related
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.
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.
Here is a very simplified example of what I am trying to do:
x = 3
def f():
print(x)
x = 5
f() #f prints 5 but I want it to print 3.
Is there a way, when declaring the function, to turn x into a constant that points somewhere other than the global variable x? I can't provide arguments to the function.
This is a pretty common trick (you usually see it in lambda expressions that want to bind a particular value within a loop):
x = 3
def f(x=x):
print(x)
x = 5
f() # prints 3
The trick is that default parameter values are evaluated at the time of function definition, so in the expression x=x, the x on the right hand side is evaluated (producing the value 3) and then stored as the default value of the x parameter in the function (which shadows the x in the outer scope).
You could equivalently write:
x = 3
def f(n=x):
print(n)
x = 5
f() # prints 3
which has the same result, but doesn't shadow the x variable.
From what I understand, you seem to want x to hold two values simultaneously - which is what complex data structures are for. A list would work fine, or a dict:
>>> x = [3]
>>> def f():
... print(x[0]) # always refers to first element. Functionally constant.
...
>>> x.append(5)
>>> f()
3
>>>
However, it sounds like you really have an XY problem, where you're asking about your solution instead of your actual problem. Go back to your code and check if this seems to be the case. If so, we might be able to point you towards a better way of solving your real issue.
This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 9 years ago.
I am python beginner and at the moment I am struggling with python recursion function:
x = 10
n = 3
def rec(x,n):
if n>0:
#global x2
x2 = x*n
return rec(x2,n-1)
else:
return x2
# function call:
fcall = rec(x,n)
print fcall
What confuses me is that global x2 line. With it, the function is working fine returns 60 as expected, but without it I am getting an error message:
Local variable 'x2' referenced before assignment
Why is that so?
Seems like once n reaches value of 3, and the else condition gets executed, it does not know what x2 is?
If you leave out the global, then the variable is expected to be in local scope within that function. That means that it needs to be assigned somewhere to actually exist. As the only assignment is when the n > 0 condition is true, the return x2 will always try to return a variable that does not exist.
When you add the global, you put that variable into the global scope, making it available everywhere. So it’s assigned and can be returned.
However, it’s not really a good idea to have a function depend on a global variable like that. It seems very unobvious that a global variable is required for it to work. And in fact, it’s not necessary here: Instead of x2 you want to reference x.
def rec (x, n):
if n > 0:
x = x * n
return rec(x, n - 1)
else:
return x
That way, as long as n is still bigger than zero, you multiply the current x by n and temporarily assign it to x again, which you then pass to the recursive call. If n is equal to zero, then the else case applies, and you just return the current x that was passed to the function.
Your problem is that x2 is only a local variable, so when you call the function it doesn't know the old x2 anymore.
By adding the global x2 you put that variable in the global space, so now the function can recognise it.
Check this out: http://gettingstartedwithpython.blogspot.de/2012/05/variable-scope.html
What you actually want to return is x, not x2, because you are passing the value of x2 into the next call of your recursion.
def rec(x,n):
if n>0:
x2 = x*n
return rec(x2,n-1)
else:
return x
Without global x2, assigning to x2 creates a new local variable. With it, assigning to x2 makes it change the global x2 variable. This applies only to assignment, not to looking up variables.
The current stack frame (and thus all local variables associated with it) disappears when the function returns, but globals stay forever and ever and ever (and you should also feel very bad for using them).
And also you can refactor your rec function code to:
def rec(x, n):
return rec(x*n, n-1) if n else x
A typical cause of problems for beginning programmers is the habit of using too many variables. This can make things more complicated and harder to understand. Your code can be made simpler like this:
def rec(x, n):
if n > 0:
return rec(x*n, n-1)
return x
or even this, using the ternary operator:
def rec(x, n):
return rec(x*n, n-1) if n > 0 else x
You can then try it out by just doing:
rec(3, 10)
The sooner you can build a habit creating new variables only when they are needed or improve the readability of the code, the easier your programs will be to write, debug and read.
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.