Вызовите метод родительского класса из дочернего класса?


608

При создании простой иерархии объектов в Python я хотел бы иметь возможность вызывать методы родительского класса из производного класса. В Perl и Java есть ключевое слово для этого ( super). В Perl я мог бы сделать это:

package Foo;

sub frotz {
    return "Bamf";
}

package Bar;
@ISA = qw(Foo);

sub frotz {
   my $str = SUPER::frotz();
   return uc($str);
}

В Python кажется, что я должен явно назвать родительский класс от дочернего. В приведенном выше примере мне нужно сделать что-то вроде Foo::frotz().

Это кажется неправильным, поскольку такое поведение затрудняет создание глубоких иерархий. Если дети должны знать, какой класс определяет унаследованный метод, то создается всякая информационная боль.

Это фактическое ограничение в Python, пробел в моем понимании или оба?


3
Я думаю, что наименование родительского класса не такая уж плохая идея. Это может помочь, когда дочерний класс наследует от более чем одного родителя, поскольку вы явно называете родительский класс.

7
Хотя возможность присвоить имя классу - не плохая идея, быть вынужденным сделать это, безусловно, есть.
Johndodo

Помните об изменениях в супер-обработке между Python 2 и Python 3 https://www.python.org/dev/peps/pep-3135/ . Названия классов больше не требуются (хотя, по крайней мере, иногда это может быть идеей бога).
jwpfox

Ответы:


737

Да, но только с классами нового стиля . Используйте super()функцию:

class Foo(Bar):
    def baz(self, arg):
        return super().baz(arg)

Для python <3 используйте:

class Foo(Bar):
    def baz(self, arg):
        return super(Foo, self).baz(arg)

53
Разве вы не можете также сделать «вернуть Bar.baz (self, arg)»? Если так, что было бы лучше?

15
Да, можно, но ОП спрашивал, как это сделать, не указав явно имя суперкласса на сайте вызовов (в данном случае Bar).
Адам Розенфилд

7
super () странно с множественным наследованием. Звонит в «следующий» класс. Это может быть родительский класс или какой-то совершенно не связанный класс, который появляется в другом месте иерархии.
Стив Джессоп

6
Я использую Python 2.x, и я получаю «Ошибка: должен быть тип, а не classobj», когда я делаю это
Крис F

28
Синтаксис super(Foo, self)для Python 2.x правильный? В Python 3.x super()предпочтительным является правильный?
Тревор Бойд Смит

144

Python также имеет супер :

super(type[, object-or-type])

Возвратите прокси-объект, который делегирует вызовы метода родительскому или родственному классу типа. Это полезно для доступа к унаследованным методам, которые были переопределены в классе. Порядок поиска такой же, как в getattr (), за исключением того, что сам тип пропускается.

Пример:

class A(object):     # deriving from 'object' declares A as a 'new-style-class'
    def foo(self):
        print "foo"

class B(A):
    def foo(self):
        super(B, self).foo()   # calls 'A.foo()'

myB = B()
myB.foo()

7
Лучший пример, чем у Адама, потому что вы продемонстрировали использование класса нового стиля с базовым классом объекта. Просто отсутствует ссылка на классы нового стиля.
Самуил

83
ImmediateParentClass.frotz(self)

будет хорошо, независимо от того, определил ли родительский класс frotzсебя или унаследовал его. superнужен только для правильной поддержки множественного наследования (и тогда он работает только в том случае, если каждый класс использует его правильно). В общем, AnyClass.whateverсобирается искать whateverв AnyClassпредках России, если AnyClassне определит / не переопределит его, и это верно для «дочернего класса, вызывающего метод родителя», как и для любого другого случая!


Это требует, чтобы ImmediateParentClass также присутствовал в области действия для этого оператора, который будет выполнен. Простое импортирование объекта из модуля не поможет.
Тушар Вазирани

что если его метод класса?
Шиплу Мокаддим

@TusharVazirani, если родительский класс является непосредственным parent, он входит в сферу действия всего класса, не так ли?
Ярослав Никитенко

1
@YaroslavNikitenko Я говорю об импорте экземпляра, а не класса.
Тушар Вазирани

1
@TusharVazirani теперь я понимаю, спасибо. Я думал о вызове из метода текущего класса (как было указано в ОП).
Ярослав Никитенко

75

Python 3 имеет другой и более простой синтаксис для вызова родительского метода.

Если Fooкласс наследует от Bar, то из Bar.__init__можно вызвать из Fooчерез super().__init__():

class Foo(Bar):

    def __init__(self, *args, **kwargs):
        # invoke Bar.__init__
        super().__init__(*args, **kwargs)

5
Приятно слышать, что Python 3 имеет лучшее решение. Вопрос исходит от Python 2.X. Спасибо за предложение, хотя.
Джоан

