Питання Розуміння Python super () з __init __ () методами [дублікат]


На це питання вже є відповідь тут:

Я намагаюся зрозуміти використання super(). З огляду на це, можна створити і дитячі класи, і просто добре.

Мені цікаво дізнатись про фактичну різницю між наступними 2 дитячими класами.

class Base(object):
    def __init__(self):
        print "Base created"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()

ChildA() 
ChildB()

1982
2018-02-23 00:30


походження




Відповіді:


super() дозволяє уникнути прямого посилання на базовий клас, що може бути приємним. Але основна перевага набуває багаторазового спадкування, де всілякі веселі речі може відбутися. Див стандартні документи на супер якщо ви ще цього не зробили.

Зауважте, що Синтаксис змінився в Python 3.0: ви можете просто сказати super().__init__() замість super(ChildB, self).__init__() який IMO набагато краще.


1427
2018-02-23 00:37



Чи можете ви навести приклад super() використовується з аргументами? - Steven Vascellaro
Чи можете ви, будь ласка, пояснити super(ChildB, self).__init__() це, що робить ChildB і self маємо справу з супер - rimiro


Я намагаюся зрозуміти super()

Причина, яку ми використовуємо super це так, що дитячі класи, які можуть використовувати коефіцієнт багаторазового спадкування, називають правильну наступну функцію батьківського класу у порядку вирішення методу (MRO).

У Python 3 ми можемо називати це таким:

class ChildB(Base):
    def __init__(self):
        super().__init__() 

У Python 2 ми повинні використовувати його таким чином:

        super(ChildB, self).__init__()

Без супер, ви обмежені вашою здатністю використовувати багаторазове спадкування:

        Base.__init__(self) # Avoid this.

Я далі поясню нижче.

"Наскільки існує насправді в цьому коді ?:"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()
        # super().__init__() # you can call super like this in Python 3!

Основна відмінність цього коду полягає в тому, що ви отримаєте шар неорієнтованості в __init__ з super, який використовує поточний клас для визначення наступного класу __init__ шукати в МРО.

Я ілюструю цю різницю у відповідь на канонічне питання, як використовувати "супер" в Python?, що демонструє ін'єкція залежності і кооперативне багатове спадкування.

Якщо в Python не було super

Ось кодекс, який насправді тісно еквівалентний super (як він впроваджується в C, за винятком деяких перевірок і резервних дій, а також перекладених у Python):

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()             # Get the Method Resolution Order.
        check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
        while check_next < len(mro):
            next_class = mro[check_next]
            if '__init__' in next_class.__dict__:
                next_class.__init__(self)
                break
            check_next += 1

Пишеться трохи більше як рідний Python:

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
            if hasattr(next_class, '__init__'):
                next_class.__init__(self)
                break

Якщо б у нас не було super об'єкт, нам доведеться писати цей ручний код скрізь (або відтворити його!), щоб переконатися, що ми називаємо належним наступним методом в Order Order Resolution Resolution!

Як супер робить це в Python 3, не кажучи явно, який клас і екземпляр з методу, з якого він був викликаний?

Він отримує кадр стеків викликів і знаходить клас (імпліцитно зберігається як локальна вільна змінна, __class__, що робить функцію викликає закриття класу) і першим аргументом для цієї функції, яка повинна бути екземпляром або класом, який інформує його про те, який метод вирішення методу (MRO) використовувати.

Оскільки це вимагає першого аргументу для МРО, використовуючи super з статичними методами неможливо.

Критики інших відповідей:

super () дозволяє уникнути прямого посилання на базовий клас, що може бути приємним. . Але головна перевага - це багаторазове успадкування, де можуть трапитися всілякі веселі речі. Перегляньте стандартні документи супер, якщо ви ще цього не зробили.

Це досить ручна хвиляста і нам не багато говорить, але справа super це не уникати написання батьківського класу. Справа в тому, щоб виклик наступного методу в порядку замовлення методу (MRO). Це стає важливим у багаторічному спадщині.

Я поясню тут.

class Base(object):
    def __init__(self):
        print("Base init'ed")

class ChildA(Base):
    def __init__(self):
        print("ChildA init'ed")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("ChildB init'ed")
        super(ChildB, self).__init__()

І давайте створимо залежність, яку ми хочемо викликати після Дитини:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super(UserDependency, self).__init__()

Запам'ятай тепер ChildB використовує супер, ChildA не:

class UserA(ChildA, UserDependency):
    def __init__(self):
        print("UserA init'ed")
        super(UserA, self).__init__()

class UserB(ChildB, UserDependency):
    def __init__(self):
        print("UserB init'ed")
        super(UserB, self).__init__()

І UserA не викликає метод UserDependency:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

Але UserB, оскільки ChildB використовує superробить !:

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

Критика для ще однієї відповіді

