A=[]
def main():
global A
A=[1,2,3,4,5]
b()
def b():
if(len(A)>0):
A=[7,8,9]
else:
if(A[3]==4):
A.remove(2)
main()
This code gives error in line A.remove(2) giving reason:"UnboundLocalError: local variable 'A' referenced before assignment"
but A list is global and for sure it has been initialized in main() function.Please explain why this is giving error?
As A has been initialized again in b function, will this cause error?
The reason you are getting this is because when you performed this assignment in your function:
A = [7, 8, 9]
The interpreter will now see A as a locally bound variable. So, what will happen now, looking at this condition:
if(len(A)>0):
Will actually throw your first UnboundLocalError exception, because, due to the fact, as mentioned, you never declared global A in your b function and you also created a locally bound variable A = [7, 8, 9], you are trying to use a local A before you ever declared it.
You actually face the same issue when you try to do this:
A.remove(2)
To solve this problem with respect to your code, simply declare the global A back in your b() function.
def b():
global A
if(len(A)>0):
A=[7,8,9]
else:
if(A[3]==4):
A.remove(2)
However, the better, ideal and recommended way to do this is to not use global and just pass the arguments to your function
A = [1, 2, 3, 4]
def main(list_of_things):
# do whatever operations you want to do here
b(list_of_things)
def b(list_of_things):
# do things with list_of_things
main(A)
You must declare a variable global in any function that assigns to it.
def b():
global A
if some_condition:
A.append(6)
else:
A.remove(2)
Without declaring A global within the scope of b(), Python assumes that A belongs in the b() namespace. You can read it, but cannot edit it (any changes made will not persist to the true A.
You're allowed to set A to something inside the function A = ... will work, but only within the scope of the function.
If you try to mutate A inside of the function without defining A in your function's namespace, then Python will tell you that you are trying to mutate a variable outside of your purview with UnboundLocalError
By declaring A global, the interpreter knows that you mean to edit the global variable.
I'm assuming that you meant for your code to look something like this (Otherwise, it runs fine)
A=[]
if __name__ == '__main__':
def main():
global A
A=[1,2,3,4,5]
b()
def b():
global A
if some_condition:
A=[7,8,9]
else:
A.remove(2)
print A
main()
print A
print A
Related
I was just playing around with Python and I came across something interesting which I didn't quite understand. The code goes as follows:
a = 1
def function():
print(a)
function()
print(a)
Here, a is a global variable and I used it in my function and the result was:
1
1
I was able to use a global variable locally in my function without having to use global a in my function.
Then, I experimented further with this:
a = 1
def function():
a = a+1
print(a)
function()
print(a)
When I ran this code, an error showed up and it said that the local variable a was referenced before assignment. I don't understand how before it recognized that a was a global variable without global a
but now I need global a like this
a = 1
def function():
global a
a = a+1
print(a)
function()
print(a)
in order for this code to work. Can someone explain this discrepancy?
You can read the value from a global variable anytime, but the global keyword allows you to change its value.
This is because when you try and set the a variable in your function, by default it will create a new local function variable named a. In order to tell python you want to update the global variable instead, you need to use the global keyword.
When creating "=" ,a new variable inside a function python does not check if that variable is a global variable, it treats that new variable as a new local variable, and since you are assigning it to itself and it does not exist locally yet, it then triggers the error
I understand that variables in python are always (?) available in subfunctions.
That is why this works:
def abc():
print(a)
def main():
abc()
pass
if __name__=='__main__':
a = 'a'
main()
However, why does it not work if the definition of ais moved to main()?
def abc():
print(a)
def main():
a = 'a'
abc()
pass
if __name__=='__main__':
main()
It is still defined before it is used and should be available in abc(), right?
No, variables defined in a function are scoped to the function by default. To make it available in abc() you would need to make it global:
def main():
global a
a = 'a'
abc()
As for the 'shadowing' warning, you get it when you redefine names at inner scopes. For example, this code creates a global variable a:
if __name__ == '__main__':
a = 'a'
main()
Now if you do a = 'b' inside main() you are effectively 'shadowing' a from the outer scope by creating a local variable with the same name.
In the first code sample, 'a' is defined as a global variable. Meaning that it can be accessed by any function called after it is instantiated.
In the second code sample, 'a' is defined as a local variable. Meaning that it only exists within the "main" function. If you wanted to pass it into the "abc" function, you would have to explicitly pass that variable. That would look like:
def abc(a):
print(a)
def main():
a = 'a'
abc(a)
pass
if __name__=='__main__':
main()
Variables at the module level of your function are available to read anywhere else in the module. When you do
if __name__=='__main__':
a = 'a'
you are defining a as a module-level variable, hence why you can read it inside abc(). This is similar to why you can see your imports within other functions.
When you define a inside abc(), it's no longer at the module-level and no longer is implicitly readable elsewhere in the module.
As an aside, global variables are usually a bad idea, it's better to pass them around explicitly. See http://wiki.c2.com/?GlobalVariablesAreBad for a good summary of why.
When I compile this snippet with func1 I get an error about referencing output before assignment, which seems reasonable to me:
def func1():
output += 1
output = 0
func1()
print(output)
But when I compile this snippet with func2 I don't get an error, which seems unreasonable to me.
def func2():
output.append(1)
output = []
func2()
print(output)
Thoughts? Thanks in advance and sorry if this is a duplicate. I didn't didn't see this particular question addressed in similarly titled posts when researching.
The problem here is in how Python binds names to variables.
Any time you write an assignment in a function (something like a = b or a += b), Python will bind that name locally for the entire function scope. This means that any variables with that name declared outside the function are ignored.
For example:
a = 1 # This variable is ignored.
def foo():
print(a) # "a" hasn't been defined yet!
a = 2 # This causes "a" to bind to a local variable.
This will produce an UnboundLocalError at the print statement because Python sees that a is assigned to later in the function, and binds it locally, ignoring the variable you defined outside the function. Since a is only defined after the print statement, you get an error.
This can be very confusing because removing the a += 2 line will cause the print statement to work as expected!
The solution is to explicitly tell Python not to bind the name locally. This can be done with the global and nonlocal keywords, e.g.:
a = 1
def foo():
nonlocal a # or: global a
print(a)
a += 2
global tells Python to bind the name in the module's global scope, while nonlocal (introduced in Python 3) tells Python to bind to the enclosing scope (for example, if you define a function inside a function).
There's a nice explanation of this (with examples) in the documentation :-)
Once we understand these rules, we can see that your first example fails because you're trying to increment a variable that doesn't yet exist. output += 1 is equivalent to output = output + 1, which triggers local name binding.
On the other hand, your second example doesn't trigger the error because you're not assigning to output (you're mutating it instead), so it will bind to the global variable that you defined.
In Python, global variables when called in functions don't work all the same.
The data types (on a high level) in Python can be categorised into "mutable" and "immutable".
If you have have mutable datatypes like list then they can be both : accessed and modified in the function without referring them with a global keyword.
Eg:
l = []
def foo():
l.append(1) #works
foo()
print(l) # prints [1], so foo() changed the global list l.
But if you have immutable types like int or str then they can be accessed but not modified.
Eg:
someString = "abcdef"
def foo():
print(someString[2]) # prints "c"
someString += "l" # error as someString cannot be modified unless you include it in the function foo() with the global keyword as shown below.
But to modify a global variable of immutable types like str you have to include it first with the global keyword then you're free to use it like any local variable.
Eg:
someString = "abcdef"
def foo():
global someString # This now includes someString in foo() scope and is allowed to be modified.
print(someString[2]) # prints "c"
someString += "l" # works as expected
print(someString) # prints "abcdefl"
Thanks to Mark Tolonen for this valuable point. At the end, you cannot reassign a global variable without the global keyword, irrespective of whether the variable is mutable or immutable.
Eg:
someInt = 1
someStr = "abc"
someList = [1,2,3]
def foo():
someInt += 3 # error, local variable referenced before assignment.
someStr += "def" # error, local variable referenced before assignment.
someList += [4] # error, local variable referenced before assignment. Note that this is **not** the same as doing someList.append(4).
To make the above work, include them in the function via global keyword and use them as required.
I don't quite understand why the code
def f():
print(s)
s = "foo"
f()
runs perfectly fine but
def f():
print(s)
s = "bar"
s = "foo"
f()
gives me UnboundLocalError. I know that I can fix this by declaring s as a global variable inside the function or by simply passing s an an argument into the function.
Still I don't understand how python seemingly knows whether or not s is referenced inside the function before the line has been executed? Does python make some sort of list of all local variable references when the function is read into the global frame?
Other answers have focused on the practical aspects of this but have not actually answered the question you asked.
Yes, the Python compiler tracks which variables are assigned when it is compiling a code block (such as in a def). If a name is assigned to in a block, the compiler marks it as local.Take a look at function.__code__.co_varnames to see which variables the compiler has identified.
The nonlocal and global statements can override this.
Yes, Python will look-ahead to recover all variables declared in the local scope. These will then overshadow global variables.
So in your code:
def f():
print(s)
s = "foo"
f()
Python did not find s in the local scope, so it tries to recover it from the global scope and finds "foo".
Now in the other case the following happens:
def f():
print(s)
s = "bar
s = "foo"
f()
Python knows that s is a local variable because it did a look-ahead before runtime, but at runtime it was not assigned yet so it raised and exception.
Note that Python will even let you reference variables that have not been declared anywhere. If you do:
def foo():
return x
f()
You will get a NameError, because Python, when not finding, x as a local variable will just remember that at runtime it should look for a global variable named x and then fail if it does not exist.
So UnboundLocalError means that the variable may eventually be declared in scope but has not been yet. On the other hand NameError means that the variable will never be declared in the local scope, so Python tried to find it in the global scope, but it did not exist.
I want to define a Python function doing:
1. Check if a variable already exist.
2. If not, create it as a global variable (because I want to use it outside the function).
def foo():
try:
x
except NameError:
global x
x = 1
else:
pass
foo()
print(x)
Then there is an error:
SyntaxError: name 'x' is used prior to global declaration
How to solve this? Thank you :)
Something like this could work.
def foo():
if not('x' in locals()):
global x
x = 1
foo()
print(x)
just check if the x variable exists.
you could also check if a variable is declared as global
if not('x' in globals()):
Just declaring a variable as global at the top of your function won't create it - so this form works:
def foo():
global x
try:
x
except NameError:
x = 1
The key thing to understand here is that when Python compiles a function, it "bakes" each variable inside the function as either a local, nonlocal, or a global (or builtin) variable - any access to that variable will have either one or the other relevant bytecode. The "global" declaration thus affects the whole function, regardless of were it is - and since tryig to access a variable before the global statement would look ambiguous, the error you saw is forced.
But since you are at it - are you sure you want to do it?
global variables are good for having values that can be shared in functions in a module - but then, even if they are to be initialized in the call to an specific function, they should be declared in the module body, and properly documented. If you can't have the final value at module load time, just assign it to None.