Why doesn't a sub-function inherit scope in Python? - python

I don't understand why the following doesn't work:
def foo( x ):
n = 1
summe = 0
def bar():
n -= 1
for i in range(0,10):
y = x+i+n
x += i
summe += y
print "{0} = {1} + {2} + {3}".format(y,x,i,n)
bar()
print "summe =", summe
return summe
Why is it that bar() doesn't inherit the scope of foo()? Is this a C'ism that I need to forget? Is there a way I can make that work?

PEP 3104 provides an explanation and a solution for this problem. The issue is Python treats any assignment to a name as a local variable declaration.
>>> n = 1
>>> def bar():
>>> n = n + 1
>>>
>>> bar()
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
bar()
File "<pyshell#7>", line 2, in bar
n = n + 1
UnboundLocalError: local variable 'n' referenced before assignment
There is a few workarounds for this problem if you use a Python version without the nonlocal keyword. One ugly trick is to wrap your variable in a list:
>>> n=[1]
>>> def bar():
>>> n[0] = n[0] + 1
>>>
>>> bar()
>>> n
[2]
Although this trick works, it is usually better to rewrite the code to remove the need for non-local assignments.

I actually found this question while searching for a solution to a slightly different problem. Local variables aren't inherited by sub's inherited but there's nothing stopping you from passing the variable to the inner function and then assigning the results on return.
This is in addition to the nonlocal statement in PEP 3104. It's slightly less ugly and makes memorizing yet another python keyword less crucial.
def foo( x ):
n = 1
summe = 0
def bar(n):
n -= 1
return n
for i in range(0,10):
n = bar(n)
y = x+i+n
x += i
summe += y
print "{0} = {1} + {2} + {3}".format(y,x,i,n)
print "summe =", summe
return summe

Related

A function returns another function. Why the function inside can scope a list from the parent but not a constant?

Consider this function:
def g():
x = []
def f():
x.append([0])
print(x)
pass
return f
Calling it:
test = g()
test()
I get the following output:
Out: [[0]]
We can reinitialize the test function and call it multiple times:
test = g()
for i in range(3):
test()
Resulting in the following output:
Out: [[0]]
[[0], [0]]
[[0], [0], [0]]
However, defining the following function:
def j():
x = 1
def f():
x += 1
print(x)
pass
return f
And calling it:
test = j()
test()
Results in an error:
UnboundLocalError: local variable 'x' referenced before assignment
The list seems to be in the inner function scope while the value is not. Why is this happening?
#rassar clearly explained the reason. Here I give a solution:
def j():
x = 1
def f():
nonlocal x
x += 1
print(x)
pass
return f
Actually this is not a simple question. Even though += looks like a bounded method call, an in-place operation (if you have experience in other languages). But what runs under is something like x = x.__add__(1) or x = x.__iadd__(1). So this is an assignment.
This is because j uses an assignment expression, where g uses a method call. Remember that x += 1 is equivalent to x = x + 1. If you change g to:
def g():
x = []
def f():
x += [[0]]
print(x)
pass
return f
You get:
>>> test = g()
>>> test()
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
test()
File "<pyshell#18>", line 5, in f
x += [[0]]
UnboundLocalError: local variable 'x' referenced before assignment
Because, the python complier knows that the variable 'x' in function f is a local variable not in the function j. So the error you mentioned above is occurred.
So you should use nonlocal. Please refer the link below.
Accessing parent function's variable in a child function called in the parent using python

Unable to import global variables in Python [duplicate]

