Python local vs global variables

前端 未结 4 1665
无人共我
无人共我 2020-12-01 21:52

I understand the concept of local and global variables in Python, but I just have a question about why the error comes out the way it is in the following code. Python execut

相关标签:
4条回答
  • 2020-12-01 22:01

    Short explanation:

    a variable is bound to the test function. When you attempt to print it, Python throws the error because the local variable a will be initialized later. But if you remove the a=0, the code will execute without any problems and output 0.

    Longer explanation

    Python interpreter starts searching for the variable named a from the local scope. It sees that a is indeed declared within the function and initialized only on the fifth line. Once it's found, the lookup is over.

    When it tries to process print a (forth line) it says 'Oh boy, I need to print a variable that's not initialized yet, I'd better throw an error.'

    Explanation based on function's bytecode

    If you run this code:

    import dis
    
    a = 0
    
    def test():
      print a  #line 4, Error : local variable 'a' referenced before assignment
      a = 0    #line 5
    
    dis.dis(test)
    

    you'll get this output:

      6           0 LOAD_FAST                0 (a)
                  3 PRINT_ITEM          
                  4 PRINT_NEWLINE       
    
      7           5 LOAD_CONST               1 (0)
                  8 STORE_FAST               0 (a)
                 11 LOAD_CONST               0 (None)
                 14 RETURN_VALUE        
    

    Explanation on the unclear parts of the above:

    • LOAD_FAST means that a's reference is being pushed onto the stack.
    • STORE_FAST means that Python assigns 0 (from LOAD_CONST) to the local variable a

    So, the problem is that LOAD_FAST goes before STORE_FAST.

    0 讨论(0)
  • 2020-12-01 22:05

    Setup and Testing

    To analyze your question, let's create two separate test functions that replicate your issue:

    a=0
    
    def test1():
        print(a)
    
    test1()
    

    prints 0. So, calling this function is not problematic, but on the next function:

    def test2():
        print(a)  # Error : local variable 'a' referenced before assignment
        a=0  
    
    test2()
    

    We get an error:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in test2
    UnboundLocalError: local variable 'a' referenced before assignment
    

    Disassembly

    We can disassemble the two functions (first Python 2):

    >>> import dis
    >>> dis.dis(test1)
      2           0 LOAD_GLOBAL              0 (a)
                  3 PRINT_ITEM          
                  4 PRINT_NEWLINE       
                  5 LOAD_CONST               0 (None)
                  8 RETURN_VALUE        
    

    And we see that the first function automatically loads the global a, while the second function:

    >>> dis.dis(test2)
      2           0 LOAD_FAST                0 (a)
                  3 PRINT_ITEM          
                  4 PRINT_NEWLINE       
    
      3           5 LOAD_CONST               1 (0)
                  8 STORE_FAST               0 (a)
                 11 LOAD_CONST               0 (None)
                 14 RETURN_VALUE      
    

    seeing that a is assigned inside it, attempts to LOAD_FAST from the locals (as a matter of optimization, as functions are pre-compiled into byte-code before running.)

    If we run this in Python 3, we see nearly the same effect:

    >>> test2()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in test2
    UnboundLocalError: local variable 'a' referenced before assignment
    >>> 
    >>> import dis
    >>> dis.dis(test1)
      2           0 LOAD_GLOBAL              0 (print) 
                  3 LOAD_GLOBAL              1 (a) 
                  6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
                  9 POP_TOP              
                 10 LOAD_CONST               0 (None) 
                 13 RETURN_VALUE     
    
    >>> dis.dis() # disassembles the last stack trace
      2           0 LOAD_GLOBAL              0 (print) 
        -->       3 LOAD_FAST                0 (a) 
                  6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
                  9 POP_TOP              
    
      3          10 LOAD_CONST               1 (0) 
                 13 STORE_FAST               0 (a) 
                 16 LOAD_CONST               0 (None) 
                 19 RETURN_VALUE        
    

    We see our error is on the LOAD_FAST again.

    0 讨论(0)
  • 2020-12-01 22:06

    Python doesn't execute line by line in the function code you submitted. It has first to parse it as a bloc of execution. It decides if a variable is local or global depending if it is written at (function) local level. As it is the case, it decides that the variable is local, hence the error.

    0 讨论(0)
  • 2020-12-01 22:14

    This is because, in python, you need to tell that you are going to modify the value of a global variable. You do that as:

    def test():
      global a
      print a  #line 4, Error : local variable 'a' referenced before assignment
      a=0      #line 5
    

    With global a, you can modify variables in the function. You may want to have a look at this:

    • Python: Why is global needed only on assignment and not on reads?
    0 讨论(0)
提交回复
热议问题