无法在“对象”类的实例上设置属性
- 2024-12-27 08:47:00
- admin 原创
- 127
问题描述:
因此,我在回答这个问题时使用 Python 进行了一些尝试,我发现这是无效的:
o = object()
o.attr = 'hello'
由于AttributeError: 'object' object has no attribute 'attr'
。但是,对于从 object 继承的任何类,它都是有效的:
class Sub(object):
pass
s = Sub()
s.attr = 'hello'
打印s.attr
按预期显示“hello”。为什么会这样?Python 语言规范中哪一项规定不能为 vanilla 对象分配属性?
有关其他解决方法,请参阅如何创建对象并向其添加属性?。
解决方案 1:
为了支持任意属性赋值,对象需要一个__dict__
:与对象关联的字典,其中可以存储任意属性。否则,就没有地方放置新属性。
的实例object
不携带——如果它携带,在可怕的循环依赖问题出现之前(因为,像大多数其他东西一样,继承自;-),这将使Python 中的每个对象都背负一个字典,这意味着每个当前没有或不需要字典的对象都会有许多字节的开销(本质上,所有没有任意可分配属性的对象都没有或不需要字典)。__dict__
`dict`object
例如,使用优秀的pympler
项目(您可以从这里通过 svn 获取它),我们可以进行一些测量……:
>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16
您不会希望每个都int
占用 144 个字节而不是 16 个字节,对吗?-)
现在,当你创建一个类(从任何类继承)时,事情就会发生变化……:
>>> class dint(int): pass
...
>>> asizeof.asizeof(dint(23))
184
...现在__dict__
添加了(另外,还有一点开销) - 因此dint
实例可以具有任意属性,但是您需要为这种灵活性付出相当大的空间成本。
那么,如果您int
只想要一个额外属性foobar
,该怎么办?这种情况很少见,但 Python 确实为此提供了一种特殊机制……
>>> class fint(int):
... __slots__ = 'foobar',
... def __init__(self, x): self.foobar=x+100
...
>>> asizeof.asizeof(fint(23))
80
...请注意,它不如那么小int
!(或者甚至是两个int
s,一个是 theself
和一个 the self.foobar
-- 第二个可以重新分配),但肯定比 好得多dint
。
当类具有__slots__
特殊属性(字符串序列)时,语句class
(更准确地说,默认元类type
)不会为该类的每个实例配备__dict__
(因此具有具有任意属性的能力),而只是配备一组有限的、严格的“插槽”(基本上是每个可以保存对某个对象的一个引用的位置)具有给定的名称。
作为失去灵活性的交换,每个实例可以获得很多字节(可能只有当您有无数个实例在四处移动时才有意义,但是,有这样的用例)。
解决方案 2:
正如其他回答者所说,object
没有__dict__
。是所有object
类型的基类,包括或。 因此 提供的任何内容对他们来说也是一种负担。 即使是像可选这样简单的东西也需要每个值都有一个额外的指针; 这会为系统中的每个对象浪费额外的 4-8 字节内存,但实用性非常有限。int
`str`object
__dict__
在 Python 3.3+ 中,您可以(并且应该)使用它,而不必创建虚拟类的实例types.SimpleNamespace
。
解决方案 3:
这仅仅是由于优化。
字典相对较大。
>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140
大多数(可能是所有)用 C 语言定义的类都没有用于优化的字典。
如果你查看源代码,你会发现有很多检查来查看对象是否有字典。
解决方案 4:
因此,在调查我自己的问题时,我发现了有关 Python 语言的这一点:你可以从 int 之类的东西继承,并且你会看到相同的行为:
>>> class MyInt(int):
pass
>>> x = MyInt()
>>> print x
0
>>> x.hello = 4
>>> print x.hello
4
>>> x = x + 1
>>> print x
1
>>> print x.hello
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: 'int' object has no attribute 'hello'
我猜想最后的错误是因为 add 函数返回一个 int,所以我必须重写类似__add__
这样的函数才能保留我的自定义属性。但是当我想到“对象”就像“int”时,这一切对我来说都是有意义的(我认为)。
解决方案 5:
https://docs.python.org/3/library/functions.html#object:
注意:
object
没有,__dict__
所以不能将任意属性分配给类的实例object
。
解决方案 6:
这是因为 object 是一种“类型”,而不是类。通常,C 扩展中定义的所有类(例如所有内置数据类型以及 numpy 数组等)都不允许添加任意属性。
解决方案 7:
这是(在我看来)Python 的一个基本限制 - 您无法重新打开类。但我认为实际问题是由于在 C 中实现的类无法在运行时修改...子类可以,但基类不能。