130

Consider the following code:

class Base(object):

    @classmethod
    def do(cls, a):
        print cls, a

class Derived(Base):

    @classmethod
    def do(cls, a):
        print 'In derived!'
        # Base.do(cls, a) -- can't pass `cls`
        Base.do(a)

if __name__ == '__main__':
    d = Derived()
    d.do('hello')

> $ python play.py  
> In derived! 
> <class '__main__.Base'> msg

From Derived.do, how do I call Base.do?

I would normally use super or even the base class name directly if this is a normal object method, but apparently I can't find a way to call the classmethod in the base class.

In the above example, Base.do(a) prints Base class instead of Derived class.

Vikrant
  • 4,920
  • 17
  • 48
  • 72
Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187

4 Answers4

145

If you're using a new-style class (i.e. derives from object in Python 2, or always in Python 3), you can do it with super() like this:

super(Derived, cls).do(a)

This is how you would invoke the code in the base class's version of the method (i.e. print cls, a), from the derived class, with cls being set to the derived class.

David Z
  • 128,184
  • 27
  • 255
  • 279
  • 8
    uh uh .. how come it never occured to me that I can use `super` on classmethods too. – Sridhar Ratnakumar Aug 12 '09 at 23:11
  • this only works (due to a limitation imposed by super) if the base derives from object, right? what do you do if that's not the case? – ars-longa-vita-brevis Jun 11 '14 at 19:06
  • Yeah, this only works for new-style classes, which derive from `object`. (at least in Python 2, but in Py3 I think all classes are new-style, IIRC) Otherwise you have to do `Base.do(self, ...)`, I think, thereby hard-coding the name of the superclass. – David Z Jun 11 '14 at 19:29
  • Inside `Derived.do()`, isn't `cls` the same as `Derived`? – Ray Oct 16 '17 at 17:01
  • @Ray If it is actually an instance of `Derived` but not of a subclass, then yes. – David Z Oct 16 '17 at 17:02
17

this has been a while, but I think I may have found an answer. When you decorate a method to become a classmethod the original unbound method is stored in a property named 'im_func':

class Base(object):
    @classmethod
    def do(cls, a):
        print cls, a

class Derived(Base):

    @classmethod
    def do(cls, a):
        print 'In derived!'
        # Base.do(cls, a) -- can't pass `cls`
        Base.do.im_func(cls, a)

if __name__ == '__main__':
    d = Derived()
    d.do('hello')
Nikolas
  • 1,269
  • 9
  • 10
3

Building on the answer from @David Z using:

super(Derived, cls).do(a)

Which can be further simplified to:

super(cls, cls).do(a)

I often use classmethods to provide alternative ways to construct my objects. In the example below I use the super functions as above for the class method load that alters the way that the objects are created:

class Base():
    
    def __init__(self,a):
        self.a = a
    
    @classmethod
    def load(cls,a):
        return cls(a=a)
    
class SubBase(Base): 

    @classmethod
    def load(cls,b):
        a = b-1
        return super(cls,cls).load(a=a)
    
base = Base.load(a=1)
print(base)
print(base.a)

sub = SubBase.load(b=3)
print(sub)
print(sub.a)

Output:

<__main__.Base object at 0x128E48B0>
1
<__main__.SubBase object at 0x128E4710>
2
Martin Alexandersson
  • 1,269
  • 10
  • 12
  • 1
    Replacing `super(Derived, cls).do(a)` with `super(cls, cls).do(a)` is not a good way. It will not work (it actually will cause an exception) when you have another `class SubSubBase(SubBase)` without `load()` method defined and you will try to use it. – Martin Grůber Feb 01 '21 at 09:33
  • @MartinGrůber: so what is the correct way in Python3? Simply `super().load(...)` will work correctly for classmethods? – MestreLion Oct 07 '21 at 12:06
  • @MestreLion Not sure what is the correct way but I would stay with `super(Derived, cls).do(a)` which works AFAIK. – Martin Grůber Oct 08 '21 at 13:07
-4

This works for me:

Base.do('hi')
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 9
    The `cls` argument will then be bound to `Base` instead of `Derived` – Sridhar Ratnakumar Aug 12 '09 at 23:14
  • what works for me is this - which looks (a lot) like Ned's answer: where self derives from QGraphicsView which has paintEvent(QPaintEvent) def paintEvent (self, qpntEvent): print dir(self) QGraphicsView.paintEvent(self, qpntEvent) – user192127 Mar 31 '12 at 15:54