[Python-au] strange pre-acting bug

PeterL pacqa100 at yahoo.com.au
Wed May 19 00:26:32 UTC 2010


Nice work Ryan. Thanks.
Peter

-- 
Peter Lovett
----------------------------------------------------------------------
Plus Plus Pty Ltd
training + consulting + development
C + C++ + C# + Perl + Python + Java + VB/VBA/ASP + SQL + HTML + XML
Unix&  Shell scripting
Microsoft&  Adobe products

http://www.plusplus.com.au/
http://www.pythontraining.com.au/

Everything should be made as simple as possible, but not simpler.
-- Einstein
----------------------------------------------------------------------



On 19/05/2010 9:40 AM, Ryan Kelly wrote:
> On Wed, 2010-05-19 at 09:09 +1000, PeterL wrote:
>    
>> Interesting. This is a common mis-understanding on the scoping rules of
>> Python functions. I might do a lightning talk on it at PyCon.
>> You need to be clear on the error. An 'UnboundLocalError' means that you
>> are using a mainline variable inside a function before you change it.
>>
>> In Python, a function *can* _read_ a mainline's variable with no problem.
>> If a function goes to change a mainline's variable, then it become
>> 'bound local', which means you must give it a value before you use it.
>>      
> It might also be instructive to look at the bytecode that python
> generates for each of these cases (or it might be that I just love
> looking at bytecode...)
>
>    
>> Compare:
>> -----------
>> # Mainline variable available to a fn
>> def fn():
>>       print x    # Prints 5
>>      
>    
>>>> import dis
>>>> dis.dis(fn)
>>>>          
>    2           0 LOAD_GLOBAL              0 (x)
>                3 PRINT_ITEM
>                4 PRINT_NEWLINE
>                5 LOAD_CONST               0 (None)
>                8 RETURN_VALUE
>
>
> Since x is not given a value inside the function, it is accessed as a
> global using the "LOAD GLOBAL" bytecode.
>
>    
>> ----------
>> # Mainline variables can be localised inside a fn
>> def fn():
>>       x = 7
>>       print x    # Prints 7
>>       # Throw the localised value of x away
>>      
>    
>>>> dis.dis(fn)
>>>>          
>    2           0 LOAD_CONST               1 (7)
>                3 STORE_FAST               0 (x)
>
>    3           6 LOAD_FAST                0 (x)
>                9 PRINT_ITEM
>               10 PRINT_NEWLINE
>               11 LOAD_CONST               0 (None)
>               14 RETURN_VALUE
>
>
> Since x is now given a value inside the function, it is treated as a
> local variable and loaded using the "LOAD_FAST" bytecode.  Note also the
> "STORE_FAST" opcode which sets the value of the local variable.
>
> This is an optimisation by the python interpreter.  Accessing local
> variables is actually a lot faster than accessing globals (basically,
> it's an array lookup rather than a hash-table lookup).
>
>    
>> ----------
>> # Local bound variables must be set before they're used
>> def fn():
>>       print x    # Error - UnboundLocalError - mainline variables that
>> are localised must be set before used
>>       x = 7     # Localise x
>>       print x    # Never reaches here
>>      
>    
>>>> dis.dis(fn)
>>>>          
>    2           0 LOAD_FAST                0 (x)
>                3 PRINT_ITEM
>                4 PRINT_NEWLINE
>
>    3           5 LOAD_CONST               1 (7)
>                8 STORE_FAST               0 (x)
>
>    4          11 LOAD_FAST                0 (x)
>               14 PRINT_ITEM
>               15 PRINT_NEWLINE
>               16 LOAD_CONST               0 (None)
>               19 RETURN_VALUE
>
> Whoops!  We now have a "LOAD_FAST" without a preceding "STORE_FAST",
> which produces the UnboundLocalError you are seeing.
>
> I've always considered this something of a "leaky optimisation" on the
> part of the python interpreter, but once you know the rule it's quite
> consistent and easy to avoid.
>
> Basically, every variable has a single unambiguous scope and that scope
> cannot change during function execution.  Assigning to an otherwise
> undeclared variable makes its scope local.
>
>
>    Cheers,
>
>       Ryan
>
>
>
>    
>
>
> _______________________________________________
> python-au maillist  -  python-au at starship.python.net
> http://starship.python.net/mailman/listinfo/python-au
>    

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://starship.python.net/pipermail/python-au/attachments/20100519/2d93d2d9/attachment-0001.htm>


More information about the python-au mailing list