<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
Nice work Ryan. Thanks.<br>
Peter<br>
<pre class="moz-signature" cols="72">-- 
Peter Lovett
----------------------------------------------------------------------
Plus Plus Pty Ltd
training + consulting + development
C + C++ + C# + Perl + Python + Java + VB/VBA/ASP + SQL + HTML + XML
Unix &amp; Shell scripting
Microsoft &amp; Adobe products

<a class="moz-txt-link-freetext" href="http://www.plusplus.com.au/">http://www.plusplus.com.au/</a>
<a class="moz-txt-link-freetext" href="http://www.pythontraining.com.au/">http://www.pythontraining.com.au/</a>

Everything should be made as simple as possible, but not simpler.
-- Einstein
----------------------------------------------------------------------</pre>
<br>
<br>
On 19/05/2010 9:40 AM, Ryan Kelly wrote:
<blockquote cite="mid:1274226056.2815.13.camel@durian" type="cite">
  <pre wrap="">On Wed, 2010-05-19 at 09:09 +1000, PeterL wrote:
  </pre>
  <blockquote type="cite">
    <pre wrap="">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.
    </pre>
  </blockquote>
  <pre wrap="">
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...)

  </pre>
  <blockquote type="cite">
    <pre wrap="">
Compare:
-----------
# Mainline variable available to a fn
def fn():
     print x    # Prints 5
    </pre>
  </blockquote>
  <pre wrap="">
  </pre>
  <blockquote type="cite">
    <blockquote type="cite">
      <blockquote type="cite">
        <pre wrap="">import dis
dis.dis(fn)
        </pre>
      </blockquote>
    </blockquote>
  </blockquote>
  <pre wrap="">  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.

  </pre>
  <blockquote type="cite">
    <pre wrap="">----------
# Mainline variables can be localised inside a fn
def fn():
     x = 7
     print x    # Prints 7
     # Throw the localised value of x away
    </pre>
  </blockquote>
  <pre wrap="">
  </pre>
  <blockquote type="cite">
    <blockquote type="cite">
      <blockquote type="cite">
        <pre wrap="">dis.dis(fn)
        </pre>
      </blockquote>
    </blockquote>
  </blockquote>
  <pre wrap="">  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).

  </pre>
  <blockquote type="cite">
    <pre wrap="">----------
# 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
    </pre>
  </blockquote>
  <pre wrap="">
  </pre>
  <blockquote type="cite">
    <blockquote type="cite">
      <blockquote type="cite">
        <pre wrap="">dis.dis(fn)
        </pre>
      </blockquote>
    </blockquote>
  </blockquote>
  <pre wrap="">  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



  </pre>
  <pre wrap="">
<fieldset class="mimeAttachmentHeader"></fieldset>
_______________________________________________
python-au maillist  -  <a class="moz-txt-link-abbreviated" href="mailto:python-au@starship.python.net">python-au@starship.python.net</a>
<a class="moz-txt-link-freetext" href="http://starship.python.net/mailman/listinfo/python-au">http://starship.python.net/mailman/listinfo/python-au</a>
  </pre>
</blockquote>
<br>
</body>
</html>