Khi gọi super()
để giải quyết phiên bản cha mẹ của phương thức phân loại, phương thức cá thể hoặc phương pháp tĩnh, chúng tôi muốn vượt qua lớp hiện tại có phạm vi mà chúng tôi tham gia với tư cách là đối số đầu tiên, để chỉ ra phạm vi của cha mẹ mà chúng tôi đang cố gắng giải quyết và như một đối số thứ hai đối tượng quan tâm để chỉ ra đối tượng nào chúng ta đang cố gắng áp dụng phạm vi đó.
Xem xét một hệ thống phân cấp lớp A
, B
và C
trong đó mỗi lớp là phụ huynh của một trong những điều sau nó, và a
, b
và c
các trường hợp tương ứng của mỗi người.
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
Sử dụng super
với tĩnh điện
ví dụ: sử dụng super()
từ bên trong __new__()
phương thức
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Giải trình:
1- mặc dù thông thường __new__()
được coi là thông số đầu tiên của nó tham chiếu đến lớp gọi, nhưng nó không được triển khai trong Python dưới dạng phân loại, mà là tĩnh đối xứng. Đó là, một tham chiếu đến một lớp phải được thông qua rõ ràng là đối số đầu tiên khi gọi __new__()
trực tiếp:
# 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- khi gọi super()
để đến lớp cha, chúng ta chuyển lớp con A
làm đối số đầu tiên của nó, sau đó chúng ta chuyển tham chiếu đến đối tượng quan tâm, trong trường hợp này là tham chiếu lớp được truyền khi A.__new__(cls)
được gọi. Trong hầu hết các trường hợp, nó cũng là một tham chiếu đến lớp con. Trong một số trường hợp, nó có thể không, ví dụ trong trường hợp thừa kế nhiều thế hệ.
super(A, cls)
3- vì như một quy tắc chung __new__()
là tĩnh, nên super(A, cls).__new__
cũng sẽ trả về tĩnh và cần được cung cấp tất cả các đối số một cách rõ ràng, bao gồm cả tham chiếu đến đối tượng insterest, trong trường hợp này cls
.
super(A, cls).__new__(cls, *a, **kw)
4- làm điều tương tự mà không cần super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Sử dụng super
với một phương thức ví dụ
ví dụ như sử dụng super()
từ bên trong__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Giải trình:
1- __init__
là một phương thức cá thể, nghĩa là nó lấy tham số đầu tiên của nó làm tham chiếu đến một thể hiện. Khi được gọi trực tiếp từ thể hiện, tham chiếu được truyền hoàn toàn, đó là bạn không cần chỉ định nó:
# 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- khi gọi super()
bên trong __init__()
chúng ta chuyển lớp con làm đối số thứ nhất và đối tượng quan tâm là đối số thứ hai, nói chung là tham chiếu đến một thể hiện của lớp con.
super(A, self)
3- Cuộc gọi super(A, self)
trả về một proxy sẽ giải quyết phạm vi và áp dụng nó self
như thể bây giờ nó là một thể hiện của lớp cha. Hãy gọi proxy đó s
. Vì __init__()
là một phương thức cá thể, cuộc gọi s.__init__(...)
sẽ ngầm chuyển một tham chiếu self
là đối số đầu tiên cho cha mẹ __init__()
.
4 - để làm tương tự mà không cần super
chúng ta chuyển một tham chiếu đến một thể hiện rõ ràng cho phiên bản của cha mẹ __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Sử dụng super
với một đẳng cấp
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)
Giải trình:
1- Một lớp đối xứng có thể được gọi trực tiếp từ lớp và lấy tham số đầu tiên của nó làm tham chiếu đến lớp.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- khi gọi super()
trong một lớp đối xứng để giải quyết phiên bản của cha mẹ của nó, chúng tôi muốn chuyển lớp con hiện tại làm đối số đầu tiên để chỉ ra phạm vi của cha mẹ mà chúng tôi đang cố gắng giải quyết và đối tượng quan tâm là đối số thứ hai để chỉ ra đối tượng nào chúng ta muốn áp dụng phạm vi đó, nói chung là tham chiếu đến chính lớp con hoặc một trong các lớp con của nó.
super(B, cls_or_subcls)
3- Cuộc gọi super(B, cls)
giải quyết theo phạm vi A
và áp dụng nó cho cls
. Vì alternate_constructor()
là một đối tượng phân loại, cuộc gọi super(B, cls).alternate_constructor(...)
sẽ hoàn toàn chuyển một tham chiếu cls
là đối số đầu tiên cho A
phiên bản củaalternate_constructor()
super(B, cls).alternate_constructor()
4 - để làm tương tự mà không sử dụng, super()
bạn sẽ cần có một tham chiếu đến phiên bản không ràng buộc của A.alternate_constructor()
(tức là phiên bản rõ ràng của chức năng). Đơn giản chỉ cần làm điều này sẽ không hoạt động:
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)
Ở trên sẽ không hoạt động vì A.alternate_constructor()
phương thức lấy tham chiếu ngầm A
làm đối số đầu tiên của nó. Việc cls
được thông qua ở đây sẽ là đối số thứ hai của nó.
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)