This message is a 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 Python 2.7.
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 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?