1

When (if at all) do functions recognize variables that have been initialized in the main function?

E.g.: When I write the following Python code:

def plus():
    variable += 1

if __name__ == '__main__': 
    variable = 1
    plus()

I get the following error: UnboundLocalError: local variable 'variable' referenced before assignment

However, when I do something similar, but with a dictionary:

def plus():
    dic[1] += 1

if __name__ == '__main__':
    dic = {}
    dic[1] = 1
    plus()
    print dic[1]

The output is : 2

What's the difference between the two cases?

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
Cheshie
  • 2,777
  • 6
  • 32
  • 51
  • Have you read e.g. https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value? There are lots of questions on SO already explaining Python scope generally and `UnboundLocalError` specifically. – jonrsharpe Jan 13 '15 at 12:32
  • possible duplicate of http://stackoverflow.com/questions/27795943/referencing-global-primitives-vs-objects-in-python/27796098 – Aran-Fey Jan 13 '15 at 12:38
  • This has been asked at least a thousand times here and elsewhere. Also and for the record, your question is wrongly worded ("variables that have been initialized in the main function") - you don't have any "main" function here, just a top-level branch. – bruno desthuilliers Jan 13 '15 at 12:47
  • OK I apologize to all of those who were annoyed by the question. I guess you need to know the answer in order to know how to ask the question with the right wording. Thanks of the answers. – Cheshie Jan 13 '15 at 12:52

1 Answers1

1

+= operator is considered as an assignment when used with simple variables. So, Python while parsing the function body will add variable in <function_object>.func_code.co_varnames, and due to this during runtime Python will never look for that variable in any other scope unless you had global or nonlocal declaration(Python 3 only) at the top of the function. Note that it doesn't matter if you used variable before using it with +=(see the last example), the variable is now local everywhere in the function body.

>>> def plus():
...     var += 1
...     
>>> dis.dis(plus)
  2           0 LOAD_FAST                0 (var)
              3 LOAD_CONST               1 (1)
              6 INPLACE_ADD         
              7 STORE_FAST               0 (var)
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE   

On the other hand, dic[1] is variable look-up followed by a BINARY_SUBSCR(same thing for LOAD_ATTR as well; you can do x.extend([100]) but not x+=[100]; where x is a list) and as there are no assignment statements related to dic after that Python considers it as a either a global variable(LOAD_GLOBAL) or a free variable(LOAD_DEREF) and fetches its value from there.

>>> def plus():
    var[0] += 1
...     
>>> dis.dis(plus)
  2           0 LOAD_GLOBAL              0 (var)
              3 LOAD_CONST               1 (0)
              6 DUP_TOPX                 2
              9 BINARY_SUBSCR       
             10 LOAD_CONST               2 (1)
             13 INPLACE_ADD         
             14 ROT_THREE           
             15 STORE_SUBSCR        
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

>>> def plus_with_twist():
    var[0] += 1  # this will fail due to the next line
    var += 1     # Now `var` is a local variable everywhere in the function body
>>> dis.dis(plus_with_twist)
  2           0 LOAD_FAST                0 (var)
              3 LOAD_CONST               1 (0)
              6 DUP_TOPX                 2
              9 BINARY_SUBSCR       
             10 LOAD_CONST               2 (1)
             13 INPLACE_ADD         
             14 ROT_THREE           
             15 STORE_SUBSCR        

  3          16 LOAD_FAST                0 (var)
             19 LOAD_CONST               2 (1)
             22 INPLACE_ADD         
             23 STORE_FAST               0 (var)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE    
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504