Hướng dẫn dùng python3 super python

tôi đang cố để hiểu super()

Lý do chúng tôi sử dụng superlà để các lớp con có thể đang sử dụng nhiều kế thừa hợp tác sẽ gọi hàm hàm cha mẹ tiếp theo chính xác trong Thứ tự phân giải phương thức (MRO).

Nội dung chính

  • tôi đang cố để hiểu super()
  • "Có gì khác biệt thực sự trong mã này?:"
  • Nếu Python không có super
  • Những lời phê bình về những câu trả lời khác:
  • Phê bình cho một câu trả lời khác

Nội dung chính

  • tôi đang cố để hiểu super()
  • "Có gì khác biệt thực sự trong mã này?:"
  • Nếu Python không có super
  • Những lời phê bình về những câu trả lời khác:
  • Phê bình cho một câu trả lời khác

Nội dung chính

  • tôi đang cố để hiểu super()
  • "Có gì khác biệt thực sự trong mã này?:"
  • Nếu Python không có super
  • Những lời phê bình về những câu trả lời khác:
  • Phê bình cho một câu trả lời khác

Trong Python 3, chúng ta có thể gọi nó như thế này:

class ChildB(Base):
    def __init__(self):
        super().__init__() 

Trong Python 2, chúng tôi được yêu cầu sử dụng nó như thế này:

        super(ChildB, self).__init__()

Không có siêu, bạn bị hạn chế về khả năng sử dụng nhiều kế thừa:

        Base.__init__(self) # Avoid this.

Tôi giải thích thêm dưới đây.

"Có gì khác biệt thực sự trong mã này?:"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()
        # super().__init__() # you can call super like this in Python 3!

Sự khác biệt chính trong mã này là bạn có được một lớp về mình trong __init__với super, trong đó sử dụng các lớp hiện tại để xác định các lớp tiếp theo của __init__để tìm kiếm trong MRO.

Tôi minh họa sự khác biệt này trong một câu trả lời tại câu hỏi chính tắc, Làm thế nào để sử dụng 'siêu' trong Python? , trong đó thể hiện tiêm phụ thuộchợp tác nhiều thừa kế .

Nếu Python không có super

Đây là mã thực sự tương đương với super(cách nó được triển khai trong C, trừ một số hành vi kiểm tra và dự phòng và được dịch sang Python):

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()             # Get the Method Resolution Order.
        check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
        while check_next < len(mro):
            next_class = mro[check_next]
            if '__init__' in next_class.__dict__:
                next_class.__init__(self)
                break
            check_next += 1

Viết giống một chút với Python bản địa:

class ChildB(Base):
    def __init__(self):
        mro = type(self).mro()
        for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
            if hasattr(next_class, '__init__'):
                next_class.__init__(self)
                break

Nếu chúng tôi không có superđối tượng, chúng tôi sẽ phải viết mã thủ công này ở mọi nơi (hoặc tạo lại nó!) Để đảm bảo rằng chúng tôi gọi phương thức tiếp theo phù hợp trong Thứ tự giải quyết phương pháp!

Làm thế nào siêu thực hiện điều này trong Python 3 mà không được nói rõ ràng về lớp và thể hiện từ phương thức mà nó được gọi từ đâu?

Nó nhận được khung ngăn xếp cuộc gọi và tìm thấy lớp (được lưu trữ ngầm định dưới dạng một biến tự do cục bộ, __class__làm cho hàm gọi đóng một lớp) và đối số đầu tiên cho hàm đó, là đối tượng hoặc lớp thông báo cho nó Phương pháp giải quyết thứ tự (MRO) để sử dụng.

Vì nó yêu cầu đối số đầu tiên cho MRO, nên sử dụng supervới các phương thức tĩnh là không thể .

Những lời phê bình về những câu trả lời khác:

super () cho phép bạn tránh tham chiếu đến lớp cơ sở một cách rõ ràng, có thể tốt. . Nhưng lợi thế chính đi kèm với nhiều kế thừa, nơi tất cả các loại công cụ thú vị có thể xảy ra. Xem các tài liệu tiêu chuẩn trên siêu nếu bạn chưa có.

Nó khá sóng và không cho chúng ta biết nhiều, nhưng quan điểm superkhông phải là tránh viết lớp cha. Vấn đề là đảm bảo rằng phương thức tiếp theo trong dòng theo thứ tự độ phân giải phương thức (MRO) được gọi. Điều này trở nên quan trọng trong nhiều kế thừa.

Tôi sẽ giải thích ở đây.

class Base(object):
    def __init__(self):
        print("Base init'ed")

class ChildA(Base):
    def __init__(self):
        print("ChildA init'ed")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("ChildB init'ed")
        super(ChildB, self).__init__()

Và hãy tạo ra một sự phụ thuộc mà chúng ta muốn được gọi sau Đứa trẻ:

class UserDependency(Base):
    def __init__(self):
        print("UserDependency init'ed")
        super(UserDependency, self).__init__()

Bây giờ hãy nhớ, ChildBsử dụng siêu, ChildAkhông:

class UserA(ChildA, UserDependency):
    def __init__(self):
        print("UserA init'ed")
        super(UserA, self).__init__()

class UserB(ChildB, UserDependency):
    def __init__(self):
        print("UserB init'ed")
        super(UserB, self).__init__()

UserAkhông gọi phương thức UserDependency:

>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>

Nhưng UserB, vì ChildBsử dụng super, nên!:

>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>

Phê bình cho một câu trả lời khác

Trong mọi trường hợp, bạn không nên làm như sau, điều mà một câu trả lời khác gợi ý, vì bạn chắc chắn sẽ gặp lỗi khi bạn phân lớp ChildB:

        super(self.__class__, self).__init__() # Don't do this. Ever.

(Câu trả lời đó không thông minh hoặc đặc biệt thú vị, nhưng bất chấp những lời chỉ trích trực tiếp trong các bình luận và hơn 17 lượt đánh giá, người trả lời vẫn kiên trì đề xuất cho đến khi một biên tập viên tốt bụng khắc phục vấn đề của mình.)

Giải thích: Câu trả lời đó gợi ý gọi siêu như thế này:

super(self.__class__, self).__init__()

Điều này là hoàn toàn sai. superchúng ta hãy tìm kiếm phụ huynh tiếp theo trong MRO (xem phần đầu tiên của câu trả lời này) cho các lớp con. Nếu bạn nói rằng superchúng ta đang ở trong phương thức của cá thể con, thì nó sẽ tìm kiếm phương thức tiếp theo trong dòng (có thể là phương thức này) dẫn đến đệ quy, có thể gây ra lỗi logic (trong ví dụ của người trả lời) hoặc RuntimeErrorkhi độ sâu đệ quy là vượt quá.

>>> class Polygon(object):
...     def __init__(self, id):
...         self.id = id
...
>>> class Rectangle(Polygon):
...     def __init__(self, id, width, height):
...         super(self.__class__, self).__init__(id)
...         self.shape = (width, height)
...
>>> class Square(Rectangle):
...     pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
  File "", line 1, in <module>
  File "", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

546 hữu ích 5 bình luận chia sẻ