เมื่อมีการโทรsuper()
เพื่อแก้ไขคลาสของเมธอดเมธอดอินสแตนซ์หรือสแตติกวิธีของเวอร์ชันพาเรนต์เราต้องการส่งคลาสปัจจุบันที่มีขอบเขตที่เราอยู่ในอาร์กิวเมนต์แรกเพื่อระบุขอบเขตของพาเรนต์ที่เรากำลังพยายามแก้ไข อาร์กิวเมนต์ที่สองคือวัตถุที่น่าสนใจเพื่อระบุว่าเรากำลังพยายามใช้ขอบเขตนั้นกับวัตถุใด
พิจารณาลำดับชั้นA
, B
และC
ที่แต่ละชั้นเป็นแม่ของหนึ่งดังต่อไปนี้มันและa
, b
และc
กรณีที่เกี่ยวข้องของแต่ละ
super(B, b)
# resolves to the scope of B's parent i.e. A
# and applies that scope to b, as if b was an instance of A
super(C, c)
# resolves to the scope of C's parent i.e. B
# and applies that scope to c
super(B, c)
# resolves to the scope of B's parent i.e. A
# and applies that scope to c
ใช้super
กับวิธีคงที่
เช่นใช้super()
จากภายใน__new__()
วิธีการ
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
คำอธิบาย:
1- ถึงแม้ว่าเป็นเรื่องปกติที่__new__()
จะใช้เป็นพารามิเตอร์แรกในการอ้างอิงถึงคลาสที่เรียก แต่จะไม่ถูกนำมาใช้ใน Python เป็น classmethod แต่เป็นวิธีการแบบคงที่ นั่นคือการอ้างอิงไปยังคลาสจะต้องผ่านอย่างชัดเจนว่าเป็นอาร์กิวเมนต์แรกเมื่อโทร__new__()
โดยตรง:
# if you defined this
class A(object):
def __new__(cls):
pass
# calling this would raise a TypeError due to the missing argument
A.__new__()
# whereas this would be fine
A.__new__(A)
2- เมื่อมีการโทรsuper()
เพื่อไปยังชั้นผู้ปกครองเราผ่านชั้นเรียนเด็กA
เป็นอาร์กิวเมนต์แรกจากนั้นเราจะส่งการอ้างอิงไปยังวัตถุที่น่าสนใจในกรณีนี้มันคือการอ้างอิงชั้นที่ถูกส่งผ่านเมื่อA.__new__(cls)
ถูกเรียก ในกรณีส่วนใหญ่มันจะเกิดขึ้นเพื่ออ้างอิงถึงชั้นเรียนเด็ก ในบางสถานการณ์มันอาจจะไม่ใช่เช่นในกรณีของการสืบทอดหลายชั่วอายุคน
super(A, cls)
3- เนื่องจากกฎทั่วไป__new__()
เป็นสแตติกวิธีsuper(A, cls).__new__
ก็จะส่งกลับสแตติกเมธอดและจำเป็นต้องระบุอาร์กิวเมนต์ทั้งหมดอย่างชัดเจนรวมถึงการอ้างอิงไปยังวัตถุที่มีความไม่สมบูรณ์ในกรณีcls
นี้
super(A, cls).__new__(cls, *a, **kw)
4- ทำสิ่งเดียวกันโดยไม่ super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
ใช้super
กับวิธีการตัวอย่าง
เช่นใช้super()
จากภายใน__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
คำอธิบาย:
1- __init__
เป็นวิธีการอินสแตนซ์ซึ่งหมายความว่ามันจะเป็นอาร์กิวเมนต์แรกของการอ้างอิงถึงอินสแตนซ์ เมื่อเรียกโดยตรงจากอินสแตนซ์การอ้างอิงจะถูกส่งผ่านโดยปริยายนั่นคือคุณไม่จำเป็นต้องระบุ:
# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...
# you create an instance
a = A()
# you call `__init__()` from that instance and it works
a.__init__()
# you can also call `__init__()` with the class and explicitly pass the instance
A.__init__(a)
2- เมื่อโทรsuper()
ภายใน__init__()
เราส่งคลาสเด็กเป็นอาร์กิวเมนต์แรกและวัตถุที่น่าสนใจเป็นอาร์กิวเมนต์ที่สองซึ่งโดยทั่วไปเป็นการอ้างอิงถึงอินสแตนซ์ของคลาสเด็ก
super(A, self)
3- การโทรsuper(A, self)
จะส่งคืนพร็อกซีที่จะแก้ไขขอบเขตและใช้กับself
ราวกับว่าตอนนี้เป็นอินสแตนซ์ของคลาสพาเรนต์ ลองเรียกพร็อกซี่s
นั้น เนื่องจาก__init__()
เป็นวิธีการเช่นการเรียกร้องs.__init__(...)
โดยปริยายจะผ่านการอ้างอิงของเป็นอาร์กิวเมนต์แรกของผู้ปกครองself
__init__()
4- จะทำเช่นเดียวกันโดยไม่ต้องเราต้องผ่านการอ้างอิงถึงตัวอย่างชัดเจนกับรุ่นของผู้ปกครองของsuper
__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
ใช้super
กับ classmethod
class A(object):
@classmethod
def alternate_constructor(cls, *a, **kw):
print "A.alternate_constructor called"
return cls(*a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
คำอธิบาย:
1- วิธีการเรียนสามารถเรียกจากชั้นเรียนโดยตรงและใช้เป็นพารามิเตอร์แรกของการอ้างอิงไปยังชั้นเรียน
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- เมื่อโทรsuper()
ภายใน classmethod เพื่อแก้ไขเวอร์ชันของ parent เราต้องการส่งคลาสย่อยปัจจุบันเป็นอาร์กิวเมนต์แรกเพื่อระบุขอบเขตของพาเรนต์ที่เรากำลังพยายามแก้ไขและวัตถุที่น่าสนใจเป็นอาร์กิวเมนต์ที่สอง เพื่อระบุว่าวัตถุใดที่เราต้องการใช้ขอบเขตนั้นซึ่งโดยทั่วไปเป็นการอ้างอิงถึงคลาสย่อยเองหรือหนึ่งในคลาสย่อย
super(B, cls_or_subcls)
3- โทรsuper(B, cls)
แก้ไขขอบเขตของและนำไปใช้A
cls
เนื่องจากalternate_constructor()
เป็นวิธีการในชั้นเรียนการโทรsuper(B, cls).alternate_constructor(...)
จะส่งผ่านการอ้างอิงโดยนัยว่าcls
เป็นอาร์กิวเมนต์แรกA
ของเวอร์ชันของalternate_constructor()
super(B, cls).alternate_constructor()
4- เพื่อทำสิ่งเดียวกันโดยไม่ใช้super()
คุณจำเป็นต้องได้รับการอ้างอิงถึงเวอร์ชันที่ไม่ได้ผูกไว้ของA.alternate_constructor()
(เช่นเวอร์ชันที่ชัดเจนของฟังก์ชัน) เพียงทำเช่นนี้จะไม่ทำงาน:
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return A.alternate_constructor(cls, *a, **kw)
ข้างต้นจะไม่ทำงานเพราะA.alternate_constructor()
วิธีการใช้การอ้างอิงโดยนัยเพื่อA
เป็นอาร์กิวเมนต์แรก cls
ถูกส่งผ่านไปที่นี่จะเป็นข้อโต้แย้งที่สอง
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
# first we get a reference to the unbound
# `A.alternate_constructor` function
unbound_func = A.alternate_constructor.im_func
# now we call it and pass our own `cls` as its first argument
return unbound_func(cls, *a, **kw)