Coming from much less dynamic C++, I have some trouble understanding the behaviour of this Python (2.7) code.
Note: I am aware that this is bad programming style / evil, but I would like to understand it non the less.
vals = [1,2,3]
def f():
vals[0] = 5
print 'inside', vals
print 'outside', vals
f()
print 'outside', vals
This code runs without error, and f
manipulates the (seemingly) global list. This is contrary to my prior understanding that global variables that are to be manipulated (and not only read) in a function must be declared as global ...
.
On the other hand, if I replace vals[0] = 5
with vals += [5,6]
, execution fails with an UnboundLocalError
unless I add a global vals
to f
. This is what I would have expected to happen in the first case as well.
Could you explain this behaviour?
Why can I manipulate vals
in the first case? Why does the second type of manipulation fail while the first does not?
Update:
It was remarked in a comment that vals.extend(...)
works without global
. This adds to my confusion - why is +=
treated differently from a call to extend
?
The use of global variable in python is considered bad practice and should generally be avoided. Instead the user can try and use a parameter for passing a value onto a function or return a value to obtain it.
The simplest way to avoid globals all together is to simply pass your variables using function arguments. As you can see, the $productData array from the controller (via HTTP request) goes through different layer: The controller receives the HTTP request. The parameters are passed to the model.
Another way to handle (not use) global variables is to wrap the functions and variables you wish to be global in a class. While this is a little heavy for this specific case - classes add a host of functionality and flexability to the project. (Personally) highly recommended.
To create a global variable in Python, you need to declare the variable outside the function or in a global scope.
global
is only needed when you are trying to change the object which the variable references. Because vals[0] = 5
changes the actual object rather than the reference, no error is raised. However, with vals += [5, 6]
, the interpreter tries to find a local variable because it can't change the global variable.
The confusing thing is that using the +=
operator with list modifies the original list, like vals[0] = 5
. And whereas vals += [5, 6]
fails, vals.extend([5, 6])
works. We can enlist the help of dis.dis
to lend us some clues.
>>> def a(): v[0] = 1
>>> def b(): v += [1]
>>> def c(): v.extend([1])
>>> import dis
>>> dis.dis(a)
1 0 LOAD_CONST 1 (1)
3 LOAD_GLOBAL 0 (v)
6 LOAD_CONST 2 (0)
9 STORE_SUBSCR
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis(b)
1 0 LOAD_FAST 0 (v)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (v)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
d
>>> dis.dis(c)
1 0 LOAD_GLOBAL 0 (v)
3 LOAD_ATTR 1 (extend)
6 LOAD_CONST 1 (1)
9 BUILD_LIST 1
12 CALL_FUNCTION 1
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
We can see that functions a
and c
use LOAD_GLOBAL
, whereas b
tries to use LOAD_FAST
. We can see now why using +=
won't work - the interpreter tries to load v
as a local variable because of it's default behaviour with in-place addition. Because it can't know whether v
is a list or not, it essentially assumes that the line means the same as v = v + [1]
.
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