This question already has answers here:
Visibility of global variables in imported modules
(9 answers)
Closed 5 years ago.
All of the following is in one file:
def inner_foo(in_arg):
global x;
x += 99
return in_arg + 1
def outer_foo(in_arg):
global x;
x = 0
output = inner_foo(in_arg)
return output
result = outer_foo(5)
print("result == ", result)
print("x == ", x)
It works just fine when it is all in one file. Here is what gets printed:
result == 6
x == 99
However, if we try to split the program across multiple files, we run into problems.
# CONTENTS OF inner_foo.py
def inner_foo(in_arg):
global x;
x += 99
return in_arg + 1
Here is the other file:
# CONTENTS OF outer_foo.py
from inner_foo import inner_foo
def outer_foo(in_arg):
global x
x = 0
output = inner_foo(in_arg)
return output
result = outer_foo(5)
print("result == ", result)
print("x == ", x)
We get an error NameError: name 'x' is not defined on the line x += 99 inside inner_foo.py
Changing the import statement to include x (from inner_foo import inner_foo, x) gives us:
ImportError: cannot import name 'x'
Python "global" scope is actually module-level, as you inferred when you tried to import. The problem is, when you do x += 1, that is the equivalent to x = x + 1, but x has never been defined in your module. So, suppose we have a.py:
def f():
global x
x += 1
There is no x in a, there is only f. So now, let's open up a REPL:
>>> import a
>>> a.f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/juan/workspace/temp/globals_modules/a.py", line 3, in f
x += 1
NameError: name 'x' is not defined
And, as stated above, "global scope" is actually module-level scope, so f's global scope is module a's namespace, not necessarily the caller's global scope, i.e.:
>>> x = 0
>>> a.f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/juan/workspace/temp/globals_modules/a.py", line 3, in f
x += 1
NameError: name 'x' is not defined
So, actually, it is looking for x in a's namespace, so if we do the following:
>>> a.x = 0
>>> a.f()
>>> a.x
1
>>> a.f()
>>> a.x
2
It works! So to put it as succinctly as possible:
>>> a.f.__globals__ is globals()
False
However!
Now that you are armed with this knowledge, do not go down this design path. Relying on changing global state across modules is a path to sorrow. Don't do it, it isn't recommended because generations of coders have found this is bad. Redesign your approach, and you will build more robust, easier to reason-about, and less buggy software.

Python scope: how to reach local variable? [duplicate]

It's ok to get and print the outer function variable a
def outer():
a = 1
def inner():
print a
It's also ok to get the outer function array a and append something
def outer():
a = []
def inner():
a.append(1)
print a
However, it caused some trouble when I tried to increase the integer:
def outer():
a = 1
def inner():
a += 1 #or a = a + 1
print a
>> UnboundLocalError: local variable 'a' referenced before assignment
Why does this happen and how can I achieve my goal (increase the integer)?
In Python 3 you can do this with the nonlocal keyword. Do nonlocal a at the beginning of inner to mark a as nonlocal.
In Python 2 it is not possible.
Workaround for Python 2:
def outer():
a = [1]
def inner():
a[0] += 1
print a[0]
A generally cleaner way to do this would be:
def outer():
a = 1
def inner(b):
b += 1
return b
a = inner(a)
Python allows a lot, but non-local variables can be generally considered as "dirty" (without going into details here).

python: How do I capture a variable declared in a non global ancestral outer scope?

Given:
def f():
x = 0
def g():
h()
def h():
x += 1
print(x)
g()
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in f
File "<stdin>", line 4, in g
File "<stdin>", line 6, in h
UnboundLocalError: local variable 'x' referenced before assignment
>>>
How can I make h see the x variable?
Thanks.
EDIT
Should have mentioned it earlier, I am using Python 2.7.3
You can make x a function attribute:
def f():
f.x = 0
def g():
h()
def h():
f.x += 1
print(f.x)
g()
Also, as of Python 3, you can use nonlocal keyword.
If you're using Python 3, you use the nonlocal keyword. Put nonlocal x at the beginning of function h. If you're using Python 2.x, a workaround is making x a list with one element, so you can modify it:
def f():
x = [0]
def g():
h()
def h():
x[0] += 1
print x[0]
g()
f()
In Python 3 just use nonlocal:
def f():
x = 0
def g():
h()
def h():
nonlocal x
x += 1
print(x)
g()
f()
Can't we put x as function arguments as workaround
def f():
x = 0
def g():
h(x)
def h(x):
x += 1
print(x)
g()
f()
Easiest is to use a dict or empty class, e.g.:
class Empty:
x = 0
def f():
closure1 = dict(x=0)
closure2 = Empty()
def g(): h(x)
def h(x):
closure1["x"] += 1
closure2.x += 1
g()
print closure1["x"], closure2.x
Although many good solutions were already provided, they have corner cases:
nonlocal, per Ashwini, is Python 3.x only
function attribute, per ovgolovin, will fail is f is redefined and later called by reference

