Розширення Python на - за допомогою супер () 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"""

Але чи не варто мені дзвонити в Ініт? Це еквівалент 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) - натомість просто нехай спадкує супер.
martineau

Корисна довідка: amyboyle.ninja/Python-Inheritance
nu everest

Корисні посилання з виправленою посиланням: 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__тому що це завжди буде посилатися на той самий клас при виклику батьків і тому створити нескінченний цикл, якщо цей батько також робить те ж саме.
мата

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

3
@CristiFati Йдеться не про __class__член, а про неявно створене лексичне __class__закриття, яке завжди посилається на визначений в даний час клас, який не існує в python2.
мата

48

У єдиному випадку спадкування (коли ви підкласуєте лише один клас), ваш новий клас успадковує методи базового класу. Сюди входить __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)

Проблема в тому, що у складніших ІР-деревах __init__методи деяких класів можуть викликатися не раз, тоді як супер / МРО гарантують, що вони викликаються лише один раз.


10
Notice how both base classes use super even though they don't have their own base classes.Вони мають. У py3k об'єкт підкласів кожного класу.
akaRem

Це відповідь, яку я шукав, але не знав, як її запитати. Опис МРО хороший.
dturvene

27

Словом, вони рівноцінні. Давайте переглянемо історію:

(1) спочатку функція виглядає приблизно так.

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

(2) зробити код більш абстрактним (і більш портативним). Винайдено поширений метод отримання суперкласу, як:

    super(<class>, <instance>)

І функцією init може бути:

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

Однак, вимагаючи явного проходження класу та екземпляра, трохи порушуйте правило DRY (не повторюйте себе).

(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 () є приховане "Я", тому той самий об'єкт переходить до методу init надкласу і атрибути додаються до об'єкта, який його назвав. Звідси 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.