python variable scope in nested functions - python

I am reading this article about decorator.
At Step 8 , there is a function defined as:
def outer():
x = 1
def inner():
print x # 1
return inner
and if we run it by:
>>> foo = outer()
>>> foo.func_closure # doctest: +ELLIPSIS
it doesn't print x. According to the explanation :
Everything works according to Python’s scoping rules - x is a local
variable in our function outer. When inner prints x at point #1 Python
looks for a local variable to inner and not finding it looks in the
enclosing scope which is the function outer, finding it there.
But what about things from the point of view of variable lifetime? Our
variable x is local to the function outer which means it only exists
while the function outer is running. We aren’t able to call inner till
after the return of outer so according to our model of how Python
works, x shouldn’t exist anymore by the time we call inner and perhaps
a runtime error of some kind should occur.
However, I don't really understand what the second paragraph means.
I understand inner() does get the value of x but why it doesn't print x out?
thanks
UPDATE:
Thanks all for the answers. Now I understand the reason.
the "return inner" is just a pointer to inner() but it doesn't get executed, that is why inner() doesn't print x as it is not called at all

I understand inner() does get the value of x but why it doesn't print
x out?
It doesn't print out anything because you've not called the inner function yet.
>>> def outer():
x = 1
def inner():
print x # 1
return inner
...
>>> func = outer()
>>> func
<function inner at 0xb61e280c>
>>> func()
1
This is called a closure, i.e even though the outer function is not in stack(finished executing) anymore but still the inner function that was returned from it remembers it's state.(i.e value of x)
>>> def outer():
x = 1
y = 2
def inner():
z=3
print x
return inner
...
>>> func = outer()
>>> func.func_code.co_freevars #returns the variables that were used in closure
('x',)
From the source code on how python decides it's a closure or not:
459 if len(code.co_freevars) == 0:
460 closure = NULL
461 else:
462 len(closure) == len(code.co_freevars)
In py3.x you can also modify the value of x using nonlocal statement inside inner function.
>>> def outer():
x = 1
def inner():
nonlocal x
x += 1
print (x)
return inner
...
>>> func = outer()
>>> func()
2
>>> func()
3
>>> func()
4

You are not calling inner. You have called outer, which returns inner, but without calling it. If you want to call inner, do foo() (since you assinged the result of outer() to the name foo).
The paragraph you quoted is sort of tangential to this issue. You say you already understand why inner gets the value of x, which is what that paragraph is explaining. Basically, if a local variable is used in a nested function, and that nested function is returned, the value of the variable is stored along with the returned function, even if the scope where that variable was defined in no longer active. Normally x would be gone after outer finished, because x is just local to outer. But outer returns inner, which still needs access to x. So x gets wrapped up into what's called a closure, so it can still be accessed by inner later on.

Related

In Python 3.0 a function returns none because a function does not store it’s output unless we use a return statement?

