Need help with understanding the following sentence from PEP 227 and the Python Language Reference
If a variable is referenced in an enclosed scope, it is an error to
delete the name. The compiler will raise a SyntaxError for 'del
name'.
Lack of examples caused I couldn't reproduce an error at compile time, so an explanation with examples is highly desirable.
The following raises the execption:
def foo():
spam = 'eggs'
def bar():
print spam
del spam
because the spam variable is being used in the enclosed scope of bar:
>>> def foo():
... spam = 'eggs'
... def bar():
... print spam
... del spam
...
SyntaxError: can not delete variable 'spam' referenced in nested scope
Python detects that spam is being referenced in bar but does not assign anything to that variable, so it looks it up in the surrounding scope of foo. It is assigned there, making the del spam statement a syntax error.
This limitation was removed in Python 3.2; you are now responsible for not deleting nested variables yourself; you'll get a runtime error (NameError) instead:
>>> def foo():
... spam = 'eggs'
... def bar():
... print(spam)
... del spam
... bar()
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in foo
File "<stdin>", line 4, in bar
NameError: free variable 'spam' referenced before assignment in enclosing scope
An example can be this one:
>>> def outer():
... x = 0
... y = (x for i in range(10))
... del x
...
SyntaxError: can not delete variable 'x' referenced in nested scope
Basically it means you can't delete variables that are used in inner blocks(in that case the genexp).
Note that this apply for python <= 2.7.x and python < 3.2.
In python3.2 it's it does not raise syntax error:
>>> def outer():
... x = 0
... y = (x for i in range(10))
... del x
...
>>>
See this link for the whole story of the change.
I think the python3.2 semanthics is more correct because if you write the same code outside a function it works:
#python2.7
>>> x = 0
>>> y = (x for i in range(10))
>>> del x
>>> y.next() #this is what I'd expect: NameError at Runtime
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
NameError: global name 'x' is not defined
While putting the same code into a function, not only changes exception but the error is at compile time.
Related
In python, I know that functions run in the context that they are called from, not the context that they are defined in. However, in the code below, where a function is returned from another function, it still has access to the variables (specifically the list 'cache') of the function it was defined in, even though that function is now out of scope (I think), and so I would have though it's variables would have been deleted. But it can also access global variables from the current context?
def wrapper(func):
cache = []
def func_new(n):
cache.append(func(n))
print(cache)
return func_new
def add1(n):
return(n+1)
x = wrapper(add1)
x(5)
x(2)
print(cache)
It doesn't?
>>> x = wrapper(add1)
>>> x(5)
[6]
>>> x(2)
[6, 3]
>>> print(cache)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
print(cache)
NameError: name 'cache' is not defined
As for the cache variable being defined within the wrapper, that matches behavior elsewhere; the function has access to all variables where it is defined. The opposite is not true; variables defined in the function are not accessible in the context scope unless explicitly a global.
Does this example help?
>>> x = 4
>>> def test():
print(x)
>>> test()
4
>>> x = 1
>>> def f():
... print x
...
>>> f()
1
>>> x = 1
>>> def f():
... x = 3
... print x
...
>>> f()
3
>>> x
1
>>> x = 1
>>> def f():
... print x
... x = 5
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
>>> x = 1
>>> def f():
... global x
... print x
... x = 5
... print x
...
>>> f()
1
5
>>> x
5
How to treat the variable "x" inside the function as local without altering the global one when I have print statement above the variable assignment?
I expect the result of "x" to be 5 inside the function and the global x should be unaltered and remains the same in value (i.e) 1
I guess, there is no keyword called local in python contrary to global
>>> x = 1
>>> def f():
... print x
... global x
... x = 5
...
<stdin>:3: SyntaxWarning: name 'x' is used prior to global declaration
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.
Source.
It's true there's no local keyword in Python; instead, Python has this rule to decide which variables are local.
Any variable in your function is either local or global. It can't be local in one part of the function and global in another. If you have a local variable x, then the function can't access the global x. If you want a local variable while accessing the global x, you can call the local variable some other name.
The behaviour is already what you want. The presence of x = inside the function body makes x a local variable which entirely shadows the outer variable. You're merely trying to print it before you assign any value to it, which is causing an error. This would cause an error under any other circumstance too; you can't print what you didn't assign.
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.
What (if anything) is actually different between using a name in a nested function and passing a name to a nested function? If there's no difference, which is preferred by convention?
def foo(bar):
def put():
bar.append('World!')
print(', '.join(bar))
put()
foo(['Hello'])
versus
def foo(bar):
def put(bar):
bar += ['World!']
print(', '.join(bar))
put(bar)
foo(['Hello'])
Since you're only talking about nested functions, and not closures, unless you have a specific reason to have it be nested, I would recommend defining each function in the module scope:
def put(bar):
bar += ['World!']
print(', '.join(bar))
def foo(bar):
put(bar)
foo(['Hello'])
If for some reason you do need it to be nested, it's better to pass in bar explicitly. However, if you do think you need it to be nested, ask about your reasoning first; there's a good chance that you don't. :)
The difference is that in the first one, bar variable is in a scope of the parent function, it can be used in the child function , unless you do assignment on it (This would be the case something similar to using global variables in function) . Example -
>>> def foo(bar):
... def put():
... bar = bar + ['World']
... print(', '.join(bar))
... put()
...
>>>
>>> foo(['Hello'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in foo
File "<stdin>", line 3, in put
UnboundLocalError: local variable 'bar' referenced before assignment
In this case if you want to use the bar and assign to it as well, you need to use the nonlocal keyword , Example -
>>> def foo(bar):
... def put():
... nonlocal bar
... bar = bar + ['World!']
... print(', '.join(bar))
... put()
...
>>> foo(['Hello'])
Hello, World!
Whereas in the second one, bar is a local variable to the put() function (because its an argument to it) and can be assigned without the above UnboundLocalError , Example -
>>> def foo(bar):
... def put(bar):
... bar = bar + ['World!']
... print(', '.join(bar))
... put(bar)
...
>>>
>>> foo(['Hello'])
Hello, World!
I would prefer explicitly passing the required arguments as done in the second case.
I was playing around with closures in Python and I do not understand why the following does not work and how to make it work:
>>> def make_counter():
... i = 0
... def inner():
... i += 1
... return i
... return inner
...
>>> c = make_counter()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'i' referenced before assignment
Could anybody shed some light on it?
Thanks.
In the inner function, the statement
i += 1
can be understood like this
i = i + 1
since you are assigning a value to i in this function, Python thinks that you are creating a new variable scoped to this current function. And then when it executes the right hand side expression, i + 1, it finds that i has not been assigned any value before using it. That is why it is throwing
UnboundLocalError: local variable 'i' referenced before assignment
To fix this, you need to explicitly tell Python that you are not creating a new variable, instead, you are accessing a variable from the outer scope, with nonlocal (Python 3.x) like this
>>> def make_counter():
... i = 0
... def inner():
... nonlocal i
... i += 1
... return i
... return inner
...
>>>
>>> make_counter()()
1
Note: If you are using Python 2.x, follow any of the methods mentioned in this question