Python mở rộng với - sử dụng super () Python 3 so với Python 2


103

Ban đầu tôi muốn hỏi câu hỏi này , nhưng sau đó tôi thấy nó đã được nghĩ đến từ trước ...

Tìm kiếm xung quanh tôi đã tìm thấy ví dụ này về mở rộng configparser . Những điều sau đây hoạt động với 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()

Nhưng không phải với 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

Sau đó, tôi đọc một chút về kiểu Python New Class so với Old Class (ví dụ: ở đây . Và bây giờ tôi đang tự hỏi, tôi có thể làm:

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

Nhưng, tôi không nên gọi init? Điều này trong Python 2 có tương đương không:

 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
Trong ví dụ của bạn, bạn không cần phải xác định một __init__()trong lớp con nếu tất cả những gì nó làm là gọi siêu lớp ' __init__()(trong Python 2 hoặc 3) - thay vào đó chỉ cần để siêu lớp được kế thừa.
martineau


Tài liệu tham khảo hữu ích với liên kết điều chỉnh: amyboyle.ninja/Python-Inheritance
fearless_fool

Câu trả lời:


155
  • super()(không có đối số) đã được giới thiệu trong Python 3 (cùng với __class__):

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

    vì vậy đó sẽ là Python 2 tương đương cho các lớp kiểu mới:

    super(CurrentClass, self)
  • đối với các lớp kiểu cũ, bạn luôn có thể sử dụng:

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

8
-1. Câu trả lời này không làm rõ bất cứ điều gì cho tôi. Trong Python 2, super(__class__)cho NameError: global name '__class__' is not definedsuper(self.__class__)cũng sai. Bạn phải cung cấp một ví dụ làm đối số thứ hai, điều này sẽ gợi ý bạn cần phải làm super(self.__class__, self), nhưng điều đó là sai . Nếu Class2thừa hưởng từ Class1Class1các cuộc gọi super(self.__class__, self).__init__(), Class1's __init__sau đó sẽ gọi bản thân khi instantiating một thể hiện của Class2.
jpmc 26

Để làm rõ một điểm, tôi nhận được TypeError: super() takes at least 1 argument (0 given)khi cố gắng gọi super(self.__class__)bằng Python 2. (Mà không làm cho rất nhiều ý nghĩa, nhưng nó thể hiện bao nhiêu thông tin là mất tích từ câu trả lời này.)
jpmc26

3
@ jpmc26: trong python2 bạn nhận được lỗi này vì bạn cố gắng để gọi __init__()mà không cần tranh luận trên các đối tượng siêu cởi ra (mà bạn có được bằng cách gọi super(self.__class__)chỉ với một đối số), bạn cần một đối tượng siêu ràng buộc sau đó nó nên làm việc: super(CurrentClass, self).__init__(). Không sử dụng self.__class__bởi vì điều đó sẽ luôn tham chiếu đến cùng một lớp khi gọi một cấp độ gốc và do đó, hãy tạo một vòng lặp vô hạn nếu cấp độ huynh đó cũng làm như vậy.
mata

__class__(thành viên) cũng tồn tại trong Python2 .
CristiFati

3
@CristiFati Đây không phải là về __class__thành viên mà là về bao đóng từ vựng được tạo ngầm__class__ luôn đề cập đến lớp hiện đang được định nghĩa, không tồn tại trong python2.
mata

48

Trong trường hợp kế thừa đơn (khi bạn chỉ phân lớp một lớp), lớp mới của bạn kế thừa các phương thức của lớp cơ sở. Điều này bao gồm __init__. Vì vậy, nếu bạn không xác định nó trong lớp của mình, bạn sẽ lấy một cái từ cơ sở.

Mọi thứ bắt đầu phức tạp nếu bạn giới thiệu đa kế thừa (phân lớp nhiều hơn một lớp cùng một lúc). Điều này là do nếu có nhiều hơn một lớp cơ sở __init__, lớp của bạn sẽ chỉ kế thừa lớp đầu tiên.

Trong những trường hợp như vậy, bạn thực sự nên sử dụng super nếu có thể, tôi sẽ giải thích tại sao. Nhưng không phải lúc nào bạn cũng làm được. Vấn đề là tất cả các lớp cơ sở của bạn cũng phải sử dụng nó (và cả các lớp cơ sở của chúng - cả cây).

Nếu đúng như vậy, thì điều này cũng sẽ hoạt động chính xác (trong Python 3 nhưng bạn có thể làm lại nó thành Python 2 - nó cũng có 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

Lưu ý cách cả hai lớp cơ sở sử dụng super mặc dù chúng không có lớp cơ sở của riêng mình.

Cái gì superlà: nó gọi phương thức từ lớp tiếp theo trong MRO (thứ tự phân giải phương thức). MRO cho Clà:(C, A, B, object) . Bạn có thể in ra C.__mro__để xem.

Vì vậy, Ckế thừa __init__từ Asupertrong A.__init__các cuộc gọi B.__init__( Bsau Atrong MRO).

Vì vậy, bằng cách không làm gì cả C, bạn sẽ gọi cả hai, đó là những gì bạn muốn.

Bây giờ nếu bạn không sử dụng super, bạn sẽ kế thừa A.__init__(như trước đây) nhưng lần này không có gì gọi B.__init__cho bạn.

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

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

class C(A, B):
    pass

C()
#prints:
#A

Để khắc phục điều đó, bạn phải xác định C.__init__:

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

Vấn đề với điều đó là trong các cây MI phức tạp hơn, __init__các phương thức của một số lớp có thể được gọi nhiều lần trong khi super / MRO đảm bảo rằng chúng chỉ được gọi một lần.


10
Notice how both base classes use super even though they don't have their own base classes.Họ có. Trong py3k mọi đối tượng lớp con của lớp.
akaRem

Đây là câu trả lời tôi đang tìm kiếm, nhưng không biết làm thế nào để hỏi. Mô tả MRO là tốt.
dturvene

27

Nói tóm lại, chúng tương đương nhau. Hãy xem lịch sử:

(1) lúc đầu, hàm trông như thế này.

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

(2) để làm cho mã trừu tượng hơn (và dễ di chuyển hơn). Một phương pháp phổ biến để có được Super-Class được phát minh như:

    super(<class>, <instance>)

Và hàm init có thể là:

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

Tuy nhiên, việc yêu cầu chuyển cả lớp và phiên bản một cách rõ ràng sẽ phá vỡ quy tắc DRY (Không lặp lại chính bạn) một chút.

(3) trong V3. Nó thông minh hơn,

    super()

là đủ trong hầu hết các trường hợp. Bạn có thể tham khảo tại http://www.python.org/dev/peps/pep-3135/


22

Chỉ để có một ví dụ đơn giản và đầy đủ cho Python 3, mà hầu hết mọi người dường như đang sử dụng bây giờ.

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)

cho

42
chickenman

3

Một triển khai python3 khác liên quan đến việc sử dụng các lớp Trừu tượng với super (). Bạn nên nhớ rằng

super().__init__(name, 10)

có tác dụng tương tự như

Person.__init__(self, name, 10)

Hãy nhớ rằng có một 'self' ẩn trong super (), Vì vậy, cùng một đối tượng sẽ chuyển cho phương thức init của lớp cha và các thuộc tính được thêm vào đối tượng đã gọi nó. Do đó super()được dịch sang Personvà sau đó nếu bạn bao gồm bản thân ẩn, bạn sẽ nhận được đoạn mã trên.

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())
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.