6

I was wondering how do you think is the way to access a class attribute from a function within the class. I haven't found a reference in PEP8 or a popular question about it. e.g.

class MyClass(object):
    BAR = 1
    def foo(self):
        # Way A:
        print(self.BAR)

        # Way B:
        print(MyClass.BAR)

Accessing via ´self´ seems reasonable as the attribute is owned by the same class, close reference for obvious same-class reference. On the other hand, accessing via the class name itself is clear as it is static and makes the origin of the use clear and also could be more clear as it is paired with the class' name.

Ofek .T.
  • 741
  • 3
  • 10
  • 29

2 Answers2

8

When explicity naming the class name, you prevent subclass from overriding your attribute.

On the other hand, using self gives you this flexability. Consider the following code:

class MyClass(object):
    BAR = 1
    def foo(self):
        # Way A:
        print(self.BAR)

        # Way B:
        print(MyClass.BAR)


class SubClass(MyClass):
    BAR = 2

class SubClass2(MyClass):
    pass

# output
>>> a = SubClass()
>>> a.foo()
2
1
>>> b = SubClass2()
>>> b.foo()
1
1
Chen A.
  • 10,140
  • 3
  • 42
  • 61
2

For reads it doesn't really matter which you use - self.BAR and MyClass.BAR are syntactically equivalent unless you have a class hierarchy where a subclass redefines the value of BAR.

For writes they are not the same. Writing to self.BAR will effectively create a new variable which is local to the self object instance, so any reads from self.BAR from another object instance will not see the modifications. These can be pretty horrible to debug because it's not obvious from the code what should happen as it is timing sensitive.

In general for class variables you really should be using MyClass.BAR if you want a specific variable from a specific level in the class hierarchy, or type(self).BAR or self.__class__.BAR if you want something which is inheritance safe. This is specifically and obviously a class variable and avoids the problem outlined above with dynamic aliases popping into existence at runtime. Using self just bakes in some fragility which can be hard to spot in future.

solidpixel
  • 10,688
  • 1
  • 20
  • 33
  • 1
    or `type(self).BAR` – Jean-François Fabre May 28 '18 at 13:24
  • I then expect your code to use `self.__class__.method(self)` instead of `self.method()` ?-) More seriously: while the first two paragraphs are perfect, the third one is just not the pythonic way. Pythoneers know that an attribute not resolved on the instance will be looked up on the class so `self.BAR` is perfectly clear. – bruno desthuilliers May 28 '18 at 13:25
  • @brunodesthuilliers The question was specifically about class variables, rather than methods. Methods already resolve via the MRO and it's unlikely that you'll be redefining methods at runtime beyond simple inherritance. – solidpixel May 28 '18 at 13:28
  • 1
    @solidpixel methods ARE class variables. – bruno desthuilliers May 28 '18 at 13:30
  • Sure, from a purely technical point of view they are. From a coding style point of view it is actually rare that you actually dynamically write over an existing bound method at runtime (it's very bad practise), whereas writable "normal" class variables can be reasonably common in real code. – solidpixel May 28 '18 at 13:33
  • Replacing methods (or adding new ones) on a per-instance basis is indeed rarer but it's sometimes the best solution and definitly not "very bad practice". OTHO, there's a strong naming convention about ALL_UPPER names denoting (pseudo) constants so rebinding `self.BAR` _is_ bad practice. Oh and yes: you're aware that methods are not the only descriptors ? Would you write `type(self).x.__get__(self, type(self))` to access a property ? I've been reading Python code for 19+ years now and I've only seen `type(self).attrname` used instead of `self.attrname` for a couple corner cases. – bruno desthuilliers May 28 '18 at 13:47
  • True - for things which are guaranteed constants I'd probably use `self.BAR` too to be honest ;) – solidpixel May 29 '18 at 12:38