Python 中自定义类的实例是可以动态添加属性的,但是内置的类的实例则不可以:
>>> class A():pass
...
>>> a = A()
>>> a.name = 'myclassA'
>>> lst = list()
>>> lst.name = 'mylst'
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'list' object has no attribute 'name'
上例中,自定义类 A 的实例 a 可以动态添加 name 属性,但 list 的实例 lst 则不可以。事实上,自定义的类若想禁止动态添加属性,则需借助 __slots__ 属性,默认情况下,自定义类是没有该属性的,比如 A 类的属性:
>>> dir(A)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
我们为自定义类 B 手动添加 __slots__ 属性,并将其值设为空:
>>> class B():
... __slots__ = {}
...
>>> b = B()
>>> b.name = 'myclassB'
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'B' object has no attribute 'name'
如上,此时 B 的实例 b 便无法像 a 一样动态添加 name 属性。
另外,对于具有 __slots__ 属性的类,该类和其实例都会缺失 __dict__ 属性,我们可以发现:实例 b 比实例 a 多了__slots__属性,而实例 a 要比实例 b 多了__dict__和 name 属性:
>>> dir(a)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
>>> dir(b)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__']
>>> b.__dict__
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'B' object has no attribute '__dict__'
>>> a.__dict__
{'name': 'myclassA'}
>>> b.__slots__
{}
>>> a.__slots__
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'A' object has no attribute '__slots__'
此外,__slot__的值若为非空,则类的实例只能添加__slots__值中定义的属性,而不能添加其他属性:
>>> class C():
... __slots__ = {'name'}
...
>>> c = C()
>>> c.name = 'MyclassC'
>>> c.other = 'dobi'
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'C' object has no attribute 'other'
>>> c.__slots__
{'name'}
如上,自定义类 C 的实例 c 只能添加 name 属性,而不能添加 other 属性;另外,即便可以添加 name 属性,C 类和 c 仍无 __dict__ 属性,不过类 C 类可以动态的添加'__dict__' 属性,但实例 c 不可以:
>>> c.__dict__
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'C' object has no attribute '__dict__'
>>> C.__dict__
mappingproxy({'__doc__': None, '__module__': '__main__', 'name': , '__slots__': {'name'}})
因为 c 是基于 C 类创建,而 C 类则是基于其超类创建,而 C 类的超类默认是没有定义__slots__的。