问题描述
所以,我在回答 时正在玩 Python这个问题,我发现这是无效的:
o = object()o.attr = '你好'
由于 AttributeError: 'object' object has no attribute 'attr'
.但是,对于从对象继承的任何类,它都是有效的:
类子(对象):经过s = 子()s.attr = '你好'
打印 s.attr
按预期显示hello".为什么会这样?Python 语言规范中有什么规定不能将属性分配给普通对象?
为了支持任意属性赋值,对象需要一个__dict__
:一个与对象关联的dict,可以存储任意属性.否则,无处可放置新属性.
object
的实例不 携带 __dict__
—— 如果确实如此,那么在可怕的循环依赖问题之前(因为 dict
,像大多数其他东西一样,继承自 object
;-),这将使 Python 中的 每个 对象都带有一个 dict,这意味着开销当前没有或不需要字典的每个对象有 许多 个字节(本质上,所有没有可任意分配属性的对象都没有或不需要字典).
例如,使用优秀的 pympler
项目(你可以通过 svn 从 这里),我们可以做一些测量...:
>>>从 pympler 导入 asizeof>>>asizeof.asizeof({})144>>>asizeof.asizeof(23)16
您不会希望每个 int
占用 144 个字节而不是 16 个字节,对吧?-)
现在,当你创建一个类(从任何东西继承)时,事情就会改变......:
>>>类 dint(int):通过...>>>asizeof.asizeof(dint(23))184
...现在添加了 __dict__
is (加上,多了一点开销)——所以 dint
实例可以有任意属性,但您为这种灵活性付出了相当大的空间成本.
那么,如果你想要 int
只带有 one 额外属性 foobar
... 怎么办?这是一种罕见的需求,但 Python 确实为此目的提供了一种特殊的机制......
>>>类fint(int):... __slots__ = 'foobar',... def __init__(self, x): self.foobar=x+100...>>>asizeof.asizeof(fint(23))80
...不像int
那么相当,请注意!(甚至是两个 int
,一个是 self
,一个是 self.foobar
- 第二个可以重新分配),但肯定是比 dint
好多了.
当类有 __slots__
特殊属性(字符串序列),然后是 class
语句(更准确地说,默认元类,type
) 确实不为该类的每个实例配备 __dict__
(因此具有任意属性的能力),只是一组有限的、刚性的槽"(基本上每个地方都可以使用给定的名称保存对某个对象的一个引用.
作为失去灵活性的交换,您在每个实例中获得了大量字节(只有当您有数以万计的实例在运行时才有意义,但是,有用例).p>
So, I was playing around with Python while answering this question, and I discovered that this is not valid:
o = object()
o.attr = 'hello'
due to an AttributeError: 'object' object has no attribute 'attr'
. However, with any class inherited from object, it is valid:
class Sub(object):
pass
s = Sub()
s.attr = 'hello'
Printing s.attr
displays 'hello' as expected. Why is this the case? What in the Python language specification specifies that you can't assign attributes to vanilla objects?
To support arbitrary attribute assignment, an object needs a __dict__
: a dict associated with the object, where arbitrary attributes can be stored. Otherwise, there's nowhere to put new attributes.
An instance of object
does not carry around a __dict__
-- if it did, before the horrible circular dependence problem (since dict
, like most everything else, inherits from object
;-), this would saddle every object in Python with a dict, which would mean an overhead of many bytes per object that currently doesn't have or need a dict (essentially, all objects that don't have arbitrarily assignable attributes don't have or need a dict).
For example, using the excellent pympler
project (you can get it via svn from here), we can do some measurements...:
>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16
You wouldn't want every int
to take up 144 bytes instead of just 16, right?-)
Now, when you make a class (inheriting from whatever), things change...:
>>> class dint(int): pass
...
>>> asizeof.asizeof(dint(23))
184
...the __dict__
is now added (plus, a little more overhead) -- so a dint
instance can have arbitrary attributes, but you pay quite a space cost for that flexibility.
So what if you wanted int
s with just one extra attribute foobar
...? It's a rare need, but Python does offer a special mechanism for the purpose...
>>> class fint(int):
... __slots__ = 'foobar',
... def __init__(self, x): self.foobar=x+100
...
>>> asizeof.asizeof(fint(23))
80
...not quite as tiny as an int
, mind you! (or even the two int
s, one the self
and one the self.foobar
-- the second one can be reassigned), but surely much better than a dint
.
When the class has the __slots__
special attribute (a sequence of strings), then the class
statement (more precisely, the default metaclass, type
) does not equip every instance of that class with a __dict__
(and therefore the ability to have arbitrary attributes), just a finite, rigid set of "slots" (basically places which can each hold one reference to some object) with the given names.
In exchange for the lost flexibility, you gain a lot of bytes per instance (probably meaningful only if you have zillions of instances gallivanting around, but, there are use cases for that).
这篇关于无法在“对象"实例上设置属性班级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!