Ні в якому разі не слід робити наступне, про що йдеться в наступній відповіді, оскільки ви обов'язково отримаєте помилки при підкласі ChildB:

        super(self.__class__, self).__init__() # Don't do this. Ever.

(Ця відповідь не є розумною або особливо цікавою, але, незважаючи на пряму критику в коментарях та понад 17 низах, відповідач наполягав на тому, щоб запропонувати його, поки якийсь редактор не вирішить проблему.)

Пояснення: ця відповідь запропонувала називати супер таким:

super(self.__class__, self).__init__()

Це повністю неправильно super дозволяє нам шукати наступного батька в MRO (див. перший розділ цієї відповіді) для дитячих класів. Якщо ти скажеш super ми перебуваємо в методі дочірнього екземпляра, він буде шукати наступний метод у рядку (ймовірно, це один), в результаті чого відбувається рекурсія, ймовірно, викликає логічний збій (у прикладі відповіді, це робиться) або RuntimeError коли перевищує глибину рекурсії.

>>> class Polygon(object):
...     def __init__(self, id):
...         self.id = id
...
>>> class Rectangle(Polygon):
...     def __init__(self, id, width, height):
...         super(self.__class__, self).__init__(id)
...         self.shape = (width, height)
...
>>> class Square(Rectangle):
...     pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

413
2017-11-25 19:00



Мені все-таки доведеться зайнятися цією проблемою super() функція, однак, ця відповідь, безумовно, найкраща з точки зору глибини та деталей. Я також дуже вдячний критиці всередині відповіді. Це також допомагає краще зрозуміти концепцію шляхом виявлення підводних каменів в інших відповідях. Дякую ! - Yohan Obadia
@ Аарон Холл, дякую за таку докладну інформацію. Я думаю, має бути ще один варіант, доступний (на всяку користь) наставникам, щоб назвати якусь відповідь невідповідною або неповною, якщо вони не надають правильної достатньої інформації. - hunch
Спасибі, це було дуже корисно. Критика поганого / неналежного використання була дуже прикладом того, чому і як використовувати супер - Xarses
Дуже добре пояснення Python Inheritance. Дякую - ioaniatr


Відзначено, що в Python 3.0+ можна використовувати

super().__init__() 

щоб зробити ваш дзвінок коротким і не вимагає прямого посилання на імена класів батьківського класу OR, що може бути корисним. Я просто хочу додати, що для Python 2.7 або нижче, можна отримати цю нечутливу поведінку, написавши self.__class__ замість назви класу, тобто

super(self.__class__, self).__init__()

Незважаючи на це, це порушує вимоги до super для будь-яких класів, які успадковують від вашого класу, де self.__class__ міг повернутися дитячий клас. Наприклад:

class Polygon(object):
    def __init__(self, id):
        self.id = id

class Rectangle(Polygon):
    def __init__(self, id, width, height):
        super(self.__class__, self).__init__(id)
        self.shape = (width, height)

class Square(Rectangle):
    pass

Ось я клас Square, який є підкласом Росії Rectangle. Скажімо, я не хочу писати окремий конструктор для Square тому що конструктор для Rectangle це досить добре, але з якихось причин я хочу реалізувати Квадрат, щоб я міг реалізувати якийсь інший метод.

Коли я створюю a Square використовуючи mSquare = Square('a', 10,10), Python закликає конструктора для Rectangle тому що я не дав Square його власний конструктор. Проте в конструкторі для Rectangle, виклик super(self.__class__,self) збирається повернути суперклас Росії mSquare, тому він закликає конструктора для Rectangle знову Так відбувається нескінченна петля, як зазначав @S_C. У цьому випадку, коли я біжу super(...).__init__() Я закликаю конструктора для Rectangle але оскільки я не даю аргументів, я отримаю помилку.


223
2017-10-08 20:08



Що дає ця відповідь super(self.__class__, self).__init__() не працює, якщо ви знову підклас, не надаючи нового __init__. Тоді у вас є нескінченна рекурсія. - glglgl
Ця відповідь є смішною. Якщо ви збираєтеся зловживати супер таким способом, ви також можете просто вказати основне ім'я класу. Менш неправильно, ніж це. Весь момент першого аргументу супер - це те, що він є ні обов'язково тип самоврядування. Будь ласка, прочитайте "rpettinger" (або перегляньте деякі його відео) "super supremely considered super". - Veky
Ярлик, показаний тут для Python 2, має підводні камені, які вже згадувалися. Не використовуйте це, або ваш код порушиться так, як ви не можете передбачити. Цей "зручний ярлик" розриває супер, але ви можете не зрозуміти це, поки ви не забудете багато часу на налагодження. Використовуйте Python 3, якщо супер дуже багатослівний. - Ryan Hiebert
Відредаговано відповідь. Вибачте, якщо цей зміна змінює значення 180 градусів, але тепер ця відповідь повинна мати певний сенс. - Tino
Що не має сенсу сказати комусь, що вони можуть робити те, що тривіально демонструється як невірний. Ви можете псевдонім echo до python. Ніхто ніколи не запропонував би це! - Aaron Hall♦


