Расширение Python с помощью super () Python 3 против Python 2


103

Изначально я хотел задать этот вопрос , но потом обнаружил, что об этом уже думали раньше ...

Погуглив, я нашел этот пример расширения configparser . Следующее работает с Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

Но не с Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

Затем я прочитал немного о стилях Python New Class vs. Old Class (например, здесь . И теперь мне интересно, я могу:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

Но разве я не должен вызывать init? Это эквивалент в Python 2:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

1
В вашем примере вам не нужно определять __init__()в подклассе, если все, что он делает, это вызывает суперкласс __init__()(в Python 2 или 3) - вместо этого просто позвольте суперклассу унаследоваться.
Мартино

Полезная ссылка: amyboyle.ninja/Python-Inheritance
nu everest 03

Полезные ссылки с исправленной ссылкой: amyboyle.ninja/Python-Inheritance
fearless_fool

Ответы:


155
  • super()(без аргументов) был введен в Python 3 (вместе с __class__):

    super() -> same as super(__class__, self)

    так что это будет эквивалент Python 2 для классов нового стиля:

    super(CurrentClass, self)
  • для классов старого стиля вы всегда можете использовать:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)

8
-1. Этот ответ мне ничего не прояснил. В Python 2 super(__class__)дает NameError: global name '__class__' is not definedи также super(self.__class__)является ошибочным. Вы должны предоставить экземпляр в качестве второго аргумента, который предполагает, что вам нужно это сделать super(self.__class__, self), но это неверно . Если Class2наследуется от Class1и Class1вызовов super(self.__class__, self).__init__(), Class1«s __init__будет называть себя , когда инстанцирование экземпляра Class2.
jpmc26

Чтобы прояснить ситуацию, я получаю TypeError: super() takes at least 1 argument (0 given)при попытке вызвать super(self.__class__)Python 2. (Что не имеет большого смысла, но демонстрирует, сколько информации отсутствует в этом ответе.)
jpmc26

3
@ jpmc26: в python2 вы получите эту ошибку , потому что вы пытаетесь вызвать __init__()без аргумента несвязанного супер объекта (который вы получите по телефону super(self.__class__)только с одним аргументом), вам нужен связанный объект супер , то он должен работать: super(CurrentClass, self).__init__(). Не используйте, self.__class__потому что это всегда будет относиться к одному и тому же классу при вызове родителя и, следовательно, создать бесконечный цикл, если этот родитель также делает то же самое.
mata

__class__(член) также существует в Python2 .
CristiFati

3
@CristiFati Речь идет не о __class__члене, а о неявно созданном лексическом __class__замыкании, которое всегда относится к классу, определяемому в данный момент, которого нет в python2.
mata

49

В одном случае наследования (когда вы подклассифицируете только один класс) ваш новый класс наследует методы базового класса. Это включает __init__. Так что, если вы не определите его в своем классе, вы получите его из базы.

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

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

Если это так, то это также будет работать правильно (в Python 3, но вы можете переработать его в Python 2 - он также есть super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

Обратите внимание, как используются оба базовых класса, superхотя у них нет собственных базовых классов.

Что superделает: он вызывает метод из следующего класса в MRO (порядок разрешения методов). МРО за Cэто: (C, A, B, object). Вы можете распечатать, C.__mro__чтобы увидеть это.

Итак, Cнаследуется __init__от Aи superв A.__init__вызовах B.__init__( Bследует Aв MRO).

Так что, ничего не делая C, вы в конечном итоге вызываете оба, чего вы и хотите.

Теперь, если бы вы не использовали super, вы бы в конечном итоге унаследовали A.__init__(как и раньше), но на этот раз нет ничего, что могло бы B.__init__вас заинтересовать.

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

Чтобы исправить это, вам нужно определить C.__init__:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

Проблема заключается в том, что в более сложных деревьях MI __init__методы некоторых классов могут вызывать более одного раза, тогда как super / MRO гарантирует, что они вызываются только один раз.


10
Notice how both base classes use super even though they don't have their own base classes.У них есть. В py3k каждый объект подкласса класса.
akaRem

Это ответ, который я искал, но не знал, как спросить. Описание MRO хорошее.
dturvene

27

Короче говоря, они эквивалентны. Давайте посмотрим на историю:

(1) сначала функция выглядит так.

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) сделать код более абстрактным (и более переносимым). Распространенный метод получения суперкласса изобретается примерно так:

    super(<class>, <instance>)

И функция инициализации может быть:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

Однако требование явной передачи как класса, так и экземпляра немного нарушает правило DRY (Don't Repeat Yourself).

(3) в V3. Это умнее,

    super()

в большинстве случаев достаточно. Вы можете обратиться к http://www.python.org/dev/peps/pep-3135/


22

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

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

дает

42
chickenman

3

Еще одна реализация python3, которая включает использование абстрактных классов с super (). Вы должны помнить это

super().__init__(name, 10)

имеет тот же эффект, что и

Person.__init__(self, name, 10)

Помните, что в super () есть скрытое «я». Таким образом, тот же объект передается методу инициализации суперкласса, и атрибуты добавляются к объекту, который его вызвал. Следовательно, super()переводится в, Personа затем, если вы включаете скрытое я, вы получаете приведенный выше фрагмент кода.

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.