春天到了,来给你的Python程序减减肥!
在执行 Python 程序时,内存中有大量对象,当多到一定程度时就可能出现内存问题(尤其是在内存较小的情况下)。在本文中,我们将讨论一下如何减少对象内存占用的方法,从而让你的 Python 程序“瘦下来”!
>>> ob = {'x':1, 'y':2, 'z':3}
>>> x = ob['x']
>>> ob['y'] = y
由于在 Python 3.6 中 dict 的实现采用了一组有序键,因此其结构更为紧凑,更深得人心。但是,让我们看看 dict 在内容中占用的空间大小:
>>> print(sys.getsizeof(ob))
240
class Point:
#
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
>>> ob = Point(1,2,3)
>>> x = ob.x
>>> ob.y = y
>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))
56 112
不难看出,由于实例的字典很大,所以实例依然占用了大量内存。
class Point:
__slots__ = 'x', 'y', 'z'
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
64
如此一来,内存中的对象就明显变小了:
>>> pprint(Point.__dict__)
mappingproxy(
....................................
'x': <member 'x' of 'Point' objects>,
'y': <member 'y' of 'Point' objects>,
'z': <member 'z' of 'Point' objects>})
为了自动化使用 __slots__ 创建类的过程,你可以使用库 namedlist(https://pypi.org/project/namedlist)。namedlist.namedlist 函数可以创建带有 __slots__ 的类:
>>> Point = namedlist('Point', ('x', 'y', 'z'))
还有一个包 attrs(https://pypi.org/project/attrs),无论是否使用 __slots__ 都可以利用这个包自动创建类。
>>> ob = (1,2,3)
>>> x = ob[0]
>>> ob[1] = y # ERROR
元组实例非常紧凑:
>>> print(sys.getsizeof(ob))
72
由于内存中的元组还包含字段数,因此需要占据内存的 8 个字节,多于带有 __slots__ 的类:
>>> Point = namedtuple('Point', ('x', 'y', 'z'))
class Point(tuple):
#
@property
def _get_x(self):
return self[0]
@property
def _get_y(self):
return self[1]
@property
def _get_z(self):
return self[2]
#
def __new__(cls, x, y, z):
return tuple.__new__(cls, (x, y, z))
>>> Point = recordclass('Point', ('x', 'y', 'z'))
>>> ob = Point(1, 2, 3)
类实例的结构也类似于 tuple,但没有 PyGC_Head:
>>> Point = make_dataclass('Point', ('x', 'y', 'z'))
class Point(dataobject):
x:int
y:int
z:int
这种方法创建的类实例不会参与循环垃圾回收机制。内存中实例的结构与带有 __slots__ 的类相同,但没有 PyGC_Head:
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
40
如果想访问字段,则需要使用特殊的描述符来表示从对象开头算起的偏移量,其位置位于类字典内:
mappingproxy({'__new__': <staticmethod at 0x7f203c4e6be0>,
.......................................
'x': <recordclass.dataobject.dataslotgetset at 0x7f203c55c690>,
'y': <recordclass.dataobject.dataslotgetset at 0x7f203c55c670>,
'z': <recordclass.dataobject.dataslotgetset at 0x7f203c55c410>})
cdef class Python:
cdef public int x, y, z
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
本例中实例占用的内存更小:
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
32
>>> Point = numpy.dtype(('x', numpy.int32), ('y', numpy.int32), ('z', numpy.int32)])
>>> points = numpy.zeros(N, dtype=Point)
>>> sys.getsizeof(points[0])
68
_往期文章推荐_
评论