Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closures in a class scope

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?

like image 672
georg Avatar asked Oct 07 '22 00:10

georg


1 Answers

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.

like image 137
Martijn Pieters Avatar answered Oct 10 '22 02:10

Martijn Pieters