When does Python evaluate a variable in a function definition - python

I am confused as when does Python evaluates a variable. For example if I do:
p=4
def g():
return p
p=5
print g()
I get 5, so in the definition of g, the variable p remains unevaluated. It is evaluated when we call g
However if I do:
def make_fun():
p=5
def f():
return p
return f
f=make_fun()
p=6
print f()
I get 5. Why does notf contains the unevaluated variable p? I would like to have a clear idea as of when precisely the evaluation of variables takes place.

Python executes the code as it loads the module / script. Hence in your first example it's not "evaluated when we call g", it simply means that the latest value of p is 5 at the time when g is executed. Python will lookup p and it'll return the current value (5).
In your second example p is a local variable. That means it's not affected by p = 6 in the global scope. Or to be clear, the p in f() in your second example is the local variable set within make_fun(). So that's the value you'll get back, it's not the p at the outer (global) scope, which is a different p.

p=4 #you are setting global p to 4
def g():
return p
p=5 #you are setting global p to 5
print g() #you are printing the result of g, which is not setting any value;
so 5 is printed;
but here:
def make_fun():
p=5 #you are setting local p to 5
def f():
return p
return f
f=make_fun()
p=6 #you are setting global p to 6
print f() #you printing the result of a function in f, which is getting the local p
so local p= 5 is printed;
hope i understood you right!

It's not a question of when evaluation takes place. In both of your examples, p is evaluated when the function is called. The real question is when does that variable get declared.
In your first example, p is declared outside of the function and therefore has global scope. Any changes to p impact it wherever it it referenced.
In your second example, you are declaring another variable p inside the function. This is local to the function and not impacted by any code outside of the function. Even if you then go ahead and declare another global p as you do in the line after f=make_fun(), this global variable is overridden within the function by the local variable.
As an experiment, try printing p after you print f() and you'll see that it's value will be 6 as you assigned it.

Related

Can anybody explain about the closure of function in Python?

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.

Difference in transmitting variables in an inner function in Python

In Python, is there a difference between the following two codes both of which gives the same result c = [0,-10]?
def foo1():
a = [0,1]
def foo2():
a[1] = -10
foo2()
return a
c = foo1()
and
def foo1():
a = [0,1]
def foo2(b):
b[1] = -10
foo2(a)
return a
c = foo1()
Some commentator suggests this is answered by this question. But it does not since my question asks about the passing of variables through an inner function whilst the linked question does not.
In the first, a is a free variable whose value is taken from the nearest enclosing scope (in this case, the scope of foo1) that defines a.
In the second, b is a local variable initialized using the argument passed to foo2 when it is called, which is the variable a defined in foo1.
In each case, you assign -10 to the second "slot" of the same list.

Are lists implicitly 'global' inside of functions? [duplicate]

This question already has answers here:
Modify global list inside a function
(5 answers)
Closed 6 years ago.
I was experimenting with this code:
def a():
#global p
p.append(4);
d=9
p=[2,3];
d=8
a();
print p # ----> [2, 3, 4]
print d # ----> 8
The variable d value is not changed as I didn't use the global keyword. But the list p was modified in function even though I didn't use global. Are all lists global by default in functions?
The critical difference is the assignment here. You are fine calling methods on existing global objects, but you can't assign to them without calling them global. In your code, the name d is being reassigned to reference another value. If you changed p with assignment you'd have a similar result
def a():
p = [5, 7] # new local variable, doesn't change global
p.append(9) # doesn't change global p
This makes sense if you think about what happens when python encounters the name for the first time. In the function you've provided, python will see p.append and say "hm, I don't have a local by the name p, let me look in the enclosing scope." It sees the global p and uses that.
In the example I've shown, python will say "there's no explicit global so I assume this is supposed to be a new local variable." and create one.
Names in python are just references. If python followed the behavior you are expecting you'd need a global for every function you called, let me explain:
def a():
p.append(1) # I should need 'global p' to do this
This would mean if you had
def g():
...
def f():
g() # this would also need 'global g', otherwise how does it see g?
def f2():
global g
def g(): # changes the global g function
return 0

