Python 是一个动态语言,可以动态的给实例或类增减属性或方法,给类添加的属性会影响到前后所有创建的实例。但是使用 __slots__
属性可以限定类或实例属性和方法,如果没有 __slots__
的话实例的属性和方法包含在实例的 __dict__
字典中,类的属性和方法包含在类的 __dict__
字典中。
在使用 __slots__
按常规写法可能会出现的问题大概有
- AttributeError: 'Xxx' object has no attribute 'yyy'
- AttributeError: 'Xxx' object attribute 'yyy' is read-only
- ValueError: 'yyy' in __slots__ conflicts with class variable
我们来看下面的例子
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 |
>>> class Cat: ... lags = 4 ... def __init__(self): ... self.eyes = 2 ... ... def walk(self): ... pass ... ... >>> Cat.__dict__ mappingproxy({'__module__': '__main__', 'lags': 4, '__init__': <function Cat.__init__ at 0x106816700>, 'walk': <function Cat.walk at 0x106817920>, '__dict__': <attribute '__dict__' of 'Cat' objects>, '__weakref__': <attribute '__weakref__' of 'Cat' objects>, '__doc__': None}) >>> c1 = Cat() >>> c1.__dict__ {'eyes': 2} >>> c1.miaow = lambda: 'hello' >>> c1.__dict__ {'eyes': 2, 'miaow': <function <lambda> at 0x106d6fce0>} >>> Cat.ears = 2 >>> c1.ears 2 >>> c1.__dict__ {'eyes': 2, 'miaow': <function <lambda> at 0x106d6fce0>} >>> Cat.__dict__ mappingproxy({'__module__': '__main__', 'lags': 4, '__init__': <function Cat.__init__ at 0x106816700>, 'walk': <function Cat.walk at 0x106817920>, '__dict__': <attribute '__dict__' of 'Cat' objects>, '__weakref__': <attribute '__weakref__' of 'Cat' objects>, '__doc__': None, '__getattribute__': <slot wrapper '__getattribute__' of 'object' objects>, 'ears': 2}) |
类或实例可以随意的添加属性和方法
如果我们引入 __slots__
来限定属性或方法
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 29 30 31 32 33 34 35 36 37 38 39 |
>>> class Cat: ... __slots__ = ('lags', 'eyes') ... def __init__(self): ... self.eyes = 2 ... ... ... >>> Cat.__dict__ mappingproxy({'__module__': '__main__', '__slots__': ('lags', 'eyes'), '__init__': <function Cat.__init__ at 0x10699ef20>, 'eyes': <member 'eyes' of 'Cat' objects>, 'lags ': <member 'lags' of 'Cat' objects>, '__doc__': None}) >>> c1 = Cat() >>> c1.__dict__ Traceback (most recent call last): File "<input>", line 1, in <module> c1.__dict__ AttributeError: 'Cat' object has no attribute '__dict__' >>> c1.lags Traceback (most recent call last): File "<input>", line 1, in <module> c1.lags AttributeError: 'Cat' object has no attribute 'lags' >>> c1.eyes 2 >>> c1.lags = 4 >>> c1.ears = 2 Traceback (most recent call last): File "<input>", line 1, in <module> c1.ears = 2 ^^^^^^^ AttributeError: 'Cat' object has no attribute 'ears' >>> c1.miaow = lambda: 'hello' Traceback (most recent call last): File "<input>", line 1, in <module> c1.miaow = lambda: 'hello' ^^^^^^^^ AttributeError: 'Cat' object has no attribute 'miaow' >>> c1.lags = lambda: 'hello' >>> c1.lags <function <lambda> at 0x106d6c7c0> |
引入了 __slots__
后实例不再有 __dict__
属性,只能添加在 __slots__
中列出的属性或方法。添加没在 __slots__
中的属性或方法时会报错误
AttributeError: 'Xxx' object has no attribute 'yyy'
在初始化函数中 __init__(self)
中也是一样的,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
>>> class Cat: ... __slots__ = ('lags', 'eyes') ... def __init__(self): ... self.ears = 2 ... ... ... >>> c1 = Cat() Traceback (most recent call last): File "<input>", line 1, in <module> c1 = Cat() ^^^^^ File "<input>", line 4, in __init__ self.ears = 2 ^^^^^^^^^ AttributeError: 'Cat' object has no attribute 'ears' |
__slots__
中没有 ears
, 所以不能在初始化方法或外部动态添加该属性
但是定义类时声明的方法不在 __slots__
约束内
1 2 3 4 5 6 7 8 |
>>> class Cat: ... __slots__ = ('lags', 'eyes') ... def walk(self): ... pass ... ... >>> c1 = Cat() >>> c1.walk() |
__slots__
也不约束通过类动态添加属性或方法
1 2 3 4 5 6 |
>>> class Cat: ... __slots__ = ('lags', 'eyes') ... ... >>> Cat.ears = 2 >>> |
在 __slots__
中不能包含类变量,比如
1 2 3 4 5 6 7 8 9 |
>>> class Cat: ... __slots__ = ('lags') ... lags = 4 ... ... Traceback (most recent call last): File "<input>", line 1, in <module> class Cat: ValueError: 'lags' in __slots__ conflicts with class variable |
声明的没定义在 __slots__
中的类变量对实例方法是只读的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
>>> class Cat: ... __slots__ = ('lags') ... eyes = 2 ... def __init__(self): ... self.eyes = 3 ... ... ... >>> c1 = Cat() Traceback (most recent call last): File "<input>", line 1, in <module> c1 = Cat() ^^^^^ File "<input>", line 5, in __init__ self.eyes = 3 ^^^^^^^^^ AttributeError: 'Cat' object attribute 'eyes' is read-only |
但是声明在没定义在 __slots__
中的类变量通过实例来修改也不行,但可以通过类属性来修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
>>> class Cat: ... __slots__ = ('lags') ... eyes = 2 ... ... >>> c1 = Cat() >>> c1.eyes = 3 Traceback (most recent call last): File "<input>", line 1, in <module> c1.eyes = 3 ^^^^^^^ AttributeError: 'Cat' object attribute 'eyes' is read-only >>> Cat.eyes = 3 >>> c1.eyes 3 |
定义在 __slots__
中的属性或方法在 IDE 中会有智能提示。
__slots__
只能作用在当前类中,不会影响到子类,子类需定义自己的 __slots__
.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
>>> class Animal: ... __slots__ = ('lags') ... ... >>> class Cat(Animal): ... pass ... >>> Animal.__slots__ 'lags' >>> Cat.__slots__ 'lags' >>> c = Cat() >>> c.eyes = 2 |
__slots__
对于实例是只读的,通过类的 __slots__
属性可修改,但不改变原有的约束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
>>> class Cat: ... __slots__ = ('lags', 'eyes') ... ... >>> c.__slots__ = ('lags', 'eyes', 'ears') Traceback (most recent call last): File "<input>", line 1, in <module> c.__slots__ = ('lags', 'eyes', 'ears') ^^^^^^^^^^^ AttributeError: 'Cat' object attribute '__slots__' is read-only >>> Cat.__slots__ = ('lags', 'eyes', 'ears') >>> c = Cat() >>> c.ears = 2 Traceback (most recent call last): File "<input>", line 1, in <module> c.ears = 2 ^^^^^^ AttributeError: 'Cat' object has no attribute 'ears' >>> c.__slots__ ('lags', 'eyes', 'ears') >>> Cat.__slots__ ('lags', 'eyes', 'ears') |
__slots__
还可声明为 list, 或省略圆括号的 tuple 形式
1 2 3 4 5 6 7 8 9 10 |
>>> class Cat: ... __slots__ = ['lags', 'eyes'] ... ... >>> class Cat: ... __slots__ = 'lags', 'eyes' ... ... >>> type(Cat.__slots__) <class 'tuple'> |
另外,用 __slots__
避免了使用 __dict__
记录实例属性和方法,可节约一些内存
本文链接 https://yanbin.blog/python-__slots__-notes/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
lazy cat.