Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing (and not) to global variable in Python

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?

like image 233
DCS Avatar asked Dec 25 '13 09:12

DCS


People also ask

Should you avoid using global variables in Python?

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.

How do you avoid global variables?

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.

How do you not change a global variable in Python?

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.

Do you need to declare global variable in Python?

To create a global variable in Python, you need to declare the variable outside the function or in a global scope.


1 Answers

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].

like image 149
Volatility Avatar answered Sep 30 '22 21:09

Volatility