What is the scope of a function defined outside of a class? - python

class player(object):
def __init__(self, a, b):
self.a = a
self.b = b
def foo():
x += 2
obj.a += 10
obj = player(0,0)
x = 4
foo()
I understand that my foo function is not able to assign a value to the local variable x since it was previously defined globally outside the foo scope. Yet the same problem won't occur for a variable a which is an attribute of the instance obj from the player class, which in turn has nothing to do with the function foo().
Apart from the error with x += 2, I would expect an error in obj.a, i.e., I think foo() should not be able to modify obj.a, but it does! It should have thrown the same error as for x += 2 perhaps, no?

All augmented assignments effectively desugar to one of three method calls, but only in one case is an actual assignment performed.
If the lefthand side is a name, then x += y desugars to x = x.__iadd__(y). Because this is an ordinary assignment statement, the parser makes x a local variable (unless preceded by a global x or nonlocal x). Regardless of scope, x must already have a value so that the righthand side can be evaluated. In other words, x is not a free variable.
If the lefthand side is an indexed name, then x[i] += y desugars to x.__setitem__(i, x[i] + y). If x is not already defined as a local variable, it is a free variable, and so will resolve to the value
of x in the first enclosing scope.
If the lefthand side is an attribute lookup, then x.a = y desugars to
x.__setattr__('a', x.a + y). Again, x is a free variable unless a local variable x already exists.

Your comparison is apples to oranges. An example which shows you the behavior you're expecting would be:
class player(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self, value):
self.a += value
def foo():
obj += 10
obj = player(0,0)
x = 4
foo()
Python permits you to read global variables inside of foo, but not redefine them. The statement obj.a += 10 only reads the variable of obj from the global scope (even though it sets a value on the instance of obj) so it is allowed. x += 2 attempts to redefine the value of x which is a global, so it is not allowed. This foo will work because we are only reading x but not redefining it:
def foo():
obj.a += 10
y = x + 10

Actually, you can't call x += 2 because x is not yet defined in your code. You can read x as a global var, or you can change it's value using global.
If you run your code, you'll get the error:
UnboundLocalError: local variable 'x' referenced before assignment
What it is saying to you is: I know there is a global x variable in your code, but you can't change it here. But, if you wish, you can create a local variable here named x, and then change it's value. Or, if you really want to modify the value from the global variable, use the global keyword, so I'll know exactly what you want to do.
Declaring it locally:
def foo():
x = 0
x += 2
obj.a += 10
obj = player(0,0)
x = 4
foo()
With global:
def foo():
global x
x += 2
obj.a += 10
obj = player(0,0)
x = 4
foo()
Now, why x.a += 10 works?
Because you are not changing the x object at all.
As mentioned by John in his answer, the same error would occur if you were changing the object itself, not its properties.
if you try to run the code below:
def foo():
obj += 10
obj = player(0,0)
x = 4
foo()
You'll get the same error as before:
UnboundLocalError: local variable 'obj' referenced before assignment

a is an attribute of player instance while x is a variable or simply value. When you are modifying x in the function, it is in local scope and results in NameError. But obj is an object in global scope and when you modify a, you are simply updating its attribute, you won't get any error here.

Without using one of the keywords global or nonlocal, a function can read but not assign a new value to a variable outside of the function's own local scope. Let's consider these two cases:
For x += 2, this is an attempt to assign a new value to the global variable x, so it is not allowed.
For obj.a += 10, it is the variable obj which is global; obj.a is an object attribute, not a "variable" in this sense. The way obj.a += 10 is evaluated is something like this: first look up the object referenced by the variable obj, and then add 10 to the attribute named a belonging to that object. Since this only reads the global variable obj and does not attempt to assign a new value to obj, it is allowed.

Related

Inconsistent scope in python