How does python remember values of enclosing scope's variable when nested function in called?

I read an example on closure
def outer():
x = 1
def inner():
print x # 1
return inner
The life of our variable x is only till the time the function outer runs .
When i call outer and it returns the adress of inner , the variable x should have died because the function outer has exited but still how can i acess it through the returned inner function ?
python variable scope in nested functions
This link answered that the value of variables used by inner is stored when outer returns inner function to caller , but
Now see this code
def outer():
x = 1
def inner():
print x*n # n not defined anywhere
return inner
I ran this code and
This piece of code doesn't give any error when i call outer , that means that python doesn't checks the variables used by inner function while running outer . so how does it know it has to retain 'x' , after exiting ?
The life of our variable x is only till the time the function outer runs
No. x lives as long as it's reachable from somewhere. And, it is — from the inner function.
This piece of code doesn't give any error when i call outer , that means that python doesn't checks the variables used by inner function while running outer
n could be defined after calling outer, but before calling its result, inner. In that case inner's body if perfectly legitimate.
If you didn't return inner, then there'd be no reason to keep x, because you can't reach inner, thus can't somehow involve x into computation.
The way x is remembered is because python has a special __closure__ attribute that remembers the object from the enclosing scope that the local function needs and keeps a reference to it so it can be used in the local function:
def outer():
x = 1
def inner():
print(x)
return inner
o = outer()
print(o.__closure__)
(<cell at 0x7fa18a2fc588: int object at 0x9f8800>,)
If you assign o = outer() and then call o() you are then calling the inner function and you will get an error, calling outer() does not call the inner function.
If you were to declare a function f and put a print(n) in the body then unless you actually call f you won't get an error so the same logic applies to your inner function, you are not actually calling it with outer() so you don't get an error:
def f():
print(n) # will only error when we actually call `f()`
If calling the inner function happened when you called outer it would defeat the purpose, consider the following function factory where we take in an exponent e to raise i to:
def raise_exp(e):
def power(i):
return i ** e # n not defined anywhere
return power
sq = raise_exp(2) # set exponent but does not call power
print(sq(2)) # now we actually all power
cube = raise_exp(3)
print(cube(3))
25
27

Variable scope in nested functions

