Reading the documentation I came across the following paragraph:
A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block. If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name. 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 decided to try accessing class variable from a method myself:
>>> class A():
i = 1
def f(self):
print(i)
>>> a = A()
>>> a.i
1
>>> a.f()
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
a.f()
File "<pyshell#4>", line 4, in f
print(i)
NameError: global name 'i' is not defined
I know that the variable i
may be accessed by explicitly pointing to the class name A.i
:
>>> a = A()
>>> class A():
i = 1
def f(self):
print(A.i)
>>> a = A()
>>> a.f()
1
The question is why the developers of the language made class variables not visible from methods? What is the rationale behind it?
A class block is syntactic sugar for building a dictionary, which is then passed to the metaclass (usually type
) to construct the class object.
class A:
i = 1
def f(self):
print(i)
Is roughly equivalent to:
def f(self):
print(i)
attributes = {'f': f, 'i': 1)
A = type('A', (object,) attributes)
Seen that way, there is no outer scope the i
name to come from. However there obviously is a temporary scope for you to execute the statements in the class block. It would be possible for that class block to desugar to something more like:
def attributes():
i = 1
def f(self):
print(i)
return locals()
A = type('A', (object,), attributes())
In this case the outer reference to i
would work. However, this would be going "against the grain" of Python's object system philosophy.
Python has objects, which contain attributes. There's not really any concept of "variables" other than local variables in functions (which can be nested to create a scope chain). A bare name is looked up as a local variable, then in outer scopes (which come from functions). Attributes are looked up, using the dotted name syntax, on other objects, and you always specify which object to look in.
There is a protocol for resolving attribute references, which says that when attribute
is not found on obj
, obj.attribute
can be resolved by looking in the class of obj
(and its base classes, using the method resolution order). This is actually how methods are found; when in your example you executed a.f()
, the a
object contains no attribute f
, so the class of a
(which is A
) is searched, and the method definition is found.
Having class attributes automatically available in an outer scope for all methods would be weird, because no other attribute works this way. It would also have the following drawbacks:
thing
rather than using Class.thing
or self.thing
. This makes them look like module globals when they're not (method definitions are usually short enough that you can easily see they're not defined locally).self
allows them to play nicer with subclasses, as it allows subclasses to override the attribute. That probably isn't as big a deal for "class constants", but it's very important for staticmethods and classmethods.Those are the main reasons I see, but ultimately it's just a choice the designers of Python made. You find it weird that you don't have this implicit ability to reference class variables, but I find implicit class and instance variable access in languages like C++ and Java to be weird. Different people have different opinions.
This seems to be related to the use of an explicit self
parameter, and the requirement that all method calls and instance attribute accesses explicitly use self
. It would be at least strange if the uncommon case of accessing a class scope function as a normal function would be much easier than the common case of accessing it as a method via self
. Class variables are usually also accessed via the instance in Python.
In C++, in contrast, the class scope is visibile in all methods, but calling a method implicitly passes this
. This seems to be the other sane choice.
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