I have tried creating a function like this:
n = ["one", "two"]
result=""
def join_strings(words):
for word in words:
result+=word
return result
print (join_strings(n))
This does not work and outputs:
UnboundLocalError: local variable 'result' referenced before assignment
The result variable has to be inside the function for it to work.
However, when I have this simple function:
x=3
def price():
return (x+3)
print (price())
This works and prints the price, even though x is outside of the function. Why is this happening?
Actually there is no inconsistency as the examples you gave are different from each other. It would still fail in the second function if you have tried to assign x to itself, like:
>>> x = 3
>>> def price():
... x +=3
... return x
...
>>> price()
UnboundLocalError: local variable 'x' referenced before assignment
Instead of assigning back to x if you choose another name, it would run with no problem:
>>> def price():
... y = x + 3
... return y
...
>>> price()
6
But, why it happens?
It's because Python's scoping rules. You can read the value of a variable outside of function but you can't change it**. When you do x += 3, which is same as x = x + 3 for integers, that means "I have a variable x that I have write access to it in the current scope." You don't have such variable, thus as the error says: you are referencing a "local variable" before assignment.
Is there a way to modify them in the function?
Yes. You can use global keyword, changes will be applied to your global variable:
>>> x = 3
>>> def price():
... global x
... x += 3
... return x
...
>>> x
3
>>> price()
6
>>> x
6
** By changing, I mean assigning something else to it so it's id will change.
The difference is that in the second example you aren't trying to modify or reassign x. (You'll get the same error if you say something like x += 3 in the price function.) Once you use an assignment operator, you're binding a new value to that name, shadowing the outer scope.
If you want to be able to modify something from an outer scope, put it inside a mutable container (like a list); you can then modify the contents without reassigning that variable.
result = [""]
def join_strings(words):
for word in words:
result[0] += word
return result[0]
If you just want to be able to reference the result value, without modifying it, that's fine too, but you have to assign it to a new variable then:
result = ""
def join_strings(words):
ret = result
for word in words:
ret += word
return ret

UnboundLocalError, - reference before assignment [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 5 months ago.
I know I should avoid using global variables in the first place due to confusion like this, but if I were to use them, is the following a valid way to go about using them? (I am trying to call the global copy of a variable created in a separate function.)
x = "somevalue"
def func_A ():
global x
# Do things to x
return x
def func_B():
x = func_A()
# Do things
return x
func_A()
func_B()
Does the x that the second function uses have the same value of the global copy of x that func_a uses and modifies? When calling the functions after definition, does order matter?
If you want to simply access a global variable you just use its name. However to change its value you need to use the global keyword.
E.g.
global someVar
someVar = 55
This would change the value of the global variable to 55. Otherwise it would just assign 55 to a local variable.
The order of function definition listings doesn't matter (assuming they don't refer to each other in some way), the order they are called does.
Within a Python scope, any assignment to a variable not already declared within that scope creates a new local variable unless that variable is declared earlier in the function as referring to a globally scoped variable with the keyword global.
Let's look at a modified version of your pseudocode to see what happens:
# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'
def func_A():
# The below declaration lets the function know that we
# mean the global 'x' when we refer to that variable, not
# any local one
global x
x = 'A'
return x
def func_B():
# Here, we are somewhat mislead. We're actually involving two different
# variables named 'x'. One is local to func_B, the other is global.
# By calling func_A(), we do two things: we're reassigning the value
# of the GLOBAL x as part of func_A, and then taking that same value
# since it's returned by func_A, and assigning it to a LOCAL variable
# named 'x'.
x = func_A() # look at this as: x_local = func_A()
# Here, we're assigning the value of 'B' to the LOCAL x.
x = 'B' # look at this as: x_local = 'B'
return x # look at this as: return x_local
In fact, you could rewrite all of func_B with the variable named x_local and it would work identically.
The order matters only as far as the order in which your functions do operations that change the value of the global x. Thus in our example, order doesn't matter, since func_B calls func_A. In this example, order does matter:
def a():
global foo
foo = 'A'
def b():
global foo
foo = 'B'
b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.
Note that global is only required to modify global objects. You can still access them from within a function without declaring global.
Thus, we have:
x = 5
def access_only():
return x
# This returns whatever the global value of 'x' is
def modify():
global x
x = 'modified'
return x
# This function makes the global 'x' equal to 'modified', and then returns that value
def create_locally():
x = 'local!'
return x
# This function creates a new local variable named 'x', and sets it as 'local',
# and returns that. The global 'x' is untouched.
Note the difference between create_locally and access_only -- access_only is accessing the global x despite not calling global, and even though create_locally doesn't use global either, it creates a local copy since it's assigning a value.
The confusion here is why you shouldn't use global variables.
You can directly access a global variable inside a function. If you want to change the value of that global variable, use "global variable_name". See the following example:
var = 1
def global_var_change():
global var
var = "value changed"
global_var_change() #call the function for changes
print var
Generally speaking, this is not a good programming practice. By breaking namespace logic, code can become difficult to understand and debug.
As others have noted, you need to declare a variable global in a function when you want that function to be able to modify the global variable. If you only want to access it, then you don't need global.
To go into a bit more detail on that, what "modify" means is this: if you want to re-bind the global name so it points to a different object, the name must be declared global in the function.
Many operations that modify (mutate) an object do not re-bind the global name to point to a different object, and so they are all valid without declaring the name global in the function.
d = {}
l = []
o = type("object", (object,), {})()
def valid(): # these are all valid without declaring any names global!
d[0] = 1 # changes what's in d, but d still points to the same object
d[0] += 1 # ditto
d.clear() # ditto! d is now empty but it`s still the same object!
l.append(0) # l is still the same list but has an additional member
o.test = 1 # creating new attribute on o, but o is still the same object
Here is one case that caught me out, using a global as a default value of a parameter.
globVar = None # initialize value of global variable
def func(param = globVar): # use globVar as default value for param
print 'param =', param, 'globVar =', globVar # display values
def test():
global globVar
globVar = 42 # change value of global
func()
test()
=========
output: param = None, globVar = 42
I had expected param to have a value of 42. Surprise. Python 2.7 evaluated the value of globVar when it first parsed the function func. Changing the value of globVar did not affect the default value assigned to param. Delaying the evaluation, as in the following, worked as I needed it to.
def func(param = eval('globVar')): # this seems to work
print 'param =', param, 'globVar =', globVar # display values
Or, if you want to be safe,
def func(param = None)):
if param == None:
param = globVar
print 'param =', param, 'globVar =', globVar # display values
You must use the global declaration when you wish to alter the value assigned to a global variable.
You do not need it to read from a global variable. Note that calling a method on an object (even if it alters the data within that object) does not alter the value of the variable holding that object (absent reflective magic).

