Вызов метода класса базового класса в Python

Рассмотрим следующий код:

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

От Derived.do, как мне позвонить Base.do?

Обычно я бы использовал super или даже имя базового класса напрямую, если это обычный метод объекта, но, видимо, я не могу найти способ вызвать метод класса в базовом классе.

В приведенном выше примере Base.do(a) печатает класс Base вместо класса Derived.


person Sridhar Ratnakumar    schedule 12.08.2009    source источник
comment


Ответы (4)


Если вы используете класс нового стиля (т. е. производный от object в Python 2 или всегда в Python 3), вы можете сделать это с super() следующим образом:

super(Derived, cls).do(a)

Вот как вы будете вызывать код в версии метода базового класса (т. е. print cls, a) из производного класса, при этом cls будет установлен в производный класс.

person David Z    schedule 12.08.2009
comment
эээ... почему мне никогда не приходило в голову, что я могу использовать super и в методах классов. - person Sridhar Ratnakumar; 13.08.2009
comment
это работает только (из-за ограничения, наложенного super), если база происходит от объекта, верно? что делать, если это не так? - person ars-longa-vita-brevis; 11.06.2014
comment
Да, это работает только для классов нового стиля, производных от object. (по крайней мере, в Python 2, но в Py3 я думаю, что все классы нового стиля, IIRC). В противном случае вам придется сделать Base.do(self, ...), я думаю, тем самым жестко закодировав имя суперкласса. - person David Z; 11.06.2014
comment
Внутри Derived.do() разве cls не совпадает с Derived? - person Ray; 16.10.2017
comment
@Ray Если это на самом деле экземпляр Derived, но не подкласса, то да. - person David Z; 16.10.2017

это было какое-то время, но я думаю, что, возможно, нашел ответ. Когда вы украшаете метод, чтобы он стал методом класса, исходный несвязанный метод сохраняется в свойстве с именем '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')
person Nikolas    schedule 15.04.2011
comment
Примечание. Этот подход работает для классов старого стиля, где super() не работает. - person Alex Q; 03.06.2011
comment
Также доступен как __func__ в Python 2.7 и 3. - person dtheodor; 30.04.2014

Основываясь на ответе @David Z, используя:

super(Derived, cls).do(a)

Что можно еще упростить до:

super(cls, cls).do(a)

Я часто использую методы классов, чтобы предоставить альтернативные способы создания моих объектов. В приведенном ниже примере я использую суперфункции, как указано выше, для загрузки метода класса, который изменяет способ создания объектов:

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)

Выход:

<__main__.Base object at 0x128E48B0>
1
<__main__.SubBase object at 0x128E4710>
2
person Martin Alexandersson    schedule 13.10.2020
comment
Замена super(Derived, cls).do(a) на super(cls, cls).do(a) не лучший способ. Это не сработает (на самом деле это вызовет исключение), когда у вас есть другой class SubSubBase(SubBase) без определенного метода load(), и вы попытаетесь его использовать. - person Martin Grůber; 01.02.2021

Это работает для меня:

Base.do('hi')
person Ned Batchelder    schedule 12.08.2009
comment
Затем аргумент cls будет привязан к Base вместо Derived. - person Sridhar Ratnakumar; 13.08.2009
comment
что работает для меня, так это то, что выглядит (много) как ответ Неда: где self происходит от QGraphicsView, у которого есть paintEvent (QPaintEvent) def paintEvent (self, qpntEvent): print dir (self) QGraphicsView.paintEvent (self, qpntEvent) - person user192127; 31.03.2012