Супер не має побічних ефектів

Base = ChildB

Base()

працює як очікується

Base = ChildA

Base()

потрапляє в нескінченну рекурсію.


75
2017-11-27 23:26



The super сам по собі не має побічних ефектів, він просто створює проксі-об'єкт. Я не зовсім впевнений, про що на цю тему намагається сказати super проти прямого батьківського класу, хоча. - davidism
Заява "Супер не має побічних ефектів", не має сенсу в цьому контексті. Супер просто гарантує, що ми називаємо правильним методом наступного класу в порядку розподілу методів, тоді як іншим способом важко кодує наступний метод, який буде викликаний, що ускладнює кодове множення спадщини. - Aaron Hall♦


Тільки голова ... з Python 2.7, і я вірю з тих пір super() був представлений у версії 2.2, ви можете дзвонити тільки super()якщо один з батьків успадковує від класу, який в кінці кінців успадковує object (класи нового стилю)

Особисто, як і для коду python 2.7, я збираюся продовжувати користуватися BaseClassName.__init__(self, args) поки я насправді не отримаю переваги використання super().


68
2018-05-25 17:52



дуже хороший момент. Якщо ви не чітко згадуєте: class Base (object): тоді ви отримаєте помилку: "TypeError: must be type, not classobj" - andilabs
@andi Я отримав цю помилку на другий день, і я зрештою відмовився від спроби з'ясувати це. Я просто перебирався на iPython. Який приголомшливий кошмар поганого повідомлення про помилку, якщо це був код, я повинен був налагоджувати! - Two-Bit Alchemist


Насправді немає. super() дивиться на наступний клас у MRO (порядок вирішення методу, доступ до якого cls.__mro__), щоб викликати методи. Просто зателефонувавши до бази __init__ називає базу __init__. Як це трапляється, MRO має рівно один предмет - база. Отже, ви дійсно робите те саме саме, але краще super() (особливо, якщо ви потрапите в багаторазові спадщини пізніше).


46
2018-02-23 00:34



Я бачу. Не могли б ви докладно розібратися в тому, чому краще використовувати супер () з багаторазовим спадком? Мені база .__ init __ (себе) коротша (чиста). Якщо б у мене було два базових класів, це було б два з цих рядків або два супер () лінії. Чи я неправильно зрозумів, що ви мали на увазі під "кращим"? - Mizipzor
Фактично, це буде одна супер () лінія. Якщо у вас є багаторазове спадкування, MRO залишається рівним. Таким чином, перший виклик super () .__ init__ викликає наступний клас в цьому, який потім називає наступним і так далі. Ви повинні дійсно перевірити деякі документи на це. - Devin Jeanpierre
Дитячий клас MRO також містить об'єкт - MRO клас видно в МРО класова мінлива - James Brady
Також зауважте, що класичні класи (попередня 2.2) не підтримують супер - ви повинні явно посилатися на базові класи. - James Brady
"Дочірній клас MRO містить також об'єкт - MRO класу видно в МРО Класна мінлива ". Це великий оуп. - Devin Jeanpierre


Головна відмінність полягає в тому, що ChildA.__init__ буде беззастережно викликати Base.__init__ в той час як ChildB.__init__ зателефоную __init__ в який клас буває ChildB предка в selfросійська лінія предків (що може відрізнятися від того, що ви очікуєте).

Якщо ви додаєте ClassC що використовує багаторазове спадкування:

class Mixin(Base):
  def __init__(self):
    print "Mixin stuff"
    super(Mixin, self).__init__()

class ChildC(ChildB, Mixin):  # Mixin is now between ChildB and Base
  pass

ChildC()
help(ChildC) # shows that the the Method Resolution Order is ChildC->ChildB->Mixin->Base

потім Base більше не є батьком ChildB за ChildC випадки Зараз super(ChildB, self) вкажемо на Mixin якщо self це ChildC екземпляр

Ви вставили Mixin між ChildB і Base. І ви можете скористатися цим super()

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

The супер вважається суперпост і Pycon 2015 супровідне відео пояснити це досить добре.


22
2017-09-21 06:41



Це. Значення super(ChildB, self) зміни залежно від МО об'єкта, про який йде мова self, який не може бути відомий до часу виконання. Іншими словами, автор книги ChildB не має ніякого способу дізнатися що super() буде вирішено у всіх випадках, якщо вони не зможуть гарантувати це ChildB ніколи не буде підкласуватися. - nispio