I've read a few other SO (PythonScope and globals don't need global) but nothing seems to explain as explicitly as I would like and I'm having trouble mentally sifting through whether or not PyDocs tells me the answer to my question:
myList = [1]
def foo():
myList = myList + [2, 3]
def bar():
myList.extend([2, 3])
def baz():
myList += [2, 3]
Now, understandably,
>>> foo()
UnboundLocalError: local variable 'myList' referenced before assignment
and
bar() # works
myList # shows [1, 2, 3]
but then
>>> baz()
UnboundLocalError: local variable 'myList' referenced before assignment
I thought, however, that things like +=
implicitly called the method operators, in this case extend()
, but the error implies that for some reason it does not actually treat +=
as extends()
. Is this consistent with how Python parsing ought to work?
I would have thought that calling functions that are equivalent to method-operators, they would be equivalent in all cases. Instead it seems that it treats +=
as an actual assignment operator. Except, this isn't completely true, because if I do something (admittedly contrived):
myList = range(50000000) # wait a second or two on my laptop before returning
myList += [0] # returns instantly
myList = myList + [1] # wait a second or two before returning
all of which is expected, if +=
actually just calls extend()
.
Is there some finer distinction (or very obvious point...) that I'm missing that makes it clear that myList
in baz()
needs to be treated as a local variable, and that therefore the +=
cannot be implicitly converted to an extend()
such that it recognizes the global variable?
Use of “global†keyword to modify global variable inside a function. If your function has a local variable with same name as global variable and you want to modify the global variable inside function then use 'global' keyword before the variable name at start of function i.e.
To access a global variable in a function, if the function has a local variable with the same name, we use the global keyword before the variable name.
The global Keyword Normally, when you create a variable inside a function, that variable is local, and can only be used inside that function. To create a global variable inside a function, you can use the global keyword.
+=
doesn't implicitly call extend()
. Firstly, it is an augmented assignment operator.
If you look at the section on assignment
it says:
Assignment of an object to a single target is recursively defined as follows.
If the target is an identifier (name):
If the name does not occur in a global statement in the current code block: the name is bound to the object in the current local namespace. Otherwise: the name is bound to the object in the current global namespace.
Since an augmented assignment is:
Augmented assignment is the combination, in a single statement, of a binary operation and an assignment statement:
It plays by the same rules. As you can see:
>>> def baz():
myList += [2, 3]
>>> dis.dis(baz)
2 0 LOAD_FAST 0 (myList)
3 LOAD_CONST 1 (2)
6 LOAD_CONST 2 (3)
9 BUILD_LIST 2
12 INPLACE_ADD
13 STORE_FAST 0 (myList)
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
An augmented assignment evaluates the target (which, unlike normal assignment statements, cannot be an unpacking) and the expression list, performs the binary operation specific to the type of assignment on the two operands, and assigns the result to the original target. The target is only evaluated once..
The first call trys to evaluate myList
, this results in LOAD_FAST
since there was no global
statement it is assumed to be a local variable:
LOAD_FAST(var_num)
Pushes a reference to the local
co_varnames[var_num]
onto the stack.
It can't be found so the error is raised. If it was found, then we get to the oppcode INPLACE_ADD
which calls the method myList.__iadd__
which does the job of extend, once this operation completes the result will be assigned back to the variable but we never get this far.
You shouldn't really be manipulating global
s anyway, return the new result from your function or pass it as a parameter.
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