11

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?

ovgolovin
  • 13,063
  • 6
  • 47
  • 78

2 Answers2

12

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:

  1. Functions defined outside the class and assigned to it later would have to use different syntax to refer to the class attribute than functions defined as part of a class.
  2. Because it's shorter, it would encourage reference to class attributes including staticmethods and classmethods as bare names: 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).
  3. Note that looking for the attributes on 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.

S.B
  • 13,077
  • 10
  • 22
  • 49
Ben
  • 68,572
  • 20
  • 126
  • 174
  • 2
    Oh, stackoverflow, where correct answers that take longer to answer are less voted than incorrect, ambiguous answers taht are written in a hush. I Hope the O.P. can perceive that this is much more an answer than the higher voted one. – jsbueno Mar 01 '12 at 17:34
  • @jsbueno: It would be more constructive to point out what exactly it is that is incorrect in my answer. (I'll comment more on this answer shortly.) – Sven Marnach Mar 01 '12 at 17:50
  • I agree with points 2. and 3. and the remarks of the interaction with inheritance -- they are certainly some of the reasons for this design decision. – Sven Marnach Mar 01 '12 at 18:25
  • 2
    @jsbueno Just because I pontificated longer doesn't mean Sven's answer isn't correct. I agree entirely with the reason he suggested, I just suggested some more. My thoughts are also guesswork, since as Sven pointed out there's no hard technical *requirement* for Python to work this way; arguments for why Python *should* work this way are always going to be at least party subjective. – Ben Mar 02 '12 at 00:40
  • @Sven The only two constructs that create scopes are class blocks and function definitions. Nothing else does, so if class scopes don't work the same way as function scopes then they're not the same thing. I'm ignorant of the actual code of the CPython interpreter, so I was only talking at a conceptual level; just because function scopes and class scopes are implemented with the same data structures internally doesn't mean they're the same thing conceptually. A class block creates a dictionary, conceptually it doesn't exist as a scope after it has done that. – Ben Mar 02 '12 at 00:46
  • 1
    @Sven Scoping in Python is lexical (I definitely agree that the alternative is insane). But *conceptually* programmers wouldn't think about a class scope as a lexical scope; they'd just think "methods implicitly have access to class attributes". So it wouldn't be strange that functions defined outside a class and then assigned to it have different scopes, but it would be confusing! The stuff about object philosophy was just that *attributes* are always retrieved from a particular object, not implicitly available in a scope. Implicitly in-scope class attributes would go against that. – Ben Mar 02 '12 at 00:50
  • @Ben: I see where you are coming from. Admittedly, I was a bit cheesed off when wrote above comments. I think I raise a point, (mainly about lexical scoping), but the wording seems a bit strong to me now, so I will simply delete them. – Sven Marnach Mar 02 '12 at 01:03
  • Modules and generator expressions also create scopes (and in Python 3.x also list/dict/set comprehensions). Module scopes are also implemented using dictionaries, but behave differently. I honestly think the best conceptual description is to say that class scopes are normal scopes that are skipped during name look-up, but I agree there are other valid ways to look at this. – Sven Marnach Mar 02 '12 at 01:10
  • @Sven: Not sure you needed to delete the comments (although I did suspect you were "a bit cheesed off" :) ). I think your point about lexical scopes adds something, if anyone can be bothered reading the comments after wading through my answer. And yes, how could I forget **modules** as scopes? :o – Ben Mar 02 '12 at 01:11
  • 1
    The fact that generator expression create a scope is also relevant in this particular context because it leads to this [strange anomaly](https://gist.github.com/1954605), which would be an argument for *not* skipping the class scope during name look-up. – Sven Marnach Mar 02 '12 at 01:30
  • 1
    @Ben I think everyone interested in the topic would read comments. Because it's comments where opinions of other people about the answer are laid out. – ovgolovin Mar 02 '12 at 08:22
7

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.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Yes. I see. So they try to enforce using `self` to access class variables. Is it possible that allowing to access class variables from methods would have other repercussions? – ovgolovin Feb 29 '12 at 20:31
  • @ovgolovin: Well, let's wait what explanations other people come up with. Maybe someone finds a good web link. – Sven Marnach Feb 29 '12 at 20:37
  • 1
    Actually, this is much of whishfull thinking guesswork"- check Ben's answer to understand what is going on in Python. – jsbueno Mar 01 '12 at 17:35
  • 2
    @jsbueno: This is a *design decision* in Python. There are no real technical reasons, the reasons are rather "soft". So any answer will have to do a bit of guesswork, unless you find a source where Guido explains why he did things the way he did. The perceived technical reasons in Ben's answer are largely wrong, though he gives a few good soft reasons, in particular the interaction with subclassing. – Sven Marnach Mar 01 '12 at 18:24