Could someone explain why the following program fails:
def g(f):
for _ in range(10):
f()
def main():
x = 10
def f():
print x
x = x + 1
g(f)
if __name__ == '__main__':
main()
with the message:
Traceback (most recent call last):
File "a.py", line 13, in <module>
main()
File "a.py", line 10, in main
g(f)
File "a.py", line 3, in g
f()
File "a.py", line 8, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
But if I simply change the variable x to an array, it works:
def g(f):
for _ in range(10):
f()
def main():
x = [10]
def f():
print x[0]
x[0] = x[0] + 1
g(f)
if __name__ == '__main__':
main()
with the output
10
11
12
13
14
15
16
17
18
19
The reason I am confused is, if from f() it can't access x, why it becomes accessible if x is an array?
Thanks.
But this answer says the problem is with assigning to x. If that's it,
then printing it should work just fine, shouldn't it?
You have to understand the order in which things happen. Before your python code is even compiled and executed, something called a parser reads through the python code and checks the syntax. Another thing the parser does is mark variables as being local. When the parser sees an assignment in the code in a local scope, the variable on the lefthand side of the assignment is marked as local. At that point, nothing has even been compiled yet--let alone executed, and therefore no assignment takes place; the variable is merely marked as a local variable.
After the parser is finished, the code is compiled and executed. When execution reaches the print statement:
def main():
x = 10 #<---x in enclosing scope
def f():
print x #<-----
x = x + 1 #<-- x marked as local variable inside the function f()
the print statement looks like it should go ahead and print the x in the enclosing scope (the 'E' in the LEGB lookup process). However, because the parser previously marked x as a local variable inside f(), python does not proceed past the local scope (the 'L' in the LEGB lookup process) to lookup x. Because x has not been assigned to in the local scope at the time 'print x' executes, python spits out an error.
Note that even if the code where an assignment occurs will NEVER execute, the parser still marks the variable on the left of an assignment as a local variable. The parser has no idea about how things will execute, so it blindly searches for syntax errors and local variables throughout your file--even in code that can never execute. Here are some examples of that:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
a b c #...yet the parser finds a syntax error here
return f
f = dostuff()
f()
--output:--
File "1.py", line 8
a b c
^
SyntaxError: invalid syntax
The parser does the same thing when marking local variables:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
x = 0 #..yet the parser marks x as a local variable
return f
f = dostuff()
f()
Now look what happens when you execute that last program:
Traceback (most recent call last):
File "1.py", line 11, in <module>
f()
File "1.py", line 4, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
When the statement 'print x' executes, because the parser marked x as a local variable the lookup for x stops at the local scope.
That 'feature' is not unique to python--it happens in other languages too.
As for the array example, when you write:
x[0] = x[0] + 1
that tells python to go lookup up an array named x and assign something to its first element. Because there is no assignment to anything named x in the local scope, the parser does not mark x as a local variable.
The reason is in first example you used an assignment operation, x = x + 1, so when the functions was defined python thought that x is local variable. But when you actually called the function python failed to find any value for the x on the RHS locally, So raised an Error.
In your second example instead of assignment you simply changed a mutable object, so python will never raise any objection and will fetch x[0]'s value from the enclosing scope(actually it looks for it firstly in the enclosing scope, then global scope and finally in the builtins, but stops as soon as it was found).
In python 3x you can handle this using the nonlocal keyword and in py2x you can either pass the value to the inner function or use a function attribute.
Using function attribute:
def main():
main.x = 1
def f():
main.x = main.x + 1
print main.x
return f
main()() #prints 2
Passing the value explicitly:
def main():
x = 1
def f(x):
x = x + 1
print x
return x
x = f(x) #pass x and store the returned value back to x
main() #prints 2
Using nonlocal in py3x:
def main():
x = 1
def f():
nonlocal x
x = x + 1
print (x)
return f
main()() #prints 2
The problem is that the variable x is picked up by closure. When you try to assign to a variable that is picked up from the closure, python will complain unless you use the global or nonlocal1 keywords. In the case where you are using a list, you're not assigning to the name -- You can modify an object which got picked up in the closure, but you can't assign to it.
Basically, the error occurs at the print x line because when python parses the function, It sees that x is assigned to so it assumes x must be a local variable. When you get to the line print x, python tries to look up a local x but it isn't there. This can be seen by using dis.dis to inspect the bytecode. Here, python uses the LOAD_FAST instruction which is used for local variables rather than the LOAD_GLOBAL instruction which is used for non-local variables.
Normally, this would cause a NameError, but python tries to be a little more helpful by looking for x in func_closure or func_globals 2. If it finds x in one of those, it raises an UnboundLocalError instead to give you a better idea about what is happening -- You have a local variable which couldn't be found (isn't "bound").
1python3.x only
2python2.x -- On python3.x, those attributes have changed to __closure__ and __globals__ respectively
The problem is in the line
x = x + 1
This is the first time x being assigned in function f(), telling the compiler that x is a local name. It conflicts with the previous line print x, which can't find any previous assignment of the local x.
That's where your error UnboundLocalError: local variable 'x' referenced before assignment comes from.
Note that the error happens when compiler tries to figure out which object the x in print x refers to. So the print x doesn't executes.
Change it to
x[0] = x[0] + 1
No new name is added. So the compiler knows you are referring to the array outside f().

Categories