Подтвердить что ты не робот

Почему str (super (B, b)) не эквивалентна супер (B, b).__ str __()?

  • Предположим, что A является родительским классом B, а B является экземпляром B. Затем может быть вызван метод переопределения A с super: super(B, b).method().

  • Состояние документа "str(object) возвращает object.__str__()" в его базовом вызове.

Он должен следовать за str(super(B, b)) == super(B, b).__str__(), но это не так (интерактивная версия):

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"

Итак, где я ошибся? Не работает ли супер-механизм для магических методов? В этом случае str не вызывает __str__? Связано ли это с этим пунктом:

Обратите внимание, что super() реализуется как часть процесса привязки для поиска явных точечных атрибутов, таких как super().__getitem__(name). Он делает это, реализуя собственный метод __getattribute__() для поиска классов в предсказуемом порядке, который поддерживает совместное множественное наследование. Соответственно, super() undefined для неявных поисков с использованием операторов или операторов, таких как super()[name].

4b9b3361

Ответ 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). Но пока (потенциально) полезно это не правильно.

Ответ 2

Документы ошибочны.

str(x) фактически эквивалентен type(x).__str__(x).

Если вы print(type(b_super).__str__(b_super)), вы получите очевидный результат.

(даже это может быть упрощено в случае странных метаклассов)