In Python, does a function just execute it’s code block & not store it unless we use a return statement?
When we print variables & expressions I understand we are printing values.
So I am thinking that a function performs it’s code block & then does not save that result unless we return it? Is this what’s happening in the computer?
Example 1
def add(a,b):
nums = a + b
print(add(2,4)+2)
Error
But when we use the return value statement it works
Example 2
def add(a,b):
nums = a + b
return nums
print(add(2,4) + 2)
Output: 8
The error was caused in the first example because the function just executed it’s code block & did not save the result therefore resulting in an error due to not being able to add None to an integer(2)?
&
It worked in example 2 because we saved the functions result with the return statement giving us an integer; Therefore allowing the print statement to print the result of the functions integer + the integer we added it to in the expression?
In python, functions are blocks of code that execute some logic of some sort (sometimes based on arguments passed into them and sometimes not). They are very broad and can do many different kinds of things depending on how they are constructed. I'm not exactly sure what you mean by "store the results" but hopefully some of the following explanation will help.
All variables created in a function are stored with the "local" scope, meaning that they are only existent when the function is running and are deleted the moment the function terminates. For example, in the following code, you cannot access the variable x after the function terminates:
def example():
x = 'Hello World'
print(x) #This prints: Hello World
example()
print(x) #This will give you a Reference error
If that is what you mean by "stores the results" then you are right: those results will not be stored. You can, however, declare a variable inside of a function to be a global variable, meaning that it can be accessed outside of the function too:
def example():
global x = 'Hello World'
print(x) #This prints: Hello World
example()
print(x) #This prints: Hello World
When you use the return statement in a function you are just telling the compiler that if a variable is set equal to a function call of said function, whatever follows the return statement is what that variable should be set equal to. However, simply returning a value does not "store" it. See the following code:
def example():
x = 'Hello World'
return x
example()
print(x) #This will still cause a reference error
x = example()
print(x) #This prints: Hello World
One final thing to note about the code above: as long as two variables are in different scopes, they can have the same name and not cause an error. The x inside the function is in a local scope and the x outside of the function is in the global scope which is why that does not cause an error.
Welcome to Stack Overflow. When I was learning programming, it helped me to think of calls to functions using an analogy to variables in math. In most languages, you can think of "substituting" the return value in for the function call, the same way you can substitute a literal number into a variable.
In math, you can do this:
m = 4
b = 2
y = m * x + b # Plug 4 and 2 in for "m" and "b"
y = 4 * x + 2
It's the same with value-returning functions:
def foo():
return 'bar'
>>> x = foo() # Plug the return value 'bar' in for "foo()"
>>> x
'bar'
In Python, when a function has no explicit return, the default return value is None. So:
def foo():
print('bar')
# No return, so Python implicitly returns None
>>> x = foo() # Plug the return value None in for "foo()"
'bar'
>>> x
None
the function define local variable even same name as global variable so when it executed if you don't return something or store the result in global variable the result not appears outside function
example
x = 10
def test():
x= 15
test()
print(x) # result 10
but if use global keyword you can access to global variable like this
x = 10
def test():
global x
x= 15
test()
print(x) #result 15
or if you return the value
x = 10
def test():
x= 15
return x
x = test()
print(x) #result 15

Inner function not printing nonlocal variable

I have one outer function and 2 inner functions.
def outer():
x = 'hello'
def inner1():
def inner2():
nonlocal x
x = 'python'
inner2()
print(x)
outer()
Please help me understand why the above code is not printing the value of x.
As per my understanding, it should print "hello"
Your code calls outer(), which in turn executes only one statement:
x = 'hello'
As it stands, the code in your question will print nothing.
If you were to add the line print(x) after the call to outer(), it would indeed print "hello", as you have suggested.
If you were to instead add the line inner1() to call the function by that name which is defined inside outer(), then inner1() would in turn call inner2() which would in turn cause x = 'python' to execute, and this would change the value of x and (thanks to the nonlocal x line within inner2()) the statement print(x) within inner1() would cause the code to print "python".

Empty Formal Arguments

def f():
print (x)
def g():
print (x)
x = 1
x = 3
f()
x = 3
g()
I am learning functions now, why are empty arguments used in these functions ?
No arguments are being used (as none are being passed).
Inside f, x is a free variable. There is no local variable by that name, so its value is looked up in the next enclosing scope, which in this case is the global scope.
Inside g, x is a local variable (by virtue of a value being assigned to it), but it is not yet defined when you call print(x). If you were to reverse the order of the assignment and the call to print, you would see 1 as the output, since the global variable by the same name is ignored.

Does Python maintain function environment after a function is called to implement closure?

I'm studying the materials in cs61a,but the 'withdraw' example in "2.4.4 Local State" gives me an illusion that Python maintains the function environment even after it's call is finished in order to implement closure.But it's seems too costly. So what the mechanism does python takes to implement closure.
When you define an outer functions that returns an inner function:
def outer():
x = 40
def inner():
return x + 2
return inner
you have access to the scope of the outer function:
>>> func = outer()
>>> func()
42
The the value for x is stored in the tuple __closure__:
>>> func.__closure__[0].cell_contents
40

How to declare a variable is from the enclosing scope?

How do I make code like the following work? I want to reference a variable, for assignment, in the enclosing function scope.
def outer():
x = 0
def inner():
x += 1
inner()
The code as written gives an UnboundLocalError. I understand why I get this error, I just don't know how I indicate that x comes from the wrapping scope.
You can do:
def outer():
x = [0]
def inner():
x[0] += 1
inner()
You can't rebind a non-local, but you can mutate it.
You cannot do what you ask in a clean way. There is nothing analagous to the global statement that can help you. You'll want to code it like this:
def outer():
x = 0
def inner(x):
return x + 1
x = inner(x)
This has the added advantage of making it explicitly clear as to how data passes into, and out of, the function.
Perhaps you will need to replace x with an object whose state can be mutated.

Categories

Resources