I would like to know details about why this doesn't work as expected:
def outer():
mylist = []
def inner():
mylist += [1]
inner()
outer()
Especially because mylist.__iadd__([1])
works fine.
Any variable assigned to is assumed to be of local scope.
To assign to a global variable you do:
global var
var = 5
You can't do quite what you're doing in Python 2, but in Python 3 you can do:
nonlocal mylist
mylist += [1]
The Python 2 alternative for changing an item or attribute of something is
def outer():
mylist = []
def inner(mylist = mylist):
mylist += [1]
inner()
outer()
If you want to replace the value of the variable, you need to:
def outer():
def setlist(newlist):
mylist = newlist
mylist = []
def inner():
setlist(['new_list'])
inner()
outer()
The problem is that when you assign to a variable name inside a function, Python assumes you're trying to create a new local variable that will mask a similarly-named variable in outer scope. Since +=
has to get the value of mylist
before it can modify it, it complains, because the local version of mylist
isn't yet defined. MRAB's answer gives a clear explanation of the semantics.
On the other hand, when you do mylist.__iadd__([1])
, you aren't assigning a new variable name inside the function. You're just using a built-in method to modify an already-assigned variable name. As long as you don't try to assign a new value to mylist
, you won't have a problem. For the same reason, the line mylist[0] = 5
would also work inside inner
if the definition of mylist
in outer
were mylist = [1]
.
Note however that if you do try to assign a new value to mylist
anywhere in the function, mylist.__iadd__([1])
will indeed fail:
>>> outer()
>>> def outer():
... mylist = []
... def inner():
... mylist.__iadd__([1])
... mylist = []
... inner()
...
>>> outer()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in outer
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'mylist' referenced before assignment
If you want to assign a new value to a variable from a containing scope, in 3.0+ you can use nonlocal
, in just the way you'd use global
to assign a new value to a variable in global scope. So instead of this:
>>> mylist = []
>>> def inner():
... global mylist
... mylist += [1]
...
>>> inner()
>>> mylist
[1]
You do this:
def outer():
mylist = []
def inner():
nonlocal mylist
mylist += [1]
inner()
print(mylist)
outer()
If a name is bound (a variable is assigned) in a function then that name is treated as local unless it's declared global.
Thus in inner
, mylist
is local.
When you write x += y
, at runtime Python will try:
x = x.__iadd__(y)
If that fails, Python then tries:
x = x.__add__(y)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With