【问题标题】:Python 2.7: What does object.__setattr__ do under the hood?Python 2.7:object.__setattr__ 在底层做了什么?
【发布时间】:2017-12-04 21:04:51
【问题描述】:

在 Python 2.7 中,我可以声明一个新样式的类并使用 object.__setattr__ 设置属性:

class A (object):
    def __init__ (self):
        object.__setattr__(self, 'foo', 'bar')

A().foo # 'bar'

但是,这在旧式类中不起作用:

class B:
    def __init__ (self):
        object.__setattr__(self, 'foo', 'bar')

B().foo

TypeError: can't apply this __setattr__ to instance object

我知道旧式类不支持这种行为,但我不知道为什么它不起作用。

当我在一个新式类上调用 object.__setattr__ 而旧式类不能发生时,幕后发生了什么?

请注意,我没有将这段代码用于任何事情,我只是想理解它。

【问题讨论】:

  • object 是新式类的基础,所以不,您不能在不是从该基础继承的实例上使用 object.__setattr__
  • @MartijnPieters 我不明白为什么会这样 - 我在这里遗漏了一些关于它是如何工作的东西
  • 请注意,我知道只有在非常特殊的情况下才会使用 object.__setattr__ 而不是 setattr。不吝赐教,什么情况下你不想在这里使用setattr()
  • @MartijnPieters 请参阅this question。您也可以找到examples of this usage in the pandas core
  • 好吧,如果您不从object 继承,那一种情况不适用。例外情况是 MRO 中的中间类被跳过。

标签: python python-2.7 class attributes python-internals


【解决方案1】:

object.__setattr__ 方法包括一个特定的检查,以确保传入的self 确实是对象(或直接重用相同 C 函数的另一个对象)的(子类)。该测试不允许传入任何其他内容,包括旧式实例。

检查是为了防止object.__setattr__ 被用来修改内置类型(在其发现者之后被称为Carlo Verre hack。

如果没有显式检查(或者如果你编译 Python 来禁用它),你可以这样做:

>>> object.__setattr__(str, 'lower', str.upper)
>>> "This is why we can't have nice things".lower()
"THIS IS WHY WE CAN'T HAVE NICE THINGS"

请参阅hackcheck() functionoriginal Python-dev discussion

旧式实例对象没有通过测试只是一个副作用。

请注意,您想要调用object.__setattr__唯一原因是如果您有一个实现了您需要绕过的__setattr__ 方法的基类。对于旧式实例,您可以改用self.__dict__[name] = value

【讨论】:

  • 这是一个很好的答案,谢谢。我没有看到任何为什么的原因,从技术上讲,这个调用会失败。现在我明白了。
  • 当然,你仍然可以使用gc.get_referents(str.__dict__)[0]['lower'] = str.upperstr.lower 变成大写,所以如果你真的想要的话,使用内置类型仍然很容易。
  • @user2357112:是的,还有其他漏洞可以利用,也许这些漏洞也应该被关闭。想把它加入开发列表,看看是否值得关闭?
  • @user2357112:实际上,那个对我不起作用,至少在 2.7 中不起作用。在 3.6 中会导致段错误!
  • @MartijnPieters:Here's an Ideone demo 你可以比较一下,看看你做的事情是否与我做的不同。
【解决方案2】:

类型——不是旧式类,而是所有旧式实例的类型——是types.InstanceType。这种类型是用 C 编写的,并实现了自己的__setattr__。 Python 将拒绝将一种 C 类型的 __setattr__ 应用于具有不同 C 级别的 C 类型的实例 __setattr__,作为保护 C 数据结构一致性的安全措施。

【讨论】:

  • 然而这里的 C 数据结构是一样的;对于旧式属性,无论如何都没有可以挂钩到__set__ 的描述符,因此设置属性始终是__dict__[attrname] = attrvalue 操作。
  • @MartijnPieters:它们实际上并不完全相同——例如,InstanceType 不提供元数据,object.__setattr__ 需要首先定位实例的__dict__
  • 没错,确实,一个有in_dict,另一个有tp_dictoffset 偏移到插槽中。尽管如此,还是可以在内部进行抽象处理。
猜你喜欢
  • 1970-01-01
  • 2012-04-09
  • 2021-05-02
  • 2011-11-25
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多