python: name resolution clarification?

I'm reading the python reference name resolution, which reads
A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.
Based on that, I would expect the following code
x = 10
def f():
x = 5
class Test:
y = x
return Test
print(f().y)
to print 10, however it prints 5. is this a mistake in the reference, or am I misunderstanding something?
In this case, 'normal' rules apply:
x = 'global'
def f():
x = 'defined in f'
class Test:
print(x) # not local, normal rules apply
f()
# defined in f
In this second case, we would expect an UnboundLocalError: local variable 'x' referenced before assignment if we were inside a function:
x = 'global'
def f():
x = 'defined in f'
class Test:
print(x) # unbound local at this time
x = 'assigned in Test'
print(x)
But for the first print(x), x will be taken from the global namespace:
f()
# global
# assigned in Test
I think #khelwood gave the answer. The value in the variable:
Test.y
is an integer that you define, but you never give the x = 10 to the function.
Maybe what you want is actually:
x = 10
def f(x=5):
class Test:
y = x
return Test
print(f().y) #print default 5
print(f(x).y)
The last line print 10 since x is given to the function, and therefore the class set y as x
You can see the exception by wrapping f in another function that defines a local variable x:
x = 10
def g():
x = 7
def f():
class Test:
y = x
return Test
return f()
print(g().y)
Now the output is 7, as the lookup of x in the class statement bypasses the local scope of g to find the global value of x.
In your code, because class establishes a new namespace, but not a new scope, the value of x is taken from the current local scope, that of f.

Python 3 syntax should work, but doesn't

It's so basic it should work. I want a function that adds a value to something
There must be something I don't know about python 3, so here we go.
x = 0
def foo(x=x): ##### with out x=x there is a error
x = x + 1 # option one
x = 1 # option two
# when we run it
foo()
print(x)
# it returns 0, it should return 1
x is a local variable in foo(); Assigning x as a default value for a keyword argument won't make it any less a local.
If you wanted it to be a global, mark it as such:
x = 0
def foo():
global x
x = x + 1
print(x)
foo()
print(x)
but you probably just wanted to pass in the value as an argument instead:
def foo(value):
return value + 1
x = 0
print(x)
x = foo(x)
print(x)
This is basically and example of scoping rules. The variable x within foo is local to foo, so nothing that happens to the local x changes anything outside foo, including the global x with which is actually a different variable. When the interpreter exits foo the global x comes back into scope and it hasn't changed from its initial value of 0. The function header foo(x=x) defines a local x whose default value is the global x. The interpreter allows it but it's generally considered bad programming practice to have the same variable name representing two variables because it leads to this kind of confusion.