46

Во многих ответах объясняется, как вызвать метод из родительского объекта, который был переопределен в дочернем элементе.

Однако

"Как вы вызываете метод родительского класса из дочернего класса?"

может также просто означать:

"Как вы называете унаследованные методы?"

Вы можете вызывать методы, унаследованные от родительского класса, так же, как если бы они были методами дочернего класса, если они не были перезаписаны.

например, в питоне 3:

class A():
  def bar(self, string):
    print("Hi, I'm bar, inherited from A"+string)

class B(A):
  def baz(self):
    self.bar(" - called by baz in B")

B().baz() # prints out "Hi, I'm bar, inherited from A - called by baz in B"

да, это может быть довольно очевидно, но я чувствую, что, не указывая на это, люди могут покинуть эту ветку с впечатлением, что вы должны перепрыгивать через нелепые обручи только для доступа к унаследованным методам в python. Тем более, что этот вопрос высоко ценится в поисках «как получить доступ к методу родительского класса в Python», а OP написан с точки зрения кого-то, кто плохо знаком с Python.

Я обнаружил: https://docs.python.org/3/tutorial/classes.html#inheritance полезный для понимания того, как вы получаете доступ к унаследованным методам.


@gizzmole, в какой ситуации это не работает? Я рад добавить любые предостережения или предупреждения, которые имеют отношение к делу, но я не мог понять, насколько ссылка, которую вы разместили, была связана с этим ответом.
Бен

@ Бен Извините, я недостаточно внимательно прочитал ваш ответ. Этот ответ не отвечает на вопрос о том, как перегрузить функции, но отвечает на другой вопрос. Я удалил свой первоначальный комментарий.
gizzmole

@ gizzmole ах, не беспокойся. Я понимаю, что этот ответ не относится к функциям перегрузки, однако в этом вопросе явно не упоминается перегрузка (хотя в приведенном примере perl показан случай перегрузки). Подавляющее большинство других ответов касается перегрузки - этот предназначен для тех людей, которым он не нужен.
Бен

@ Что делать, если я хочу вызвать B (). Baz () из другого класса с именем 'C', используя переменные, определенные в C ?. Я попытался выполнить B (). Baz () в классе C, где self.string - это еще одно значение. Это не дало мне ничего, когда я казнил
Джои

@ Joey Я не уверен, что вы пытаетесь сделать, или если это входит в сферу этого вопроса. Строка является локальной переменной - установка self.stringничего не сделает. В классе C вы все равно можете позвонитьB().bar(string)
Бен

27

Вот пример использования super () :

#New-style classes inherit from object, or from another new-style class
class Dog(object):

    name = ''
    moves = []

    def __init__(self, name):
        self.name = name

    def moves_setup(self):
        self.moves.append('walk')
        self.moves.append('run')

    def get_moves(self):
        return self.moves

class Superdog(Dog):

    #Let's try to append new fly ability to our Superdog
    def moves_setup(self):
        #Set default moves by calling method of parent class
        super(Superdog, self).moves_setup()
        self.moves.append('fly')

dog = Superdog('Freddy')
print dog.name # Freddy
dog.moves_setup()
print dog.get_moves() # ['walk', 'run', 'fly']. 
#As you can see our Superdog has all moves defined in the base Dog class

Разве это не плохой пример? Я считаю, что не совсем безопасно использовать Super, если вся ваша иерархия не является классами «нового стиля» и не вызывает должным образом Super ().
Init

14

В Python тоже есть супер (). Это немного шатко из-за старых и новых стилей Python, но довольно часто используется, например, в конструкторах:

class Foo(Bar):
    def __init__(self):
        super(Foo, self).__init__()
        self.baz = 5

10

Я бы порекомендовал использовать CLASS.__bases__ что-то вроде этого

class A:
   def __init__(self):
        print "I am Class %s"%self.__class__.__name__
        for parentClass in self.__class__.__bases__:
              print "   I am inherited from:",parentClass.__name__
              #parentClass.foo(self) <- call parents function with self as first param
class B(A):pass
class C(B):pass
a,b,c = A(),B(),C()

1
Имейте в виду, что, как вы можете сказать из объема ссылок на супер здесь, что люди действительно не хотят использовать основы, если вы не делаете что-то действительно глубокое. Если тебе не нравится супер , посмотри на мро . Взять первую запись в mro безопаснее, чем отслеживать, какую запись в базах использовать. Множественное наследование в Python сканирует некоторые вещи в обратном направлении, а другие - в обратном направлении, так что позволить языку раскрутить процесс наиболее безопасно.
Джон Джей Обермарк

8

Если вы не знаете, сколько аргументов вы можете получить, и хотите передать их все также ребенку:

