ทำไม Python 3.x's super () เวทมนต์?


159

ใน Python 3.x super()สามารถเรียกใช้โดยไม่มีอาร์กิวเมนต์:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

เพื่อที่จะทำให้งานนี้บางมายากลรวบรวมเวลาที่จะดำเนินการอย่างใดอย่างหนึ่งเป็นผลมาจากที่ซึ่งเป็นรหัสต่อไปนี้ (ซึ่ง rebinds superไปsuper_) ล้มเหลว:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

เหตุใดจึงsuper()ไม่สามารถแก้ไข superclass ตอนรันไทม์โดยไม่มีความช่วยเหลือจากคอมไพเลอร์ มีสถานการณ์ในทางปฏิบัติที่พฤติกรรมนี้หรือเหตุผลพื้นฐานสำหรับมันสามารถกัดโปรแกรมเมอร์ที่ไม่ระวัง?

... และตามคำถามด้านข้าง: มีตัวอย่างอื่นใน Python ของฟังก์ชั่นวิธีการ ฯลฯ ที่สามารถใช้งานไม่ได้โดยการ rebinding ให้เป็นชื่ออื่นหรือไม่?


6
ฉันจะให้ Armin ทำอธิบายเกี่ยวกับเรื่องนี้อย่างใดอย่างหนึ่ง นี่เป็นอีกหนึ่งโพสต์
เกม Brainiac

ที่เกี่ยวข้อง: stackoverflow.com/q/36993577/674039
Wim

คำตอบ:


217

ความมหัศจรรย์ใหม่super()พฤติกรรมถูกเพิ่มเข้ามาเพื่อหลีกเลี่ยงการละเมิดแห้ง (ไม่ซ้ำตัวเอง) หลักการดูPEP 3135 การตั้งชื่อชั้นเรียนอย่างชัดเจนโดยการอ้างอิงว่าเป็นโกลบอลก็มีแนวโน้มที่จะมีปัญหาเรื่องการตอบโต้แบบเดียวกันกับที่คุณค้นพบด้วยsuper()ตัวเอง:

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

เช่นเดียวกับการใช้ class decorators ซึ่ง decorator ส่งคืนออบเจ็กต์ใหม่ซึ่ง rebinds ชื่อคลาส:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

super() __class__เซลล์เวทย์มนตร์ก้าวเท้าลงปัญหาเหล่านี้เป็นอย่างดีโดยให้คุณเข้าถึงวัตถุคลาสดั้งเดิม

PEP ถูกเตะออกโดยกุยซึ่งตอนแรกคาดว่าsuperจะกลายเป็นคำหลักและความคิดในการใช้เซลล์เพื่อค้นหาชั้นเรียนปัจจุบันก็เป็นของเขาเช่นกัน แน่นอนความคิดที่จะทำให้มันเป็นคำหลักที่เป็นส่วนหนึ่งของร่างแรกของห้าวหาญ

อย่างไรก็ตามในความเป็นจริงกุยโด้เองก็ก้าวออกมาจากแนวคิดคำหลักว่า 'มีมนต์ขลังมาก'โดยเสนอให้มีการใช้งานปัจจุบันแทน เขาคาดว่าการใช้ชื่ออื่นsuper()อาจเป็นปัญหา :

แพทช์ของฉันจะใช้วิธีการแก้ปัญหาระดับกลาง: จะอนุมานว่าคุณต้องการเมื่อใดก็ตามที่คุณใช้ชื่อตัวแปร__class__ 'super'ดังนั้นหากคุณ (ทั่วโลก) เปลี่ยนชื่อsuperเป็นsupperและใช้supperแต่ไม่superทำงานมันจะไม่ทำงานหากไม่มีข้อโต้แย้ง (แต่จะยังคงใช้งานได้หากคุณผ่านมัน __class__หรือวัตถุคลาสจริง); หากคุณมีชื่อตัวแปรที่ไม่เกี่ยวข้องsuperสิ่งต่าง ๆ จะใช้งานได้ แต่วิธีการจะใช้เส้นทางการโทรที่ช้ากว่าเล็กน้อยที่ใช้สำหรับตัวแปรเซลล์

