为什么str(super(B,b))不等于super(B,b).__ str __()?

tja*_*son 24 python inheritance python-3.x

它应该遵循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"
Run Code Online (Sandbox Code Playgroud)

那我哪里出错了?超级机制不适用于魔术方法吗?难道str不能调用__str__在这种情况下?它与本段有关:

请注意,它super()是作为显式点状属性查找的绑定过程的一部分实现的,例如super().__getitem__(name).它通过实现自己的__getattribute__()方法以可预测的顺序搜索类来支持协作多重继承.因此,super()对于使用语句或运算符(例如)的隐式查找,未定义super()[name].

MSe*_*ert 22

str()__str__通过常规属性查找过程查找方法.相反,它以MRO顺序直接搜索其参数的层次结构__str____dict__s中的方法.这找到了,这给了.super.__str__"<super: <class 'B'>, <B object>>"

但是,当您b_super.__str__手动查找时,super.__getattribute__钩子会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 (1).__str__
'1'
Run Code Online (Sandbox Code Playgroud)

在这种情况下同样存在的问题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'
Run Code Online (Sandbox Code Playgroud)

然而,在缺少元类的情况下,str(x)视为等效可能会有所帮助type(x).__str__(x).但虽然(可能)有帮助,但这是不正确的.

  • @tjanson 是的,有几个内置函数绕过了 `__getattribute__`(参见 [`__getattribute__`](https://docs.python.org/3/reference/datamodel.html#object.__getattribute__) 文档中的注释)。但是没有完整的列表,只是提到它是出于“性能原因”而这样做的。 (2认同)