Python __slots__ 内存优化实战与副作用分析
本文通过《Fluent Python》案例验证Python中__slots__的内存节省效果,显示50万个实例可大幅降低内存占用。同时深入剖析其副作用:子类需重定义__slots__、限制动态属性添加及弱引用支持问题。结论指出,对于实例数不足万级的场景,__slots__并非必要,需根据业务规模权衡使用。
前言
相信许多资深Python开发者都读过那篇极具吸引力的文章《Saving 9 GB of RAM with Python's slots》,其中作者利用__slots__将内存占用从25.5GB降至16.2GB。在当时看来,这种简单方式实现了30%的内存缩减,效果令人惊叹。由于作者未披露具体业务场景和代码细节,我们将基于《Fluent Python》中的示例进行验证,以确认其实际效果是否如描述般显著。
from __future__ import print_function
import resource
class A(object):
def __init__(self):
self.a = 'string'
self.b = 10
self.c = True
class B(object):
__slots__ = ['a', 'b', 'c']
def __init__(self):
self.a = 'string'
self.b = 10
self.c = True
def test(cls):
mem_init = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
l = []
for i in range(500000):
l.append(cls())
mem_final = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
del l
print('Class: {}:\n'.format(getattr(cls, '__name__')))
print('Initial RAM usage: {:14,}'.format(mem_init))
print(' Final RAM usage: {:14,}'.format(mem_final))
print('-' * 20)
if __name__ == '__main__':
import sys
test(globals()[sys.argv[1].upper()])
我们分别运行以下两个类的测试代码:
❯ python mem_test.py a Class: A: Initial RAM usage: 4,890,624 Final RAM usage: 200,454,144 -------------------- ❯ python mem_test.py b Class: B: Initial RAM usage: 4,919,296 Final RAM usage: 60,235,776
两种方法初始化后的初始内存略有差异,但该差异相对于总内存量极小,可忽略不计。由此得出的结论是:
使用__slots__可使内存使用量减少约3.5倍!# 该数值通过公式 (200 - 4) / ((60 - 4) * 1.0) 计算得出
那么,使用__slots__是否非常有必要?事实上,创建500000个实例的场景在现实中较为罕见,是否使用应完全取决于具体业务需求,切忌以偏概全。因为(此处需重点注意)使用__slots__会引入以下副作用:
- 每个继承的子类都必须重新定义一遍__slots__。
- 实例只能包含在__slots__中定义的属性,这会影响编程的灵活性。例如,若因某种原因想给instance动态设置一个新属性(如instance.a = 1),但由于a未在__slots__中定义,程序将直接报错。此时你必须不断修改__slots__或采用其他迂回方法来解决。
- 实例无法支持弱引用(weakref)目标,除非显式将__weakref__加入__slots__中。
第三点可能较难理解,下面通过一个示例进行说明:
In [2]: %pycat ref_example.py from weakref import ref class A(object): __slots__ = ['b'] def __init__(self): self.b = 1 class B(object): __slots__ = ['b', '__weakref__'] def __init__(self): self.b = 1 In [3]: from ref_example import * In [4]: a = A() In [5]: r = ref(a) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-6-75a6d689c8b3> in <module>() ----> 1 r = ref(a) TypeError: cannot create weak reference to 'A' object In [6]: b = B() In [7]: r = ref(b) In [8]: r Out[8]: <weakref at 0x109199578; to 'B' at 0x10919f890>
因此,对于实例数量不超过万级别的类,使用__slots__通常不太值得。
PS:《Fluent Python》中的观点更为激进,认为实例数小于百万级别的场景均不值得使用__slots__。
总结
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对本站的支持。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









