According to the documentation,
Return a proxy object that delegates method calls to a parent or sibling class of type.
The closest answer to what I need is found here:
Just like
int()
orstr()
or any of the other built-in types, the type of the object returned by callingsuper()
is that type.
My interpretation is, super()
returns an object of the specified type. However, I'm not sure why the term 'proxy' is used, does it have to the with the design pattern named 'proxy'? For example,
class Contact:
def __init__(self, phone, email):
self.phone = phone
self.email = email
class Friend(Contact):
def __init__(self, name, phone, email):
self.name = name
super().__init__(phone, email)
For this case, as far as I'm concerned, super().__init__(phone, email)
is equivalent to using Contact.__init__(self, phone, email)
. That is, an instance of Contact is returned from super()
for which we define a new attribute name
, but I don't see how it would relate to that design pattern.
That is, an instance of
Contact
is returned fromsuper()
for which we define a new attribute name
No, it doesn't. You can check by looking at print(type(super()))
. super()
returns an instance of super
... super
is actually a class! This objects acts as a proxy, i.e. it proxies the attribute lookup to the next class in the method resolution order. Here is how you might implement it in pure Python:
class SimpleSuper:
def __init__(self, cls, instance):
self.cls = cls
self.instance = instance
def __getattr__(self, name):
mro = type(self.instance).mro()
next_cls = mro[mro.index(self.cls) + 1]
attribute = getattr(next_cls, name)
if hasattr(attribute, "__get__"):
return attribute.__get__(self.instance, self.cls)
return attribute
The super
call does not provide an instance of the superclass, but instead a representation for "self
interpreted as a superclass instance". This object does not actually have the methods of the superclass, nor the attributes of self
; rather, it serves to provide access to methods/attributes of the superclass/self
– in other words, it is a proxy.
The result of the super
call is an object that we can inspect:
class Friend(Contact):
def __init__(self, name, phone, email):
self.name = name
print("super():", super())
print(super().name) # should work since `self` has that attribute?
super().__init__(phone, email)
This will provide an output such as the following:
super(): <super: <class 'Friend'>, <Friend object>>
Traceback (most recent call last):
...
File "/Users/so/example.py", line 10, in __init__
print(super().name)
AttributeError: 'super' object has no attribute 'name'
Notice how it only refers to the actual class Friend
and the actual instance <Friend object>
¹, yet does not have the attributes set on self
. The super
object is not an instance of Contact
, neither a new one nor from casting self
.
Instead, accessing a method on the super
object results in an active search of the Method Resolution Order of self
, which means looking at the superclasses of Friend
and dynamically binding self
to them. The super
object itself never actually has these methods, it merely acts as a proxy to search them.
¹It is important that the self
and __class__
bound to super
only satisfy isinstance(self, __class__)
but not always type(self) == __class__
. self
defines the MRO, but __class__
defines at what point to start searching the MRO. This ensures that super
works properly when subclassing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With