Ответ 1
str()
не ищет метод __str__
через обычную процедуру поиска атрибутов. Вместо этого он выполняет прямой поиск метода __str__
в __dict__
его иерархии классов аргументов, в MRO порядке. Это находит super.__str__
, что дает "<super: <class 'B'>, <B object>>"
.
Однако, когда вы просматриваете b_super.__str__
вручную, который проходит через super.__getattribute__
, hook super
использует, чтобы обеспечить его особый способ поиска атрибутов. Поиск через __getattribute__
разрешит A.__str__
и вызовет это.
Рассмотрим этот класс, который иллюстрирует разницу (надеюсь):
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 A.__str__
'1'
Проблема в этом случае, а также для super
заключается в том, что это прокси, которые полагаются на __getattribute__
для пересылки. Поэтому любая функция или метод, которые не проходят через __getattribute__
, не пересылаются. И str()
- такая функция.
Просто для полноты, потому что это было упомянуто в комментариях и другом ответе.
Но str(x)
не эквивалентен type(x).__str__(x)
, потому что str()
даже избегает обычной процедуры поиска атрибутов "функции в классе". Он проверяет только tp_str
(или если этот NULL tp_repr
) слот класса. Поэтому он даже не вызывает __getattribute__
метакласса, который type(x).__str__(x)
выполнил бы:
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'
Однако в отсутствие метакласса было бы полезно подумать о str(x)
как эквивалентном type(x).__str__(x)
. Но пока (потенциально) полезно это не правильно.