关于python:无法设置对象类的属性

Can't set attributes of object class

所以,我在回答这个问题的时候和python一起玩,我发现这是无效的:

1
2
o = object()
o.attr = 'hello'

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

1
2
3
4
5
class Sub(object):
    pass

s = Sub()
s.attr = 'hello'

打印s.attr会按预期显示"你好"。为什么会这样?在Python语言规范中,什么指定了您不能将属性分配给普通对象?


为了支持任意属性分配,一个对象需要一个__dict__:一个与该对象相关联的dict,其中可以存储任意属性。否则,就没有地方可以放置新属性。

object的一个实例不携带__dict__--如果它是这样的话,在可怕的循环依赖问题(因为dict和大多数其他东西一样继承了object--之前),这将使python中的每个对象都有一个dict,这意味着每个对象的开销为许多字节,而当前没有或需要一个dict(本质上,所有没有任意可分配属性的对象都没有或需要dict)。

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

1
2
3
4
5
>>> from pympler import asizeof
>>> asizeof.asizeof({})
144
>>> asizeof.asizeof(23)
16

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

现在,当您创建一个类(从任何类继承)时,事情会发生变化…

1
2
3
4
>>> class dint(int): pass
...
>>> asizeof.asizeof(dint(23))
184

…现在增加了__dict__(再加上一点开销)--所以dint实例可以具有任意属性,但是您为此灵活性付出了相当大的空间成本。

那么,如果您只需要一个额外属性foobarints,该怎么办?这是一种罕见的需求,但python确实为此提供了一种特殊的机制…

1
2
3
4
5
6
>>> class fint(int):
...   __slots__ = 'foobar',
...   def __init__(self, x): self.foobar=x+100
...
>>> asizeof.asizeof(fint(23))
80

…还不如江户的那么小,当心!(甚至是两个int,一个是self,一个是self.foobar,第二个可以重新分配),但肯定比dint要好得多。

当类具有__slots__特殊属性(字符串序列)时,那么class语句(更准确地说,默认元类type并没有为该类的每个实例配备__dict__(因此具有任意属性的能力),只是一组有限的、刚性的"slots"(基本上放置在它们都可以用给定的名称保存对某个对象的一个引用。

作为失去灵活性的交换,每个实例可以获得大量的字节(可能只有当您有无数的实例四处游荡时才有意义,但是,有这样的用例)。


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

在python 3.3+中,您可以(并且应该)使用types.SimpleNamespace来代替执行虚拟类的实例。


这仅仅是由于优化。

口述比较大。

1
2
3
>>> import sys
>>> sys.getsizeof((lambda:1).__dict__)
140

在C中定义的大多数(可能全部)类没有用于优化的dict。

如果您查看源代码,您将看到有许多检查来查看对象是否有dict。


因此,在研究我自己的问题时,我发现了关于Python语言的这一点:您可以从int之类的东西继承,并且可以看到相同的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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"时,这一切对我(我想)都是有意义的。


这是因为对象是"类型",而不是类。一般来说,在C扩展中定义的所有类(如所有内置数据类型和numpy数组等)都不允许添加任意属性。


这是(imo)Python的一个基本限制——不能重新打开类。不过,我认为实际的问题是由于在C中实现的类在运行时不能被修改…子类可以,但不能是基类。