54

I have this class:

class ReallyLongClassName:
    
    static_var = 5

    def instance_method(self):
        ReallyLongClassName.static_var += 1

Is there some way to access the static variable using the self variable?

I'd rather do something like class(self).static_var += 1, because long names are unreadable.

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
Jacklynn
  • 1,503
  • 1
  • 13
  • 23

2 Answers2

77

Use self.__class__.classAttr. This should work for both old & new style classes.

Judah Meek
  • 565
  • 7
  • 16
  • 2
    Is this somehow better than `type(self)`? – Alexey Feb 07 '18 at 14:56
  • @Alexey see the comments on https://stackoverflow.com/a/25577642/5241481 – Judah Meek Feb 08 '18 at 08:12
  • 16
    Please don't get convinced by upvotes, `self.__class__` is very un-pythonic, and using `type(self)` should be the way to do it (if you don't want the classname hardcoded) as illustrated by https://stackoverflow.com/a/25577642/860421. The only case to be cautious in is when you're accessing superclass's attribute in an inherited class & the attribute name starts (but not ends) with `__`, as name mangling will haunt you. In any case, using hardcoded classname is the safest approach. – 0xc0de Oct 09 '18 at 11:26
71

The answer is "yes, but…"

The best way to understand is to actually try it:

>>> class RLCN:
...     static_var = 5
...     def method1(self):
...         RLCN.static_var += 1
...     def method2(self):
...         self.static_var += 1
>>> rlcn = RLCN()
>>> RLCN.static_var, rlcn.static_var
(5, 5)
>>> rlcn.static_var
5
>>> rlcn.method1()
>>> RLCN.static_var, rlcn.static_var
(6, 6)
>>> rlcn.method2()
>>> RLCN.static_var, rlcn.static_var
(6, 7)

What happened?

Well, accessing a class attribute through self works just fine. If there is no instance attribute of the same name, you get the class attribute.

But assigning to it will hide the class attribute with a new instance attribute of the same name. Which is probably not what you wanted.

Note that this means you can use class attributes as "default values" or "initial values" for instance attributes. But I'm not sure it's very Pythonic to do so; what's actually happening, and what a novice (especially someone coming from, e.g., C++11 or Java) thinks is happening, are very different.

(Things get slightly more complicated when you deal with descriptors, like methods or @propertys, but let's ignore that; in the simple case that you're discussing, it's not relevant.)


I'd rather do something like class(self).static_var += 1, because long names are unreadable.

You can, you just need to spell it right: type is the function that returns the type of any object. So:

type(self).static_var += 1

This has the added advantage of being dynamic (e.g., when you have multiple inheritance and don't know which side a @property comes from, you probably don't want to explicitly list a class name, for basically the same reason you want to use super() instead of explicitly calling a base class method).

This has the disadvantage of not working on old-style classes in Python 2.x, but then you shouldn't be using those anyway. Especially in classes that have a need for class attributes, because those are exactly the types you're often going to later want to add @classmethods, @propertys, etc. to, and none of that works with old-style classes (along with many other things). If you really need to handle old-style and new-style classes transparently for some reason, self.__class__ is works with old-style classes. I'm not sure it's guaranteed to work with new-style classes; the docs say that the return value of type(object) is "generally the same object as returned by object.__class__", but doesn't say under what conditions that "generally" is untrue. It's also documented as a special attribute "added by the implementation" for "several object types" in 3.x. In practice, I don't know of any cases where they're different in 3.x, and in 2.x, the most prominent case where they're different is old-style classes.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • It seems that `type(self).static_var += 1` works only in Python3. – Anton Savin Aug 30 '14 at 00:06
  • @AntonSavin: No, it works in Python 2 just fine. See it [here](http://repl.it/XrJ) in Python 2.7.2. – abarnert Aug 30 '14 at 00:36
  • 3
    @AntonSavin: If you're using old-style classes, sure, `type` doesn't work, and all of the relevant benefits become irrelevant anyway, since `super`, `@property`, multiple inheritance, etc. don't work on old-style classes anyway. But the answer to that is simple: don't use old-style classes. But I'll add a comment to that effect. – abarnert Aug 30 '14 at 00:46
  • Rereading this, I'm wondering whether it's worth pointing out the similarities between self vs. class attributes and local vs. global/closure variables (without a `global` or `nonlocal` statement), or whether that would just potentially confuse people? – abarnert Sep 02 '14 at 17:37
  • 1
    Good explanation..the only drawback is see is if one changes the class name after creating the class, then all reference to the old class name in the class itself will need to be changed.... – repzero Feb 06 '17 at 00:43
  • 1
    Is there an accepted name for the promotion of class variables to instance variables on assignment? Googling for an explanation of what is happening in `method2` is tough. – bigh_29 May 29 '19 at 20:39
  • @bigh_29 `self.cls = type(self)` – Break Jan 24 '21 at 10:18