需求:
某网络游戏中,定义了玩家类Player(id,name,status,...)每有一个在线玩家,在服务器程序内则有一个Player的实例,当在线人数很多时,将产生大量实例(如百万级)
如何降低这些大量实例的内存开销?
思路:
定义类的__slots__属性,它是用来声明实例属性名字的列表(关闭动态绑定)。
代码:
e.py:
class Player(object):
def __init__(self,uid,name,status=0,level=1):
self.uid = uid
self.name = name
self.stat = status
self.level = level
class Player2(object):
__slots__ = ['uid','name','stat','level'] # 声明类实例化对象所有的属性名,禁止实例属性的动态绑定。即该实例没有了__dict__属性。
def __init__(self,uid,name,status=0,level=1):
self.uid = uid
self.name = name
self.stat = status
self.level = level
==============================================
测试:
In [3]: from e import Player, Player2
In [4]: p1 = Player('001','Jim')
In [5]: p2 = Player2('002','Tom')
In [6]: dir(p1)
Out[6]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'level',
'name',
'stat',
'uid']
In [7]: dir(p2)
Out[7]:
['__class__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'level',
'name',
'stat',
'uid']
In [8]: set(dir(p1)) - set(dir(p2)) # 取集合的差集,这里是p1比p2多的属性
Out[8]: {'__dict__', '__weakref__'}
In [9]: set(dir(p2)) - set(dir(p1))
Out[9]: {'__slots__'}
In [10]: p1.__dict__ # 这里__dict__是一个动态的字典
Out[10]: {'uid': '001', 'name': 'Jim', 'stat': 0, 'level': 1}
In [11]: p1.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-11-54f8378113e3> in <module>
----> 1 p1.x
AttributeError: 'Player' object has no attribute 'x'
In [12]: p1.x = 123 # 可以动态的增加属性
In [13]: p1.x
Out[13]: 123
In [14]: p1.__dict__
Out[14]: {'uid': '001', 'name': 'Jim', 'stat': 0, 'level': 1, 'x': 123}
In [15]: p1.__dict__['y'] = 99
In [16]: p1.y
Out[16]: 99
In [17]: del p1.__dict__['x'] # 删除属性
In [18]: p1.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-18-54f8378113e3> in <module>
----> 1 p1.x
AttributeError: 'Player' object has no attribute 'x'
In [19]: import sys
In [20]: sys.getsizeof(p1.__dict__) # 获取该属性所占用的内存空间,所以禁用该属性后占用的内存空间减少
Out[20]: 368
In [21]: p2.x = 123
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-21-2c5f7091e7e8> in <module>
----> 1 p2.x = 123
AttributeError: 'Player2' object has no attribute 'x'
In [22]: sys.getsizeof(p2.__slot__)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-22-d68996e840e6> in <module>
----> 1 sys.getsizeof(p2.__slot__)
AttributeError: 'Player2' object has no attribute '__slot__'
In [23]: sys.getsizeof(p2.__slots__)
Out[23]: 96
=====================================================================
>>> 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('0001','Jim',20)
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'level', 'name', 'uid']
>>> dir(p2)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'level', 'name', 'uid']
>>> set(dir(p1)) - set(dir(p2))
{'__weakref__', '__dict__'}
>>> p1.x = 100
>>> p1.y = 200
>>> p1.x
100
>>> p1.xy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Player1' object has no attribute 'xy'
>>> p1.y
200
>>> p1.__dict__
{'uid': '0001', 'name': 'Jim', 'level': 20, 'x': 100, 'y': 200}
>>> p1.__dict__['z'] = 300
>>> p1.z
300
>>> p1.__dict__.pop('x')
100
>>> p1.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Player1' object has no attribute 'x'
>>> import sys
>>> sys.getsizeof(p1.__dict__)
648
>>> sys.getsizeof(p1.name)
52
>>> sys.getsizeof(p1.level)
28
>>> sys.getsizeof(p1.uid)
53
>>> p2.x = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Player2' object has no attribute 'x'
>>> p2.name='abc'
=====================================================================================================
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
import tracemalloc
tracemalloc.start()
# start
la = [Player1(1,2,3) for _ in range(100000)]
#lb = [Player2(1,2,3) for _ in range(100000)]
# end
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('filename') # 统计整个文件占用内存的大小,通过对比可以发现lb占用的内在更小
for stat in top_stats[:10]:print(stat)