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


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.