Подтвердить что ты не робот

Python - лучший метод вызова суперкласса?

Все время, которое я использовал:

SuperClass.__init__(self, *args, **kwargs)

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

Однако другие коды, с которыми я столкнулся, используют

super(MyClass, self).__init__(*args, **kwargs)

вместо.

Это может стать неоднозначным, если оно используется в:

class MyClass(SuperClass1, SuperClass2):
    def __init__(self, *args, **kwargs):
        super(MyClass, self).__init__(*args, **kwargs) #which SuperClass is being used?

Я хотел бы знать, почему эта форма призвания широко принята? Любое преимущество вообще?

4b9b3361

Ответ 1

Причина, по которой super является предпочтительной для современных (новых стилей) классов, заключается в том, что она позволяет совместное множественное наследование. Вот пример.

>>> class Foo(object):
...     def display(self):
...         print "In Foo"
... 
>>> class Foo2(Foo):
...     def display(self):
...         print "In Foo2"
...         super(Foo2, self).display()
...         print "Back in Foo2"
... 
>>> class Bar(Foo):
...     def display(self):
...         print "In Bar"
...         super(Bar, self).display()
...         print "Back in Bar"
... 
>>> class FooBar(Foo2, Bar):
...     pass
... 
>>> FooBar().display()
In Foo2
In Bar
In Foo
Back in Bar
Back in Foo2
>>> class BarFoo(Bar, Foo2):
...     pass
... 
>>> BarFoo().display()
In Bar
In Foo2
In Foo
Back in Foo2
Back in Bar

Обратите внимание, что я не делал ничего, чтобы изменить метод display на суперклассах, но у меня были разные методы display в подклассах, изменив порядок, в котором я упорядочил суперклассы. BarFoo и FooBar имеют разные методы. Это связано с тем, что они имеют разные приемы разрешения методов

>>> BarFoo.__mro__
(<class '__main__.BarFoo'>, <class '__main__.Bar'>, <class '__main__.Foo2'>, <class '__main__.Foo'>, <type 'object'>)
>>> FooBar.__mro__
(<class '__main__.FooBar'>, <class '__main__.Foo2'>, <class '__main__.Bar'>, <class '__main__.Foo'>, <type 'object'>)

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

Ответ 2

Для новых классов стилей, которые наследуются от object, используется super.

Атрибут __mro__ (порядок разрешения метода) типа перечисляет порядок поиска разрешения метода, используемый super.

>>> class X(object):
    pass

>>> class Y(object):
    pass

>>> class Z(X, Y):
    pass

>>> Z.__mro__
(<class '__main__.Z'>, <class '__main__.X'>, <class '__main__.Y'>, <type 'object'>)

Это указывает порядок для Z. Так как он Z(X, Y), X является первым в иерархии. Если бы он был Z(Y, X), Y превысил бы X.

Для классов старого стиля используется SuperClass.__init__(self, *args, **kwargs).

UPDATE:

По вопросу о том, какой SuperClass используется.

>>> class First(object):
    pass

>>> class Second(object):
    def __init__(self, *args, **kwargs):
        print 'Second __init__ called'

>>> class MInherit(First, Second):
    def __init__(self):
        super(MInherit, self).__init__()

>>> i = MInherit()
Second __init__ called

Во-первых, First будет проверяться, есть ли у него __init__, так как First входит первым в MRO. Так как в этом случае "First" не имеет __init__, поэтому вызывается Second. Если бы в First был __init__, только это было бы вызвано.

Ответ 3

В дополнение к хорошим ответам, уже опубликованным, вот дополнительная информация:

  • Классы старого стиля (те, которые не выводятся из объекта) имеют порядок разрешения метода глубины. Классы старого стиля называют их суперклассы так, как вы привыкли.

  • Классы нового стиля (те, которые производятся от объекта) имеют более сложный порядок разрешения метода. На самом высоком уровне он сначала сродни ширине, но сложнее. См. Эту страницу для отличного объяснения. Использование "super()" позволяет новым классам стиля следовать этому порядку разрешения метода.

  • Если вы используете классы нового стиля, вы все равно можете использовать старый стиль вызовов суперклассов, и ваш код будет работать. Различия становятся очевидными только для более сложных моделей множественного наследования.