16

Let's say I have a class

class SimpleGenerator(object):
    @classmethod
    def get_description(cls):
        return cls.name

class AdvancedGenerator(SimpleGenerator):
    @classmethod
    def get_description(cls):
        desc = SimpleGenerator.get_description() # this fails
        return desc + ' Advanced(tm) ' + cls.adv_feature

Now I have extended each of the above classes to have a concrete one of each:

class StringGenerator(SimpleGenerator)
    name = 'Generates strings'
    def do_something():
        pass

class SpaceShuttleGenerator(AdvancedGenerator)
    name = 'Generates space shuttles'
    adv_feature = ' - builds complicated components'
    def do_something():
        pass

Now let's say I call

SpaceShuttleGenerator.get_description()

The issue is that in AdvancedGenerator I want to call the method in SimpleGenerator passing along an instance of the class, specifically SpaceShuttleGenerator. Can this be done?

NOTE: The example is simplified, as my concrete example is more involved. Let's say that my goal is not to concatenate strings.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Krystian Cybulski
  • 10,789
  • 12
  • 67
  • 98
  • Is this python 2 or 3? If 2, are you inheriting from `object`? – Martijn Pieters Mar 08 '13 at 10:03
  • I'm confused by your using subclasses as instances. They're not the same thing at all. – Daniel Roseman Mar 08 '13 at 10:05
  • @Martjin: python 2, inheriting object. Fixing examples. – Krystian Cybulski Mar 08 '13 at 10:07
  • @DanielRoseman The concrete `SpaceShuttleGenerator` inherits from what in Java I would consider an abstract class `AdvancedGenerator`, which inherits from another abstract class `SimpleGenerator`. – Krystian Cybulski Mar 08 '13 at 10:09
  • @KoliberServices: Hrm; beware of using Java analogies. [Python is not Java](http://dirtsimple.org/2004/12/python-is-not-java.html), don't fall into the traps of trying to bring Java idioms over to Python. – Martijn Pieters Mar 08 '13 at 10:11
  • @MartijnPieters Thanks for the tip. Unfortunately years of Java have ingrained themselves on my problem solving approaches. How would I accomplish something akin to abstract classes and concrete classes, where the concrete ones can call the abstract ones? – Krystian Cybulski Mar 08 '13 at 10:13
  • @KoliberServices: Inheritance is fine, although perhaps you were looking for [ABCs (abstract base classes)](http://docs.python.org/2/library/abc.html) instead? – Martijn Pieters Mar 08 '13 at 10:16
  • Possible duplicate of [Calling a base class's classmethod in Python](https://stackoverflow.com/questions/1269217/calling-a-base-classs-classmethod-in-python) – Mr_and_Mrs_D Nov 03 '17 at 12:11

1 Answers1

16

Use super():

@classmethod
def get_description(cls):
    desc = super(AdvancedGenerator, cls).get_description()
    return desc + ' Advanced(tm) ' + cls.adv_feature

The difference between using SimpleGenerator.get_description() and super(AdvancedGenerator, cls).get_description() is what cls will be set to. When calling the class directly, cls is set to SimpleGenerator, using super(), cls will refer to AdvancedGenerator.

Compare your code (adjusted to use __name__ to illustrate the difference):

>>> class SimpleGenerator(object):
...     @classmethod
...     def get_description(cls):
...         return cls.__name__
... 
>>> class AdvancedGenerator(SimpleGenerator):
...     @classmethod
...     def get_description(cls):
...         desc = SimpleGenerator.get_description() 
...         return desc + ' Advanced(tm)'
... 
>>> AdvancedGenerator.get_description()
'SimpleGenerator Advanced(tm)'

and using super():

>>> class AdvancedGenerator(SimpleGenerator):
...     @classmethod
...     def get_description(cls):
...         desc = super(AdvancedGenerator, cls).get_description()
...         return desc + ' Advanced(tm)'
... 
>>> AdvancedGenerator.get_description()
'AdvancedGenerator Advanced(tm)'
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Can I make class method for all my derived classes that will print own class name (not classname of instance)? I mean `A2 = AdvancedGenerator2(AdvancedGenerator) A2.get_description()` and return: `AdvancedGenerator2 AdvancedGenerator SimpleGenerator`? – Crusader Mar 22 '17 at 21:31
  • @Crusader: You mean you want the names of all classes in the MRO? `return ' '.join([c.__name__ for c in cls.__mro__])`. – Martijn Pieters Mar 23 '17 at 09:49
  • No, I mean how to get a current class variable somewhere in the class tree. Example classes: Grandparent->Parent->Child, every class has some class var 'test', and I need to get concat of all this vars. In other language I can do like this `result = self.var + super().get_var()` – Crusader Mar 24 '17 at 13:20
  • @Crusader: `super().get_var()` would not be a class variable. Sorry, you are still using terms too vague to let me help you with a specific answer. `super()` allows you to access class attributes just as much as methods; `super().var` gives you the class variable from the parent class in the MRO. If you need *all* variables in the hierarchy, iterate over the `__mro__` sequence. – Martijn Pieters Mar 25 '17 at 08:24
  • what if it has parameter? can i just write it as def get_description(cls,params): ? – greendino Mar 14 '21 at 01:09