例如,在某网络游戏中,定义了玩家类Player(id, name, level, ...)
。每有一个在线玩家,在服务器程序内则有一个Player的实例,当在线人数很多时,将产生大量实例(如百万级)。
要求:降低大量实例的内存开销。
解决方案:
定义类的__slots__
属性,声明实例有哪些属性(关闭动态绑定)。
- 对于类的
__slots__
属性:
>>> class Player1: def __init__(self, uid, name, level): self.uid = uid self.name = name self.level = level>>> class Player2: __slots__ = ['uid', 'name', 'level'] def __init__(self, uid, name, level): self.uid = uid self.name = name self.level = level>>> p1 = Player1('0001', 'Jim', 20)>>> p2 = Player2('0002', 'Jim', 20)
>>> set(dir(p1)) - set(dir(p2)){'__dict__', '__weakref__'}>>> p1.x = 100>>> p1.y = 200>>> p1.__dict__{'uid': '0001', 'name': 'Jim', 'level': 20, 'x': 100, 'y': 200}
__weakref__
属性用于弱引用,它并不消耗很多内存。弱引用的用途之一是实现大对象的缓存或者映射,由于是缓存或映射,对象不需要独立存在。
>>> import sys>>> sys.getsizeof(p1.__dict__)368>>> sys.getsizeof(p1.uid) + sys.getsizeof(p1.name) + sys.getsizeof(p1.level)133>>> sys.getsizeof(p2.uid) + sys.getsizeof(p2.name) + sys.getsizeof(p2.level)133
主要消耗内存的是__dict__
属性,它用于维护动态添加的属性。
>>> p2.x =100Traceback (most recent call last): File "", line 1, in <module> p2.x =100AttributeError: 'Player2' object has no attribute 'x'>>> p2.name'Jim'
类的__slots__
属性用于声明实例的属性,关闭实例属性的动态添加。
- 方案示例:
import tracemallocclass Player1: def __init__(self, uid, name, level): self.uid = uid self.name = name self.level = levelclass Player2: __slots__ = ['uid', 'name', 'level'] def __init__(self, uid, name, level): self.uid = uid self.name = name self.level = level tracemalloc.start() #startl1 = [Player1(1, 2, 3) for _ in range(100000)]#l2 = [Player2(1, 2, 3) for _ in range(100000)]snapshot = tracemalloc.take_snapshot() #endtop_stats = snapshot.statistics('filename')for stat in top_stats[:10]: print(stat)
注释l2时,运行结果:
0: size=16.8 MiB, count=299996, average=59 B #占用内存为16.8 MiB 0: size=64 B, count=1, average=64 B
注释l1时,运行结果:
0: size=7056 KiB, count=100002, average=72 B #占用内存为7056 KiB 0: size=64 B, count=1, average=64 B
由此可见,当创建大量重复实例时,可通过定义类的__slots__
属性来节省内存。