ดังนั้นในท้ายที่สุด Guido เองก็ประกาศว่าการใช้superคำหลักนั้นไม่ถูกต้องและการจัดหา__class__เซลล์เวทย์มนตร์เป็นการประนีประนอมที่ยอมรับได้

ฉันยอมรับว่าเวทมนตร์พฤติกรรมโดยปริยายของการใช้งานนั้นค่อนข้างน่าแปลกใจ แต่super()เป็นหนึ่งในฟังก์ชั่นที่ใช้ผิดพลาดมากที่สุดในภาษา เพียงแค่ดูความผิดพลาดsuper(type(self), self)หรือsuper(self.__class__, self) การเรียกใช้ที่พบในอินเทอร์เน็ต ถ้ามีของรหัสที่เคยถูกเรียกจากชั้นเรียนมาคุณจะจบลงด้วยการยกเว้นการเรียกซ้ำอนันต์ อย่างน้อยที่สุดง่ายขึ้นsuper()โทรโดยไม่ขัดแย้งหลีกเลี่ยงว่าปัญหาที่เกิดขึ้น

สำหรับการเปลี่ยนชื่อsuper_; เพียงอ้างอิง__class__ในวิธีการของคุณเช่นกันและมันจะทำงานอีกครั้ง เซลล์ถูกสร้างขึ้นหากคุณอ้างอิงsuper หรือ __class__ชื่อในวิธีการของคุณ:

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

1
ดีบทความ มันยังใสเหมือนโคลนอย่างไรก็ตาม คุณกำลังบอกว่า super () เทียบเท่ากับฟังก์ชั่นอินสแตนซ์อัตโนมัติเช่นdef super(of_class=magic __class__)ชนิด a self.super(); def super(self): return self.__class__?
Charles Merriam

17
@CharlesMerriam: โพสต์นี้ไม่เกี่ยวกับวิธีที่super()ไม่มีข้อโต้แย้งทำงาน ส่วนใหญ่เกี่ยวข้องกับสาเหตุที่มีอยู่ super()ในวิธีการเรียนเทียบเท่ากับsuper(ReferenceToClassMethodIsBeingDefinedIn, self)ที่ReferenceToClassMethodIsBeingDefinedInกำหนดในเวลารวบรวมที่แนบมากับวิธีการปิดชื่อ__class__และsuper()จะค้นหาทั้งสองจากกรอบการโทรที่รันไทม์ แต่คุณไม่จำเป็นต้องรู้ทั้งหมดนี้
Martijn Pieters

1
@CharlesMerriam: แต่super()ไม่ใกล้กับฟังก์ชั่นอินสแตนซ์โดยอัตโนมัติไม่
Martijn Pieters

1
@ chris.leonard: ประโยคที่สำคัญคือเซลล์ที่ถูกสร้างขึ้นถ้าคุณใช้ซุปเปอร์ () หรือใช้__class__ในวิธีการของคุณ คุณใช้ชื่อsuperในการทำงานของคุณ คอมไพเลอร์เห็นและเพิ่มการ__class__ปิด
Martijn Pieters

4
@Alexey: มันไม่เพียงพอ type(self)ให้ชนิดปัจจุบันซึ่งไม่เหมือนกับวิธีที่กำหนดไว้ ดังนั้นคลาสที่Fooมีเมธอดbazต้องการsuper(Foo, self).baz(), เพราะมันสามารถซับคลาสเป็นclass Ham(Foo):, ณ จุดtype(self)นี้Hamและsuper(type(self), self).baz()จะให้ลูปไม่สิ้นสุด ดูโพสต์ที่ฉันลิงค์ไปในคำตอบของฉัน: เมื่อโทร super () ในชั้นเรียนที่ได้รับฉันจะส่งด้วยตนเอง. _ class__?
Martijn Pieters
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.