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

Why can't you add attributes to object in python?

本问题已经有最佳答案,请猛点这里访问。

(用python shell编写)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> 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
>>>

为什么对象不允许您向其添加属性?


注意,object实例没有__dict__属性:

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

在派生类中演示此行为的示例:

1
2
3
4
5
6
7
8
>>> 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上的文件:

[...] The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

编辑:为了回答托马斯的评论,OP的测试班是一个"老式"的班。尝试:

1
2
3
4
5
6
7
8
>>> 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__,这是防止动态分配属性的原因。我已经重新整理了我的答案,使它更清晰(把第二段移到顶部)。


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

1
2
3
4
5
6
7
8
>>> 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'

iirc,这与__dict__属性的存在有关,或者更准确地说,当对象没有__dict__属性时,setattr()爆炸。