无法在“对象"实例的实例上设置属性班级 [英] Can't set attributes on instance of "object" class
问题描述
因此,我在回答的同时玩弄Python这个问题,我发现这是无效的:
So, I was playing around with Python while answering this question, and I discovered that this is not valid:
o = object()
o.attr = 'hello'
由于AttributeError: 'object' object has no attribute 'attr'
.但是,对于从对象继承的任何类,它都是有效的:
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'
打印s.attr
会按预期显示"hello".为什么会这样呢?在Python语言规范中,有什么规定不能将属性分配给香草对象?
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?
推荐答案
要支持任意属性分配,对象需要__dict__
:与该对象关联的字典,可以在其中存储任意属性.否则,就没有放置新属性的地方.
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.
object
的实例不会不携带__dict__
-如果确实存在,则应在可怕的循环依赖问题之前出现(因为dict
与大多数其他事物一样,是从object
;-),这将使dict中的每个对象受约束,这意味着每个当前不具有或不需要对象的对象的许多字节的开销字典(基本上,所有不具有任意可分配属性的对象都没有或不需要字典).
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).
例如,使用出色的pympler
项目(您可以通过svn从这里),我们可以进行一些测量...:
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
您不希望每个int
占用144个字节,而不仅仅是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
...现在添加了__dict__
(加上更多的开销)-因此dint
实例可以具有任意属性,但是您为此付出了相当大的空间成本灵活性.
...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.
那如果您只想要foobar
...的int
,该怎么办?这是一种罕见的需求,但是Python确实为此目的提供了一种特殊的机制...
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
...不是相当像int
一样小,请注意! (甚至两个int
,一个self
和一个self.foobar
-可以重新分配第二个),但是肯定比dint
好得多.
...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
.
当类具有__slots__
特殊属性(字符串序列)时,class
语句(更确切地说,默认元类type
)不会不为每个实例配备该类具有__dict__
(因此具有任意属性的能力),只是具有给定名称的一组有限的刚性槽"(基本上每个位置可以容纳一个对某个对象的引用).
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).
这篇关于无法在“对象"实例的实例上设置属性班级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!