Does Python have something like Perl 5.10's "state" variables?

In Perl 5.10, I can say:
sub foo () {
state $x = 1;
say $x++;
}
foo();
foo();
foo();
...and it will print out:
1
2
3
Does Python have something like this?
A class may be a better fit here (and is usually a better fit for anything involving "state"):
class Stateful(object):
def __init__(self):
self.state_var = 0
def __call__(self):
self.state_var = self.state_var + 1
print self.state_var
foo = Stateful()
foo()
foo()
The closest parallel is probably to attach values to the function itself.
def foo():
foo.bar = foo.bar + 1
foo.bar = 0
foo()
foo()
foo()
print foo.bar # prints 3
Python has generators which do something similar:
What does the "yield" keyword do in Python?
Not sure if this is what you're looking for, but python has generator functions that don't return a value per se, but a generator object that generates a new value everytime
def gen():
x = 10
while True:
yield x
x += 1
usage:
>>> a = gen()
>>> a.next()
10
>>> a.next()
11
>>> a.next()
12
>>> a.next()
13
>>>
look here for more explanation on yield:
What does the "yield" keyword do in Python?
Here's one way to implement a closure in python:
def outer():
a = [4]
def inner():
print a[0]
a[0] = a[0] + 1
return inner
fn = outer()
fn() # => 4
fn() # => 5
fn() # => 6
I borrowed this example verbatim from a python mailing list post.
Not that I'm recommending this, but just for fun:
def foo(var=[1]):
print var[0]
var[0] += 1
This works because of the way mutable default arguments work in Python.
Yes, though you have to declare your global variable first before it is encountered in foo:
x = 0
def foo():
global x
x += 1
print x
foo()
foo()
foo()
EDIT: In response to the comment, it's true that python has no static variables scoped within a function. Note that x in this example is only exposed as global to the rest of the module. For example, say the code above is in test.py. Now suppose you write the following module:
from test import foo
x = 100
foo()
foo()
The output will be only 1 and 2, not 101 and 102.
You could also use something like
def static_num2():
k = 0
while True:
k += 1
yield k
static = static_num2().next
for i in range(0,10) :
print static()
to avoid a global var. Lifted from this link about the same question.
The preferable way is to use class or generator (yield).
For the sake of completeness here's a variant w/ closure in Python 3.x:
>>> def make_foo():
... x = 1
... def foo():
... nonlocal x
... print(x)
... x += 1
... return foo
...
>>> foo = make_foo()
>>> foo()
1
>>> foo()
2
>>> foo()
3
>>> def foo():
x = 1
while True:
yield x
x += 1
>>> z = iter(foo())
>>> next(z)
1
>>> next(z)
2
>>> next(z)
3
Here's another dirty cheap way to do it, it's a variation on Tryiptich's answer, but using decorators
def static_var( name, value ):
def dec( function ):
setattr( function, name, value )
return function
return dec
#static_var( 'counter', 0 )
def counting_function():
counting_function.counter = counting_function.counter + 1
print counting_function.counter
"""
>>> counting_function()
1
>>> counting_function()
2
>>> counting_function()
3
>>> counting_function()
4
>>> counting_function()
5
>>>
"""

Categories