无法设置对象类的属性

Sma*_*ery 81 python attributes language-design

所以,我在回答这个问题时正在玩Python ,我发现这是无效的:

o = object()
o.attr = 'hello'
Run Code Online (Sandbox Code Playgroud)

由于AttributeError: 'object' object has no attribute 'attr'.但是,对于从object继承的任何类,它是有效的:

class Sub(object):
    pass

s = Sub()
s.attr = 'hello'
Run Code Online (Sandbox Code Playgroud)

打印s.attr按预期显示"你好".为什么会这样?Python语言规范中的内容指定您不能将属性分配给vanilla对象?

Ale*_*lli 122

为了支持任意属性赋值,对象需要__dict__:与对象关联的字典,其中可以存储任意属性.否则,无处可新属性.

的实例object没有随身携带__dict__-如果它这样做了,可怕的循环依赖问题(因为之前dict,像其他所有的事情,从继承object;-),这将鞍每一个在Python对象有一个字典,这将意味着开销当前没有或不需要dict的每个对象的许多字节(实质上,所有没有任意可分配属性的对象都没有或不需要dict).

例如,使用优秀的pympler项目(你可以从这里通过svn获得它),我们可以做一些测量...:

>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16
Run Code Online (Sandbox Code Playgroud)

你不希望每个int人占用144个字节而不是16个,对吧? - )

现在,当你创建一个类(继承自任何东西)时,事情会改变......:

>>> class dint(int): pass
... 
>>> asizeof.asizeof(dint(23))
184
Run Code Online (Sandbox Code Playgroud)

...的__dict__ 现在又增加了(加,多一点开销) -所以一个dint实例可以有任意的属性,但是你付出相当的灵活性的空间成本.

所以,如果你想ints的只是一个额外的属性foobar...?这是一种罕见的需求,但Python确实为此提供了一种特殊的机制......

>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
... 
>>> asizeof.asizeof(fint(23))
80
Run Code Online (Sandbox Code Playgroud)

......不要那么int,请注意!(甚至两个ints,一个self和一个self.foobar- 第二个可以重新分配),但肯定比a好多了dint.

当类具有__slots__特殊属性(字符串序列),那么class语句(更准确地说,默认元类type)并没有装备有该类的每个实例__dict__(有任意属性,因此的能力),只是一个有限,具有给定名称的刚性"槽"集(基本上每个可以对一些对象保持一个引用的位置).

为了弥补灵活性的损失,你可以获得每个实例的大量字节(仅当你有数以万计的实例加入时才有意义,但是,有一些用例).

  • 这解释了机制是如何实现的,但没有解释为什么以这种方式实现.我可以想到至少有两三种方法来实现添加__dict__,它不会产生开销缺点,但会增加一些简单性. (5认同)
  • 对象的 `__dict__` 仅在第一次需要时创建,因此内存成本情况并不像 `asizeof` 输出看起来那么简单。(`asizeof` 不知道如何避免 `__dict__` 物化。)您可以看到在 [本示例](https://ideone.com/xVKqMC) 中需要时才实现 dict,并且您可以看到其中之一负责`__dict__` 实现的代码路径[这里](https://github.com/python/cpython/blob/v3.7.2/Objects/dictobject.c#L4288)。 (3认同)

Ant*_*ala 17

正如其他回答者所说,一个object没有__dict__.object所有类型的基类,包括intstr.因此,提供的任何东西也object将成为他们的负担.即使像可选的 那样简单,__dict__也需要为每个值添加额外的指针; 这会为系统中的每个对象浪费额外的4-8个字节的内存,这对于非常有限的实用程序.


在Python 3.3+中,您可以(而且应该)使用它types.SimpleNamespace来代替虚拟类的实例.