This code throws NameError: name 'x' is not defined":
def function():
global x
print(x)
function()
print(x)
However, this code works and prints "2" two times:
def function():
global x
x = 2
print(x)
function()
print(x)
My question is, what actually happens at the moment x is declared as global?
You have not defined x, resulting in the errors. Try and make the function have a parameter of x.
def function(x):
print(x)
Also, when you globalize a variable, its scope is not just within the function, but in every function/class.
Carden
Related
My problem is simple.
This code,
def first():
x = "hello"
def second():
global x
x = "goodbye"
second()
first()
does not work, since globalizing a variable declared inside a function isn't valid. If it was just one function I know it would, but how could I get this to work like this?
Problem :
The problem is there are two different x variables getting confused. The x in second() is global and can be printed out after running after running your code. The problem is the first x is considered a local in first(). You can check it on your own by copy-pasting the following code in different places to understand how the code considers the variable:
if 'x' in locals():
print("local")
elif 'x' in globals():
print("global")
else:
print("unedfined")
Solution 1:
Always having x as a global by adding global x right on the line after def first() (and keeping it after def second())
Solution 2:
Accepting not having globals. Instead you can return x at the end of your functions and assigning an other variable (that could have the same name) to the returned value of the function eg.x = second(). Then using that variable as an argument eg. def first(x):
Hope it helps.
You need to declare a variable as global before you write to it in each of the functions. Like so:
def first():
global x
x = "hello"
def second():
global x
x = "goodbye"
second()
first()
print(x)
# >>> goodbye
I'd recommend a different structure to your project though
What I understood, you can initialize x variable before the function and then assign value to it when you want like this:
x = ''
def first():
x = "hello"
def second():
x = "goodbye"
second()
first()
Code extract is below, I am trying to modify a variable in a nested function, I started out by practising modifying in a normal function and using the keyword 'global', however in a nested function I can only make it work by putting in global (highlighted in comment), but by doing this I am expanding the scope of the 'y' var outside the function bar, to the main program which is not what I want.
I only want the inner function foo to be able to see and modify 'y' but I do not want to make 'y' available to the main program.
Note I have already seen this post Using a global variable inside a function nested in a function in Python, however the question remains.
Thank you.
# accessing x outside scope is okay
x = 5
def bar():
print(x)
bar()
# modifying x outside scope needs global
x = 5
def bar():
global x
x = x+5
print(x)
# now original reference has been changed as well.
bar()
print(x)
# scope within nested functions
def bar():
global y ## why is this needed?
y = 5
print(f'y before foo called (): {y}')
def foo():
global y
y = y + 1
print(f'y in foo called (): {y}')
foo()
print(f'y after foo called (): {y}')
bar()
print(f'y outside function foo called (): {y}')
Your solution works because global y makes y global. It is now available in all functions, including in nested functions. Instead, you probably want to declare nonlocal y inside foo() and remove all the global y declarations. This allows you to assign a value to y in the nested scope without exposing it to other functions.
I am using Python 3.7.0 and I am doing some experiments in order to comprehend the subtleties of variable scopes but I still don't understand this behaviour.
When I execute this code:
def f():
x=0
def g():y=x+1;x=y
return g
f()()
I get UnboundLocalError: local variable 'x' referenced before assignment
However when I execute this one:
def f():
x=0
def g():y=x+1
return g
f()()
It works fine. And even this one:
def f():
x=0
def g():x=1
return g
f()()
It also works fine. So I am confused. It seems to me that if assigning the value 1 to the nonlocal variable x in the g function works fine alone and on the other hand if assigning an expression containing x to a local variable y in the function g works also fine then the instructions y=x+1 and x=y should both work. I don't understand what is causing the error. I feel I am missing something fundamental in my understanding of Python's behaviour.
I'll take an ill-advised crack at this. In the following code:
def f():
x = 0 # assignment makes this x local to f()
def g():
y = x + 1
x = y # assignment makes this x local to g()
return g
f()()
The assignment of x inside g() forces it to be a variable local to g(). So the first line causes the error as you accessing the value of an unassigned local. I believe this "assignment makes it local" logic doesn't follow the flow of your program -- the function is viewed as whole to make this determination and then the code is looked at line by line.
You can access any variable in scope from a function, but you can only set locals. Unless you've declared the variable you're assigning as global or nonlocal:
def f():
x = 0 # assignment makes this x local to f()
def g():
nonlocal x
y = x + 1
x = y # x is declared to come from a nonlocal scope
return g
f()()
In your second example, it's not an issue as you're only looking at the value of x, not setting it, so it can come from any appropriate scope:
def f():
x = 0 # assignment makes this x local to f()
def g():
y = x + 1 # x comes from an outer scope
return g
f()()
Finally, your third example is the same as the first without the unssigned local variable usage error:
def f():
x = 0 # assignment makes this x local to f()
def g():
x = 1 # assignment makes this x local to g()
return g
f()()
It seems to me that if assigning the value 1 to the nonlocal variable
x in the g function works fine alone
How did you determine that the reassignment of x in f() worked fine?
def name():
global x
x = "Hello"
print(x + " Guys!")
print(x)
print(name())
the output is
NameError: name 'x' is not defined
Why is this happening? This is weird because I placed the (global) function.
The first time print(x) is called outside the function, the function not having been run yet, x is not defined. Once you run name(), then you can print(x) whenever you like.
Consider the following python snippet (I am running Python 3)
name = "Sammy"
def greet():
name = 'johny'
def hello():
print('hello ' + name) # gets 'name' from the enclosing 'greet'
hello()
greet()
This produces the output hello johny as expected
However,
x = 50
def func1():
x = 20
def func2():
print("x is ", x) # Generates error here
x = 2
print("Changed the local x to ",x)
func2()
func1()
print("x is still ",x)
generates an UnboundLocalError: local variable 'x' referenced before assignment.
Why does the first snippet work, whereas the second doesn't?
The error is actually caused (indirectly) by the following line, i.e. x = 2. Try commenting out that line and you'll see that the function works.
The fact that there is an assignment to a variable named x makes x local to the function at compile time, however, at execution time the first reference to x fails because, at the time that the print() statement is executed, it does not exist yet in the current scope.
Correct it by using nonlocal in func2():
def func2():
nonlocal x
print("x is ", x)
x = 2
print("Changed the local x to ",x)
The reason that the first function (greet()) works is because it's OK to read variables in an outer scope, however, you can not assign to them unless you specify that the variable exists in an outer scope (with nonlocal or global).
You can assign to a variable of the same name, however, that would create a new local variable, not the variable in the outer scope. So this also works:
def func1():
x = 20
def func2():
x = 2
print('Inner x is', x)
func2()
print('Outer x is', x)
Here x is assigned to before being referenced. This creates a new variable named x in the scope of function func2() which shadows the x defined in func1().
In a given scope, you can only reference a variable's name from one given scope. Your variable cannot be global at some point and local later on or vice versa.
For that reason, if x is ever to be declared in a scope, Python will assume that you are refering to the local variable everywhere in that scope, unless you explicitly state otherwise.
This is why you first function, greet, works. The variable name is unambiguously coming from the closure. Although, in func2 the variable x is used in the scope and thus you cannot reference the x from the closure unless explicitly stating otherwise with nonlocal.
The following errors might enlighten us on this.
A variable cannot become global after use
def func1():
x = 20
def func2():
print("x is ", x)
global x
print("Changed the local x to ",x)
func2()
This raises a SyntaxError: name 'x' is used prior to global declaration. This means that the closure's x cannot be used and then the global one.
A global variable cannot become local
Here is another case using global at the top of func2.
def func1():
x = 20
def func2():
global x
print("x is ", x)
x = 2
print("Changed the local x to ",x)
func2()
This code was exectued without error, but notice that the assignment to x updated the global variable, it did not make x become local again.