Memperluas Python dengan - menggunakan super () Python 3 vs Python 2


103

Awalnya saya ingin menanyakan pertanyaan ini , tetapi kemudian saya menemukan itu sudah terpikirkan sebelumnya ...

Googling sekitar saya menemukan contoh memperluas configparser ini . Pekerjaan berikut dengan 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()

Tetapi tidak dengan 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

Kemudian saya membaca sedikit tentang gaya Kelas Baru vs Kelas Lama Python (misalnya di sini . Dan sekarang saya bertanya-tanya, saya bisa melakukan:

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

Tapi, haruskah saya menelepon init? Apakah ini setara dengan 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
Dalam contoh Anda, Anda tidak perlu mendefinisikan sebuah __init__()di subkelas jika yang dilakukannya hanyalah memanggil kelas super ' __init__()(baik dalam Python 2 atau 3) - alih-alih biarkan super diwariskan.
martineau


Referensi yang berguna dengan tautan yang diperbaiki: amyboyle.ninja/Python-Inheritance
fearless_fool

Jawaban:


155
  • super()(tanpa argumen) diperkenalkan dengan Python 3 (bersama dengan __class__):

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

    jadi itu akan menjadi Python 2 yang setara untuk kelas gaya baru:

    super(CurrentClass, self)
  • untuk kelas gaya lama Anda selalu dapat menggunakan:

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

8
-1. Jawaban ini tidak menjelaskan apa pun bagi saya. Dalam Python 2, super(__class__)memberi NameError: global name '__class__' is not defined, dan super(self.__class__)juga salah. Anda harus memberikan contoh sebagai argumen kedua, yang menyarankan Anda perlu melakukannya super(self.__class__, self), tetapi itu salah . Jika Class2mewarisi dari Class1dan Class1panggilan super(self.__class__, self).__init__(), Class1's __init__maka akan menyebut dirinya ketika instantiate sebuah contoh dari Class2.
jpmc26

Untuk memperjelas suatu hal, saya mengerti TypeError: super() takes at least 1 argument (0 given)ketika mencoba memanggil super(self.__class__)dengan Python 2. (Yang tidak masuk akal, tetapi ini menunjukkan berapa banyak informasi yang hilang dari jawaban ini.)
jpmc26

3
@ jpmc26: di python2 Anda mendapatkan kesalahan ini karena Anda mencoba untuk memanggil __init__()tanpa argumen pada objek yang super terikat (yang Anda dapatkan dengan menelepon super(self.__class__)dengan hanya satu argumen), Anda memerlukan objek yang super terikat maka harus bekerja: super(CurrentClass, self).__init__(). Jangan gunakan self.__class__karena itu akan selalu merujuk ke kelas yang sama saat memanggil induk dan oleh karena itu buat pengulangan tak terbatas jika induk tersebut juga melakukan hal yang sama.
mata

__class__(anggota) juga ada di Python2 .
CristiFati

3
@CristiFati Ini bukan tentang __class__anggota tetapi tentang penutupan leksikal yang dibuat secara implisit__class__ yang selalu merujuk ke kelas yang saat ini sedang didefinisikan, yang tidak ada di python2.
mata

48

Dalam kasus pewarisan tunggal (saat Anda membuat subkelas satu kelas saja), kelas baru Anda mewarisi metode dari kelas dasar. Ini termasuk __init__. Jadi jika Anda tidak mendefinisikannya di kelas Anda, Anda akan mendapatkan satu dari basis.

Hal-hal mulai menjadi rumit jika Anda memperkenalkan beberapa warisan (membuat subkelas lebih dari satu kelas dalam satu waktu). Ini karena jika lebih dari satu kelas dasar yang dimiliki __init__, kelas Anda hanya akan mewarisi kelas pertama.

Dalam kasus seperti itu, Anda harus benar-benar menggunakan superjika Anda bisa, saya akan menjelaskan alasannya. Tapi tidak selalu bisa. Masalahnya adalah bahwa semua kelas dasar Anda juga harus menggunakannya (dan kelas dasarnya juga - seluruh pohon).

Jika itu masalahnya, maka ini juga akan bekerja dengan benar (dengan Python 3 tetapi Anda dapat mengerjakannya ulang menjadi Python 2 - ia juga memiliki 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

Perhatikan bagaimana kedua kelas dasar digunakan supermeskipun mereka tidak memiliki kelas dasarnya sendiri.

Apa yang superdilakukannya adalah: memanggil metode dari kelas berikutnya dalam MRO (urutan resolusi metode). MRO untuk Cadalah: (C, A, B, object). Anda dapat mencetak C.__mro__untuk melihatnya.

Jadi, Cmewarisi __init__dari Adan superdalam A.__init__panggilan B.__init__( Bmengikuti Adi MRO).

Jadi dengan tidak melakukan apa pun C, Anda akhirnya memanggil keduanya, yang Anda inginkan.

Sekarang jika Anda tidak menggunakan super, Anda akan mendapatkan warisan A.__init__(seperti sebelumnya) tapi kali ini tidak ada yang akan memanggil B.__init__Anda.

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

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

class C(A, B):
    pass

C()
#prints:
#A

Untuk memperbaikinya, Anda harus menentukan C.__init__:

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

Masalahnya adalah bahwa di pohon MI yang lebih rumit, __init__metode beberapa kelas mungkin akan dipanggil lebih dari sekali sedangkan super / MRO menjamin bahwa mereka dipanggil hanya sekali.


10
Notice how both base classes use super even though they don't have their own base classes.Mereka punya. Dalam py3k setiap objek subclass kelas.
akaRem

Ini adalah jawaban yang saya cari, tetapi tidak tahu bagaimana cara bertanya. Deskripsi MRO bagus.
dturvene

27

Singkatnya, mereka setara. Mari kita lihat sejarah:

(1) pada awalnya, fungsinya terlihat seperti ini.

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

(2) untuk membuat kode lebih abstrak (dan lebih portabel). Metode umum untuk mendapatkan Super-Class ditemukan seperti:

    super(<class>, <instance>)

Dan fungsi init dapat berupa:

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

Namun membutuhkan pengalihan eksplisit baik kelas dan contoh melanggar aturan DRY (Jangan Ulangi Diri Anda) sedikit.

(3) di V3. Itu lebih pintar,

    super()

dalam banyak kasus sudah cukup. Anda dapat merujuk ke http://www.python.org/dev/peps/pep-3135/


22

Hanya untuk mendapatkan contoh sederhana dan lengkap untuk Python 3, yang sepertinya digunakan kebanyakan orang sekarang.

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)

memberi

42
chickenman

3

Implementasi python3 lain yang melibatkan penggunaan kelas Abstrak dengan super (). Anda harus ingat itu

super().__init__(name, 10)

memiliki efek yang sama seperti

Person.__init__(self, name, 10)

Ingat ada 'diri' yang tersembunyi di super (), Jadi objek yang sama diteruskan ke metode superclass init dan atribut ditambahkan ke objek yang memanggilnya. Karenanya super()akan diterjemahkan ke Persondan kemudian jika Anda menyertakan diri yang tersembunyi, Anda mendapatkan frag kode di atas.

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())
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.