From my understanding, function and class scopes behave pretty much the same:
>>> def x():
... a = 123
... print (locals())
...
>>> x()
{'a': 123}
>>> class x():
... a = 123
... print (locals())
...
{'a': 123, '__module__': '__main__'}
When, however, I define a closure, behaviors are different. A function simply returns the local binding, as expected:
>>> def x():
... a = 123
... t = lambda: a
... return t
...
>>> x()()
123
whereas in a class the binding appears to be lost:
>>> class x():
... a = 123
... t = lambda self: a
...
>>> x().t()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in <lambda>
NameError: global name 'a' is not defined
Can anyone explain the discrepancy?
The class scope is a temporary scope, it only exists while executing the body of the class definition. The resulting dict
is used to create the class namespace, the __dict__
of the class.
As far as functions defined in a class are concerned, the next scope 'up' is the scope of the class
definition itself.
The following works fine:
>>> def foo():
... spam = 'eggs'
... class Bar(object):
... def baz(self): return spam
... return Bar()
...
>>> foo().baz()
'eggs'
This is documented in pep 227:
Names in class scope are not accessible. Names are resolved in the innermost enclosing function scope. If a class definition occurs in a chain of nested scopes, the resolution process skips class definitions.
and in the class
compound statement documentation:
The class’s suite is then executed in a new execution frame (see section Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains only function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved. [4] A class object is then created using the inheritance list for the base classes and the saved local namespace for the attribute dictionary.
Emphasis mine; the execution frame is the temporary scope.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With