Rozszerzanie Pythona za pomocą - używając super () Python 3 vs Python 2

103

Początkowo chciałem zadać to pytanie , ale potem odkryłem, że już o nim pomyślano ...

Szukając w Google, znalazłem przykład rozszerzenia configparser . Następujące elementy działają z 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()

Ale nie w Pythonie 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

Potem poczytałem trochę o stylach Python New Class vs. Old Class (np . Tutaj . I teraz się zastanawiam, mogę zrobić:

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"""

Ale czy nie powinienem wywołać init? Czy to w Pythonie 2 jest równoważne:

 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)
Oz123
źródło
1
W twoim przykładzie nie musisz definiować klasy __init__()w podklasie, jeśli wszystko, co robi, to wywoływanie superklasy __init__()(w Pythonie 2 lub 3) - zamiast tego po prostu pozwól dziedziczyć superklasę.
martineau
Przydatne odniesienie: amyboyle.ninja/Python-Inheritance
nu everest
Przydatne odniesienia ze skorygowanym linku: amyboyle.ninja/Python-Inheritance
fearless_fool

Odpowiedzi:

155
  • super()(bez argumentów) został wprowadzony w Pythonie 3 (wraz z __class__):

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

    więc byłby to odpowiednik Pythona 2 dla klas w nowym stylu:

    super(CurrentClass, self)
  • na zajęciach w starym stylu możesz zawsze użyć:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
    
mata
źródło
8
-1. Ta odpowiedź niczego mnie nie wyjaśniła. W Pythonie 2 super(__class__)daje NameError: global name '__class__' is not definedi również super(self.__class__)jest błędny. Państwo musi zapewnić wystąpienie jako drugi argument, która sugerowałaby trzeba zrobić super(self.__class__, self), ale to nie tak . Jeśli Class2dziedziczy Class1i Class1połączeń super(self.__class__, self).__init__(), Class1jest __init__wtedy nazywać się przy uruchamianiu instancji Class2.
jpmc26
Aby wyjaśnić kwestię, otrzymuję TypeError: super() takes at least 1 argument (0 given), próbując wywołać super(self.__class__)w Pythonie 2. (Co nie ma większego sensu, ale pokazuje, jak wiele informacji brakuje w tej odpowiedzi).
jpmc26
3
@ jpmc26: w python2 pojawi się ten błąd, ponieważ próbujesz wywołać __init__()bez argumentów na niezwiązanej Super obiektu (który można uzyskać poprzez wywołanie super(self.__class__)tylko jeden argument), trzeba związanego super-obiekt to powinno działać: super(CurrentClass, self).__init__(). Nie używaj, self.__class__ponieważ to zawsze będzie odnosić się do tej samej klasy podczas wywoływania rodzica, a zatem tworzy nieskończoną pętlę, jeśli ten rodzic również robi to samo.
mata
__class__(członek) istnieje również w Pythonie2 .
CristiFati
3
@CristiFati Nie chodzi o element __class__członkowski, ale o niejawnie utworzone __class__zamknięcie leksykalne, które zawsze odnosi się do aktualnie definiowanej klasy, która nie istnieje w pythonie2.
mata
48

W pojedynczym przypadku dziedziczenia (w przypadku podklasy tylko jednej klasy) nowa klasa dziedziczy metody klasy bazowej. Obejmuje to __init__. Jeśli więc nie zdefiniujesz tego w swojej klasie, otrzymasz ten z bazy.

Sprawy zaczynają się komplikować, jeśli wprowadzisz wielokrotne dziedziczenie (tworzenie podklas więcej niż jednej klasy na raz). Dzieje się tak, ponieważ jeśli więcej niż jedna klasa bazowa ma __init__, twoja klasa odziedziczy tylko pierwszą.

W takich przypadkach powinieneś naprawdę używać, superjeśli możesz, wyjaśnię dlaczego. Ale nie zawsze możesz. Problem w tym, że wszystkie twoje klasy bazowe również muszą go używać (a także ich klasy bazowe - całe drzewo).

Jeśli tak jest, to również będzie działać poprawnie (w Pythonie 3, ale można go przerobić na Python 2 - tak też jest 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

Zauważ, że obie klasy bazowe używają, supermimo że nie mają własnych klas bazowych.

Co superrobi: wywołuje metodę z następnej klasy w MRO (kolejność rozwiązywania metod). MRO na Cto: (C, A, B, object). Możesz wydrukować, C.__mro__aby to zobaczyć.

Tak więc Cdziedziczy __init__po Ai superw A.__init__połączeniach B.__init__( Bnastępuje Aw MRO).

Więc nie robiąc nic, w Ckońcu dzwonisz do obu, a właśnie tego chcesz.

Teraz, gdybyś nie używał super, skończyłbyś na dziedziczeniu A.__init__(jak poprzednio), ale tym razem nie ma nic, co by B.__init__cię wymagało .

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

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

class C(A, B):
    pass

C()
#prints:
#A

Aby to naprawić, musisz zdefiniować C.__init__:

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

Problem polega na tym, że w bardziej skomplikowanych drzewach MI __init__metody niektórych klas mogą być wywoływane więcej niż raz, podczas gdy super / MRO gwarantują, że zostaną wywołane tylko raz.

jak
źródło
10
Notice how both base classes use super even though they don't have their own base classes.Oni mają. W py3k każda klasa stanowi podklasę obiektu.
akaRem
To jest odpowiedź, której szukałem, ale nie wiedziałem, jak o to zapytać. Opis MRO jest dobry.
dturvene
27

Krótko mówiąc, są równoważne. Spójrzmy na historię:

(1) na początku funkcja wygląda następująco.

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

(2) aby kod był bardziej abstrakcyjny (i bardziej przenośny). Wynaleziono powszechną metodę uzyskania superklasy, na przykład:

    super(<class>, <instance>)

Funkcją init może być:

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

Jednak wymaganie jawnego przekazania zarówno klasy, jak i instancji powoduje nieco złamanie reguły DRY (Don't Repeat Yourself).

(3) w wersji V3. Jest mądrzejszy,

    super()

w większości przypadków wystarczy. Możesz odnieść się do http://www.python.org/dev/peps/pep-3135/

wuliang
źródło
22

Żeby mieć prosty i kompletny przykład dla Pythona 3, którego większość ludzi używa teraz.

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)

daje

42
chickenman
Jonathan Mugan
źródło
3

Kolejna implementacja pythona3, która wykorzystuje klasy abstrakcyjne z super (). Powinieneś o tym pamiętać

super().__init__(name, 10)

ma taki sam efekt jak

Person.__init__(self, name, 10)

Pamiętaj, że w super () znajduje się ukryte „self”, więc ten sam obiekt jest przekazywany do metody init nadklasy, a atrybuty są dodawane do obiektu, który go wywołał. W związku z tym super()zostanie przetłumaczony na, Persona następnie, jeśli dołączysz ukrytą jaźń, otrzymasz powyższy fragment kodu.

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())
SeasonalShot
źródło