在Python中,哪些对象可以动态添加属性?

7
在Python中,我可以向之前定义的类C添加属性。但是,我无法向list添加属性 - 结果的错误消息解释说这是因为list是内置类型。
>>> class C: pass
...
>>> C.foo = 1
>>> C.foo
1

>>> list.foo = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'list'

同样地,可以向 C 的实例添加属性,但不能向 list 的实例添加属性。然而,在该情况下,错误信息更加模糊。
>>> o = C()
>>> o.bar = 2
>>> o.bar
2

>>> o = []
>>> o.bar = 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'bar'

为什么我不能向list实例中添加成员?这是因为list是内置类型吗?
更一般地说,Python中哪些对象可以动态添加属性?

我猜想这指的是任何不是内置类型的东西。 - Abhishek Patel
强相关:https://dev59.com/YWw15IYBdhLWcg3wO5WX - cs95
通常不是内置类型。唯一的小惊喜是您可以向普通函数添加属性。请搜索PEP 232。 - Dave Rove
2个回答

2

当在对象上设置任意属性时,这实际上是改变了对象的__dict____dict__是一个存储对象所有成员的字典。因此,为了向对象添加成员,有两个条件:

  1. 对象需要有一个__dict__
  2. 对象的__dict__需要是可变的。

以下类型的多种原因可能不符合其中之一的条件,这些类型通常不允许对象修改:

  • 内置的本地类型是用本地代码实现的,并具有固定的成员集,已经包含在其本地对象定义中。例如,intstrlistbytes等,大概是大部分(但并非全部)内置类型
  • 来自外部库的本地类型,例如numpy的东西。
  • 使用__slots__的任何Python类。Slots是一种明确的方式,可以关闭__dict__并将其替换为一组固定的成员。因此,这将防止以后向对象添加内容(按设计)。

如何检测您是否可以修改对象?好的方法是检查上述条件。对象是否具有__dict__

>>> class Example:
        pass
>>> class SlotsExample:
        __slots__ = ['x']

>>> hasattr(Example(), '__dict__')
True
>>> hasattr(SlotsExample(), '__dict__')
False
>>> hasattr(list, '__dict__')
True
>>> hasattr([], '__dict__')
False

无论__dict__是否是真正的字典:

>>> isinstance(Example().__dict__, dict)
True
>>> isinstance(list.__dict__, dict)
False

0
简而言之,这取决于__setattr__魔术方法。
Python属性通过__getattr____setattr__检索。如果你有一个没有它们的类,则应用默认值。(Slots 有点不同。)
对于内置/扩展类型(在CPython中的HEAPTYPE),默认情况下无法添加任何其他属性,但是您可以通过提供另一个函数来覆盖它。(使用纯Python时无法实现。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接