[Tutor] Python scope and variable binding

Arnaud Legout arnaud.legout at inria.fr
Wed Nov 27 16:04:15 CET 2013


Hi,

I have some experience on Python programming, but I have hard time to
understand to full variable and attribute lookup in Python in corner
cases. This mail will be a bit long with many examples, but I hope it
will help me and others to better grasp the full story of variables
and attribute lookup in the Python 2.7 branch.

I am using the terms of PEP 227
(http://www.python.org/dev/peps/pep-0227/) for code blocks (such as
modules, class definition, function definitions, etc.) and
variable bindings (such as assignments, argument declarations, class
and function declaration, for loops, etc.)

I am using the terms variables for names that can be called without a
dot, and attributes for names that need to be qualified with an object
name (such as obj.x for the attribute x of object obj).

There are three scopes in Python for all code blocks, but the functions:
-local
-global
-builtin

There are four blocks in Python for the functions only (according to
PEP 227):
-local
-enclosing functions
-global
-builtin

The rule for a variable to bind it to and find it in a block is
quite simple:
-any binding of a variable to an object in a block makes this variable
local to this block, unless the variable is declared global (in that
case the variable belongs to the global scope)
-a reference to a variable is looked up using the rule LGB (local,
global, builtin) for all blocks, but the functions
-a reference to a variable is looked up using the rule LEGB (local,
enclosing, global, builtin) for the functions only.

Let me know take examples validating this rule, and showing many
special cases. For each example, I will give my understanding. Please
correct me if I am wrong. For the last example, I don't understand the
outcome.

example 1:

x = "x in module"
class A():
     print "A: "  + x                    #x in module
     x = "x in class A"
     print locals()
     class B():
         print "B: " + x                 #x in module
         x = "x in class B"
         print locals()
         def f(self):
             print "f: " + x             #x in module
             self.x = "self.x in f"
             print x, self.x
             print locals()

 >>>A.B().f()
A: x in module
{'x': 'x in class A', '__module__': '__main__'}
B: x in module
{'x': 'x in class B', '__module__': '__main__'}
f: x in module
x in module self.x in f
{'self': <__main__.B instance at 0x00000000026FC9C8>}

There is no nested scope for the classes (rule LGB) and a function in
a class cannot access the attributes of the class without using a
qualified name (self.x in this example). This is well described in
PEP227.

example 2:

z = "z in module"
def f():
     z = "z in f()"
     class C():
         z = "z in C"
         def g(self):
             print z
             print C.z
     C().g()
f()
 >>>
z in f()
z in C

Here variables in functions are looked up using the LEGB rule, but if
a class is in the path, the class arguments are skipped. Here again,
this is what PEP 227 is explaining.

example 3:

var = 0
def func():
     print var
     var = 1
 >>> func()

Traceback (most recent call last):
   File "<pyshell#102>", line 1, in <module>
     func()
   File "C:/Users/aa/Desktop/test2.py", line 25, in func
     print var
UnboundLocalError: local variable 'var' referenced before assignment


We expect with a dynamic language such as python that everything is
resolved dynamically. But this is not the case for functions. Local
variables are determined at compile time. PEP 227  and
http://docs.python.org/2.7/reference/executionmodel.html describe this
behavior this way

  "If a name binding operation occurs anywhere within a code block, all
uses of the name within the block are treated as references to the
current block."

example 4:

x = "x in module"
class A():
     print "A: " + x
     x = "x in A"
     print "A: " + x
     print locals()
     del x
     print locals()
     print "A: " + x
 >>>
A: x in module
A: x in A
{'x': 'x in A', '__module__': '__main__'}
{'__module__': '__main__'}
A: x in module

But we see here that this statement in PEP227 "If a name binding
operation occurs anywhere within a code block, all uses of the name
within the block are treated as references to the current block." is
wrong when the code block is a class. Moreover, for classes, it seems
that local name binding is not made at compile time, but during
execution using the class namespace in __dict__. In that respect,
PEP227 and the execution model in the Python doc is misleading and for
some parts wrong.

example 5:

x = 'x in module'
def  f2():
     x = 'x in f2'
     def myfunc():
         x = 'x in myfunc'
         class MyClass(object):
             x = x
             print x
         return MyClass
     myfunc()
f2()
 >>>
x in module

my understanding of this code is the following. The instruction x = x
first look up the object the right hand x of the expression is referring
to. In that case, the object is looked up locally in the class, then
following the rule LGB it is looked up in the global scope, which is
the string 'x in module'. Then a local attribute x to MyClass is
created in the class dictionary and pointed to the string object.

example 6:
Now here is an example I cannot explain.
It is very close to example 5, I am just changing the local MyClass
attribute from x to y.

x = 'x in module'
def  f2():
     x = 'x in f2'
     def myfunc():
         x = 'x in myfunc'
         class MyClass(object):
             y = x
             print y
         return MyClass
     myfunc()
f2()
 >>>
x in myfunc

Why in that case the x reference in MyClass is looked up in the
innermost function?

Best,
Arnaud.



More information about the Tutor mailing list