Assume A is the parent class of B and b is an instance of B. Then an overriden method of A can be called with super: super(B, b).method().
The docs state "str(object) returns object.__str__()" in its basic invocation.
It should follow that str(super(B, b)) == super(B, b).__str__(), but that's not the case (interactive version):
class A:
def __str__(self):
return "A"
class B(A):
def __str__(self):
return "B"
b = B()
b_super = super(B, b)
print(str(b_super)) # "<super: <class 'B'>, <B object>>"
print(b_super.__str__()) # "A"
So where did I go wrong? Does the super mechanism not work for magic methods? Does str not invoke __str__ in this case? Is it related to this paragraph:
Note that
super()is implemented as part of the binding process for explicit dotted attribute lookups such assuper().__getitem__(name). It does so by implementing its own__getattribute__()method for searching classes in a predictable order that supports cooperative multiple inheritance. Accordingly,super()is undefined for implicit lookups using statements or operators such assuper()[name].
str() doesn't look up the __str__ method through the normal attribute lookup procedure. Instead, it performs a direct search for the __str__ method in the __dict__s of its argument's class hierarchy, in MRO order. This finds super.__str__, which gives "<super: <class 'B'>, <B object>>".
However, when you look up b_super.__str__ manually, that goes through super.__getattribute__, the hook super uses to provide its special attribute lookup behavior. The lookup through __getattribute__ will resolve to A.__str__ and call that.
Consider this class, which illustrates the difference (I hope):
class B(object):
def __init__(self, other):
self.other = other
def __getattribute__(self, name):
if name == 'other':
return object.__getattribute__(self, 'other')
elif name == '__str__':
return getattr(self.other, name)
else:
return name
def __str__(self):
return 'fun'
>>> str(B(1)) # calls B.__str__ because it doesn't invoke __getattribute__
'fun'
>>> B(1).__str__() # calls B.__getattribute__ to look up the __str__ method which returns (1).__str__
'1'
The problem in this case and likewise for super is that these are proxies that rely on __getattribute__ to forward it. So any function or method that doesn't go through __getattribute__ doesn't forward. And str() is such a function.
Just for completeness because it was mentioned in the comments and the other answer.
But str(x) isn't equivalent to type(x).__str__(x) because str() even avoids the normal attribute lookup procedure of the "function on the class". It only checks the tp_str (or if that's NULL the tp_repr) slot of the class. So it doesn't even invoke __getattribute__ of the metaclass, which type(x).__str__(x) would do:
class A(type):
def __getattribute__(self, name):
print(name)
if name == '__str__':
return lambda self: 'A'
else:
return type.__getattribute__(self, name)
class B(metaclass=A):
def __str__(self):
return 'B'
>>> b = B()
>>> str(b)
'B'
>>> type(b).__str__(b)
__str__
'A'
However in the absense of a metaclass it might be helpful to think of str(x) as equivalent to type(x).__str__(x). But while (potentially) helpful it's not correct.
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