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


606

Створюючи просту ієрархію об'єктів у 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

Відповіді:


735

Так, але тільки з новими класами . Використовуйте 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", коли я це роблю
Chris F

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

143

Також 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
Кращий приклад, ніж Адамовий, тому що ви демонстрували використання нового стилю з базовим класом об'єкта. Просто не вистачає посилання на нові класи.
Самуїл

82
ImmediateParentClass.frotz(self)

буде просто чудово, незалежно від того, чи визначив безпосередній батьківський клас frotzсебе чи успадкував його. superпотрібен лише для належної підтримки багаторазового успадкування (і тоді він працює лише в тому випадку, якщо кожен клас використовує його правильно). Загалом, AnyClass.whateverбудуть шукати whateverв AnyClassпредках «S , якщо AnyClassне визначені / перевизначити його, і це справедливо для" дочірнього класу виклику методу батька " , як і для будь-якого іншого появи!


Це передбачає, що ImmediateParentClass також повинен бути присутнім в обсязі для виконання цього твердження. Просто наявність об'єкта, імпортованого з модуля, не допоможе.
Тушар Вазірані

що робити, якщо це метод класу?
Шиплу Мокаддім

@TusharVazirani, якщо батьківський клас є безпосереднім parent, він охоплює весь клас, чи не так?
Ярослав Нікітенко

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

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

74

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. Дякую за пропозицію, хоча.
jjohn

45

Багато відповідей пояснювали, як викликати метод від батька, який був переохолоджений у дитини.

Однак

"як ви називаєте метод батьківського класу з дочірнього класу?"

також може означати:

"як ви називаєте успадковані методи?"

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

наприклад, у python 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", і ОП написано з точки зору когось нового в python.

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


@gizzmole в якій ситуації це не працює? Я радий додати будь-які застереження чи попередження, які є актуальними, але я не зміг зрозуміти, наскільки посилання, яке ви опублікували, стосувалося цієї відповіді.
Бен

@Ben Вибачте, я недостатньо уважно прочитав вашу відповідь. Ця відповідь не відповідає на питання про те, як перевантажити функції, але відповідає на інше питання. Я видалив свій початковий коментар.
gizzmole

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

@ Що робити, якщо я хочу викликати B (). Baz () з іншого класу з назвою "C", використовуючи параметри, визначені в C ?. Я спробував зробити B (). Baz () в класі C, де self.string - це ще один val. Це не дало мені нічого, коли я стратив
Joey

@joey Я не впевнений, що ти намагаєшся робити, або якщо це входить в рамки цього питання. Рядок - це локальна змінна - налаштування self.stringнічого не зробить. Однак у класі C ви все ще можете подзвонитиB().bar(string)
Бен,

26

Ось приклад використання 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 ()
Jaykul

13

У Python також є супер (). Це трохи примхливо через класи старого та нового стилю Python, але досить часто використовується, наприклад, у конструкторах:

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

9

Я б рекомендував використовувати 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 . Зробити перший запис у mro безпечніше, ніж відстежувати, який запис у базах використовувати. Багатократне успадкування в Python сканує деякі речі назад, а інші - вперед, тож дозволяючи мові розгортати процес, це найбезпечніше.
Джон Джей Обермарк

7

Якщо ви не знаєте, скільки аргументів ви можете отримати, і хочете передати їх також і дитині:

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

(Від: Python - найпростіший спосіб переохочення __init__, коли необов'язковий kwarg повинен бути використаний після виклику super ()? )


4

У 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()) 

Цей приклад схожий на пояснений вище. Однак є одна різниця, що супер не має жодних аргументів, переданих йому. Цей вище код виконується у версії python 3.4.


2

У цьому прикладі 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

1

У Python 2 у мене не було великої долі з super (). Я використовував відповідь від jimifiki на цій темі SO як звернутися до батьківського методу в 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(): тоді super () буде працювати
blndev

Я не впевнений, що слідкую Ви маєте на увазі під визначенням класу? Очевидно, ви повинні якось вказати, що клас має батьків, щоб подумати, що ви могли б посилатися на батьків! Оскільки я опублікував це 9 місяців тому, я трохи нечіткі деталі, але, думаю, я мав на увазі, що приклади з Аруна Гоша, Лоуренса, Kkk тощо не застосовуються в Py 2.x. Натомість, ось метод, який працює, і простий спосіб звернутися до батьків, щоб було трохи зрозуміліше, що ви робите. (на кшталт використання super)
BuvinJ

-1
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()

-4
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__ не працює з супер, оскільки 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.