class Foo(bar)
    def baz(self, arg, *args, **kwargs):
        # ... Do your thing
        return super(Foo, self).baz(arg, *args, **kwargs)

(From: Python - Самый чистый способ переопределить __init__, где необязательный kwarg должен использоваться после вызова super ()? )


5

В python также есть супер ().

Пример того, как метод суперкласса вызывается из метода подкласса

class Dog(object):
    name = ''
    moves = []

    def __init__(self, name):
        self.name = name

    def moves_setup(self,x):
        self.moves.append('walk')
        self.moves.append('run')
        self.moves.append(x)
    def get_moves(self):
        return self.moves

class Superdog(Dog):

    #Let's try to append new fly ability to our Superdog
    def moves_setup(self):
        #Set default moves by calling method of parent class
        super().moves_setup("hello world")
        self.moves.append('fly')
dog = Superdog('Freddy')
print (dog.name)
dog.moves_setup()
print (dog.get_moves()) 

Этот пример похож на тот, который описан выше. Однако есть одно отличие, что у super нет аргументов, переданных ему. Этот код выполняется в версии Python 3.4.


3

В этом примере cafec_paramэто базовый класс (родительский класс) и abcдочерний класс. abcвызывает AWCметод в базовом классе.

class cafec_param:

    def __init__(self,precip,pe,awc,nmonths):

        self.precip = precip
        self.pe = pe
        self.awc = awc
        self.nmonths = nmonths

    def AWC(self):

        if self.awc<254:
            Ss = self.awc
            Su = 0
            self.Ss=Ss
        else:
            Ss = 254; Su = self.awc-254
            self.Ss=Ss + Su   
        AWC = Ss + Su
        return self.Ss


    def test(self):
        return self.Ss
        #return self.Ss*4

class abc(cafec_param):
    def rr(self):
        return self.AWC()


ee=cafec_param('re',34,56,2)
dd=abc('re',34,56,2)
print(dd.rr())
print(ee.AWC())
print(ee.test())

Вывод

56

56

56

2

В Python 2 мне не очень повезло с super (). Я использовал ответ от jimifiki в этой теме, как ссылаться на родительский метод в python?, Затем я добавил к этому свой небольшой поворот, который, я думаю, является улучшением юзабилити (особенно если у вас длинные имена классов).

Определите базовый класс в одном модуле:

 # myA.py

class A():     
    def foo( self ):
        print "foo"

Затем импортируйте класс в другие модули as parent:

# myB.py

from myA import A as parent

class B( parent ):
    def foo( self ):
        parent.foo( self )   # calls 'A.foo()'

2
Вы должны использовать class A(object): вместо class A(): Тогда супер () будет работать
blndev

Я не уверен, что следую. Вы имеете в виду в определении класса? Очевидно, вы должны указать, что у класса есть родитель, как-то, чтобы думать, что вы можете сослаться на родителя! С тех пор, как я опубликовал это 9 месяцев назад, я немного неясен в деталях, но я думаю, что имел в виду примеры от Аруна Гоша, Лоуренса, Ккка и т. Д., Которые не применимы в Py 2.x. Вместо этого, вот метод, который работает и простой способ обратиться к родителю, так что немного более понятно, что вы делаете. (как с использованием супер)
BuvinJ

0
class department:
    campus_name="attock"
    def printer(self):
        print(self.campus_name)

class CS_dept(department):
    def overr_CS(self):
        department.printer(self)
        print("i am child class1")

c=CS_dept()
c.overr_CS()

-3
class a(object):
    def my_hello(self):
        print "hello ravi"

class b(a):
    def my_hello(self):
    super(b,self).my_hello()
    print "hi"

obj = b()
obj.my_hello()

2
Добро пожаловать в stackoverflow. На этот вопрос уже подробно отвечали почти десять лет назад, ответ принят и в целом одобрен, и ваш ответ не добавляет ничего нового. Вы можете попытаться ответить на более свежие (и предпочтительно без ответа) вопросы. Как примечание, в редакторе есть кнопка, чтобы применить правильное форматирование кода.
Bruno Desthuilliers

-24

Это более абстрактный метод:

super(self.__class__,self).baz(arg)

15
self .__ class__ не работает правильно с super, поскольку self всегда является экземпляром, а его класс всегда является классом экземпляра. Это означает, что если вы используете super (self .__ class__ .. в классе и этот класс наследуется, он больше не будет работать.
xitrium

16
Просто чтобы подчеркнуть, не используйте этот код. Это побеждает всю цель super (), которая состоит в том, чтобы правильно пройти MRO.
Eevee

1
stackoverflow.com/a/19257335/419348 сказал почему бы не позвонить super(self.__class__, self).baz(arg).
AechoLiu
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.