Python中类的__slots__属性
默认情况下,Python对象队象的每个实例(instance)都会有一个字典来存储该实例的属性,这样做的好处在于运行时期每个对象可以任意设置新的属性。而相对应的坏处是,当创建成百上千个这样的实例的时候回很浪费内存。所以引入__slots__
,用来指定实例只拥有固定的属性,因此python会给每个实例对象分配固定的内存空间,从而减少内存消耗。而且使用__slots__
可以加快属性的访问。
用法
__slots__
可以被设置成属性名称的字符串,可遍历的对象或者序列。
之前在看odoo源码缓存相关的内容时,看到过下面这个例子:
class ormcache_counter(object): """ Statistic counters for cache entries. """ __slots__ = ['hit', 'miss', 'err'] def __init__(self): self.hit = 0 self.miss = 0 self.err = 0 @property def ratio(self): return 100.0 * self.hit / (self.hit + self.miss or 1)
这里创建了一个用来记录每个方法缓存情况的对象,因为对于需要每个缓存的方法,都会创建一个该实例来记录缓存的状况(比如缓存用到或没用的次数等),所以为了节省内存加快访问速度这里指定了该对象拥有的三个属性。
测试
访问速度测试
timeit是python一个用来简单测试运行时间的模块,详细可参见官方文档。
# In Python2.7 # test1.py import timeit class Foo(object): __slots__ = 'foo', class Bar(object): pass slotted = Foo() not_slotted = Bar() def get_set_delete_fn(obj): def get_set_delete(): obj.foo = 'foo' obj.foo del obj.foo return get_set_delete # In REPL >>> from test1 import * >>> min(timeit.repeat(get_set_delete_fn(not_slotted))) min(timeit.repeat(get_set_delete_fn(not_slotted))) 0.24305510520935059 >>> min(timeit.repeat(get_set_delete_fn(slotted))) min(timeit.repeat(get_set_delete_fn(slotted))) 0.21287798881530762
可以看见,使用__slots__
的对象有更快的访问速度,虽然在python2.7中差别没有在python3中那么明显
内存占用参考
关于内存占用情况的测试我还没测,但可以参考 stackoverflow上的测试,我这里机(无)智(耻)地取个结果:
# 单位 bytes attrs __slots__ no slots declared + __dict__ none 16 64 (+ 280 if __dict__ referenced) one 56 64 + 280 two 64 64 + 280 six 96 64 + 1048 22 224 64 + 3352
可以明显看到内存占用减少的情况。
注意事项
__dict__
可以理解成类里面存储属性的字典,
- 当一个类A继承自一个没有定义
__slots__
的类B时,A是有__dict__
属性,这是再定义__slots__
属性没有意义, 不能达到限制内存的作用 - 当尝试给一个定义了
__slots__
的类,而没有定义__dict__
的类设置不在__slots__
指定的那些属性时,会导致一个AttributeError
其它注意请参照文档
相关推荐
YENCSDN 2020-11-17
lsjweiyi 2020-11-17
houmenghu 2020-11-17
Erick 2020-11-17
HeyShHeyou 2020-11-17
以梦为马不负韶华 2020-10-20
lhtzbj 2020-11-17
夜斗不是神 2020-11-17
pythonjw 2020-11-17
dingwun 2020-11-16
lhxxhl 2020-11-16
坚持是一种品质 2020-11-16
染血白衣 2020-11-16
huavhuahua 2020-11-20
meylovezn 2020-11-20
逍遥友 2020-11-20
weiiron 2020-11-16