I'm not entirely sure this is for stackoverflow, so please correct me if not.
i.e. say we have t.py with contents:
class A(object):
pass
print("A:", A)
class B(object):
print("From B: A:", A)
class OuterClass(object):
class AA(object):
pass
print("AA:", AA)
class BB(object):
print("From BB: AA:", AA)
And now we execute it: $ python3 t.py
A: <class '__main__.A'>
From B: A: <class '__main__.A'>
AA: <class '__main__.AA'>
From BB: AA:
Traceback (most recent call last):
File "t.py", line 9, in <module>
class OuterClass(object):
File "t.py", line 14, in OuterClass
class BB(object):
File "t.py", line 15, in BB
print "From BB: AA:", AA
NameError: name 'AA' is not defined
From the docs:
A class definition is an executable statement. It first evaluates the inheritance list, if present. Each item in the inheritance list should evaluate to a class object or class type which allows subclassing. 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. The class name is bound to this class object in the original local namespace.
So I understand the behaviour but not the rationale behind making the scope not be lexical like everywhere else. It goes against "Special cases aren't special enough to break the rules." Why does class
behave differently than, say, def
?
Is this a "practicality beats purity" case? If so, what's the justification? I initially thought it might be an artefact of python 2.x, but as you can see above, the behaviour is also present in python 3.3.
The braces {} make a block all by themselves. I'd like to point out that if doesn't even create scopes in Python, so even if you wanted to use if True: it doesn't do anything (beside waste time, effort, and space).
Local (or function) scope is the code block or body of any Python function or lambda expression. This Python scope contains the names that you define inside the function. These names will only be visible from the code of the function.
Name references search at most three scopes: local, then global, then built-in. Name assignments create or change local names by default. “Global” declarations map assigned names to an enclosing module's scope.
As Wooble noted in a comment, the class block does create a new scope. The problem is that names in a class block scope are not accessible to scopes nested within that scope. This is mentioned in the documentation:
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope.
I can't find the source of this right now, but somewhere (I think on a StackOverflow question) I found one plausible rationale for this: if the class def were accessible inside nested blocks, method names would shadow global functions, including builtins. This would make it awkward to create classes that have methods with short, simple names, but that also make use of builtin functions of the same name. For instance, you couldn't do this:
class SomeDataStructure(object):
def sum(self):
return sum(self._data)
def otherCalc(self):
return sum(item for item in somethingElse)
If the class block were in scope within methods, this would cause an infinite recursion, since the inner sum
call would have access to the method name and would call that method instead of the builtin sum
function. Likewise, other methods such as the the otherCalc
method would no longer have access to the global sum function for their own use, but would always be getting the method. An answer to another SO question describes this by saying "it's because you're supposed to use self
to access methods in Python".
Now, that argument really only makes sense for functions inside classes, because function bodies aren't executed when the def
is, but class bodies are executed when the class
statement is. However, I think if you combine what I said above with the notions from this blog post, you get a reasonable rationale. Namely, nested classes weren't --- and still aren't --- considered a style that's worth supporting. Functions inside classes, though, are of course commonplace. The simplest way to handle the function-inside-a-class use case was to make the class
block not pass on its names to any nested scopes. It would arguably be better if it passed on its names to nested classes but not nested functions, but that would be a more complex rule, and no one cares enough about supporting nested classes to make it worthwhile.
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