【问题标题】:Why isn't setattr(super(), ...) equivalent to super.__setattr__(...)?为什么 setattr(super(), ...) 不等同于 super.__setattr__(...)?
【发布时间】:2019-05-15 07:53:59
【问题描述】:

根据this answer:

setattr(instance, name, value) 是instance.__setattr__(name, value) 的语法糖

但是:

class C:
    def __init__(self):
        # OK.
        super().__setattr__("foo", 123)

        # AttributeError: 'super' object has no attribute 'foo'
        setattr(super(), "foo", 123)

c = C()

什么给了?他们不应该做同样的事情吗?

【问题讨论】:

    标签: python python-3.x


    【解决方案1】:

    您链接到的答案掩盖了一些重要的细节。长话短说,setattr 绕过了super 的魔法,因此它尝试在super() 代理对象本身而不是self 上设置属性。


    setattr(a, b, c) 不是a.__setattr__(b, c) 的语法糖。 setattr 和常规属性赋值都通过直接搜索对象类型的 method resolution order(类和所有超类的仔细排序列表)来查找 __setattr__ 方法,绕过实例字典和 __getattribute__/ __getattr__ 钩子。

    相比之下,a.__setattr__(b, c) 只是对__setattr__ 执行常规属性查找,因此它会遍历__getattribute__ 和__getattr__,并检查实例字典,除非__getattribute__ 拒绝。


    super 实现了一个自定义的__getattribute__ 方法来实现它的属性魔法。 super().__setattr__ 使用这个钩子,找到C 的超类__setattr__ 并绑定self,得到一个用于设置self 属性的方法对象。

    setattr(super(), ...) 绕过__getattribute__。它执行前面提到的直接 MRO 搜索,通过 super 的 MRO 进行搜索,产生一个用于设置 超级实例本身的属性的方法对象。 super 实例没有 __dict__ 来存储任意属性数据,因此尝试设置 foo 属性失败并返回 TypeError。

    【讨论】:

    • 那么,super() 的自负在setattr(super(), ...) 下是站不住脚的,因为super() 实际上并没有它假装的__mro__?这个限制在任何地方都有正式记录吗? super() docs 有一个注释“未定义使用语句或运算符的隐式查找”,但我不认为这是一个隐式查找。
    • @Maxpm:这与常规属性访问中涉及的查找一样隐含。尽管名称非常相似,setattr 并不是明确的 __setattr__ 查找。
    • 至于super 文档,super 文档真的很糟糕。他们甚至没有说足够的内容来解释super 的普通用法是如何工作的。也就是说,他们确实解释了super 通过实现__getattribute__ 起作用,并且__getattribute__ 不会为这些类型的操作触发。
    • @user2357112:您的回答无法解释setattr(...) 和instance.__setattr__(...) 之间的区别。
    • @EthanFurman:我添加了更多解释。在解释不够和过度解释太多令人分心的细节之间很难找到适当的平衡。我可以继续讨论常规属性查找、特殊方法查找和super.__getattribute__ 之间的区别,讨论tp_setattro 以及通常如何优化 MRO 搜索,讨论描述符协议,讨论描述符协议如何通常也被优化掉,等等。
    猜你喜欢
    • 1970-01-01
    • 2021-08-21
    • 2014-04-13
    • 1970-01-01
    • 1970-01-01
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    • 2019-04-26
    相关资源
    最近更新 更多