为什么在Python中不能向对象添加属性?

69

(使用Python shell编写)

>>> o = object()
>>> o.test = 1

Traceback (most recent call last):
  File "<pyshell#45>", line 1, in <module>
    o.test = 1
AttributeError: 'object' object has no attribute 'test'
>>> class test1:
    pass

>>> t = test1()
>>> t.test

Traceback (most recent call last):
  File "<pyshell#50>", line 1, in <module>
    t.test
AttributeError: test1 instance has no attribute 'test'
>>> t.test = 1
>>> t.test
1
>>> class test2(object):
    pass

>>> t = test2()
>>> t.test = 1
>>> t.test
1
>>> 

为什么对象不允许您向其中添加属性?
2个回答

57

请注意,一个对象实例没有__dict__属性:

>>> dir(object())
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__']

一个示例来说明派生类中的这种行为:

>>> class Foo(object):
...     __slots__ = {}
...
>>> f = Foo()
>>> f.bar = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'

引用来自 slots 文档的说法:

[...] __slots__ 声明接受一个实例变量序列,并保留足够的空间在每个实例中保存每个变量的值。由于不会为每个实例创建__dict__,因此节省了空间。

编辑:回答评论区的ThomasH,原始测试类是一种“旧式”的类。请尝试:

>>> class test: pass
...
>>> getattr(test(), '__dict__')
{}
>>> getattr(object(), '__dict__')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__dict__'

你会注意到有一个__dict__实例。对象类可能没有定义__slots__,但结果是一样的:缺少__dict__,这就阻止了属性的动态分配。我重新组织了我的答案以使它更清晰(将第二段移到顶部)。


3
你的意思是“object”类在某种程度上使用了“__slots__”来防止实例成员的赋值。这是一种假设还是事实(即在“object”的实现中)?因为仅仅缺少“__dict__”并不能说明问题。当使用dir()调用OP的“test1”类的实例时,“__dict__”也会缺失,但仍然允许实例成员的赋值。 - ThomasH
1
@ThomasH,请查看我添加到答案中的注释。 - ars

5

好问题,我猜这可能与object是一个内置/扩展类型有关。

>>> class test(object):
...  pass
...
>>> test.test = 1
>>> object.test = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'object'

据我所知,这与一个 __dict__ 属性的存在有关,更确切地说,setattr() 在对象没有 __dict__ 属性时会崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接