Python function global variables? [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 6 months ago.
I know I should avoid using global variables in the first place due to confusion like this, but if I were to use them, is the following a valid way to go about using them? (I am trying to call the global copy of a variable created in a separate function.)
x = "somevalue"
def func_A ():
global x
# Do things to x
return x
def func_B():
x = func_A()
# Do things
return x
func_A()
func_B()
Does the x that the second function uses have the same value of the global copy of x that func_a uses and modifies? When calling the functions after definition, does order matter?
If you want to simply access a global variable you just use its name. However to change its value you need to use the global keyword.
E.g.
global someVar
someVar = 55
This would change the value of the global variable to 55. Otherwise it would just assign 55 to a local variable.
The order of function definition listings doesn't matter (assuming they don't refer to each other in some way), the order they are called does.
Within a Python scope, any assignment to a variable not already declared within that scope creates a new local variable unless that variable is declared earlier in the function as referring to a globally scoped variable with the keyword global.
Let's look at a modified version of your pseudocode to see what happens:
# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'
def func_A():
# The below declaration lets the function know that we
# mean the global 'x' when we refer to that variable, not
# any local one
global x
x = 'A'
return x
def func_B():
# Here, we are somewhat mislead. We're actually involving two different
# variables named 'x'. One is local to func_B, the other is global.
# By calling func_A(), we do two things: we're reassigning the value
# of the GLOBAL x as part of func_A, and then taking that same value
# since it's returned by func_A, and assigning it to a LOCAL variable
# named 'x'.
x = func_A() # look at this as: x_local = func_A()
# Here, we're assigning the value of 'B' to the LOCAL x.
x = 'B' # look at this as: x_local = 'B'
return x # look at this as: return x_local
In fact, you could rewrite all of func_B with the variable named x_local and it would work identically.
The order matters only as far as the order in which your functions do operations that change the value of the global x. Thus in our example, order doesn't matter, since func_B calls func_A. In this example, order does matter:
def a():
global foo
foo = 'A'
def b():
global foo
foo = 'B'
b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.
Note that global is only required to modify global objects. You can still access them from within a function without declaring global.
Thus, we have:
x = 5
def access_only():
return x
# This returns whatever the global value of 'x' is
def modify():
global x
x = 'modified'
return x
# This function makes the global 'x' equal to 'modified', and then returns that value
def create_locally():
x = 'local!'
return x
# This function creates a new local variable named 'x', and sets it as 'local',
# and returns that. The global 'x' is untouched.
Note the difference between create_locally and access_only -- access_only is accessing the global x despite not calling global, and even though create_locally doesn't use global either, it creates a local copy since it's assigning a value.
The confusion here is why you shouldn't use global variables.
You can directly access a global variable inside a function. If you want to change the value of that global variable, use "global variable_name". See the following example:
var = 1
def global_var_change():
global var
var = "value changed"
global_var_change() #call the function for changes
print var
Generally speaking, this is not a good programming practice. By breaking namespace logic, code can become difficult to understand and debug.
As others have noted, you need to declare a variable global in a function when you want that function to be able to modify the global variable. If you only want to access it, then you don't need global.
To go into a bit more detail on that, what "modify" means is this: if you want to re-bind the global name so it points to a different object, the name must be declared global in the function.
Many operations that modify (mutate) an object do not re-bind the global name to point to a different object, and so they are all valid without declaring the name global in the function.
d = {}
l = []
o = type("object", (object,), {})()
def valid(): # these are all valid without declaring any names global!
d[0] = 1 # changes what's in d, but d still points to the same object
d[0] += 1 # ditto
d.clear() # ditto! d is now empty but it`s still the same object!
l.append(0) # l is still the same list but has an additional member
o.test = 1 # creating new attribute on o, but o is still the same object
Here is one case that caught me out, using a global as a default value of a parameter.
globVar = None # initialize value of global variable
def func(param = globVar): # use globVar as default value for param
print 'param =', param, 'globVar =', globVar # display values
def test():
global globVar
globVar = 42 # change value of global
func()
test()
=========
output: param = None, globVar = 42
I had expected param to have a value of 42. Surprise. Python 2.7 evaluated the value of globVar when it first parsed the function func. Changing the value of globVar did not affect the default value assigned to param. Delaying the evaluation, as in the following, worked as I needed it to.
def func(param = eval('globVar')): # this seems to work
print 'param =', param, 'globVar =', globVar # display values
Or, if you want to be safe,
def func(param = None)):
if param == None:
param = globVar
print 'param =', param, 'globVar =', globVar # display values
You must use the global declaration when you wish to alter the value assigned to a global variable.
You do not need it to read from a global variable. Note that calling a method on an object (even if it alters the data within that object) does not alter the value of the variable holding that object (absent reflective magic).

Categories