__slots__适用于需要构建数百万以上众多对象,且内存容量较为紧张,同时实例的属性简单、固定且不用动态增加的场景
1、先看python中的简单对象int、float、字符串占用的内存,以及由这些类型构成的list所占用的内存
a = 1
print('整数1占用的内存为:', a.__sizeof__(), 'byte') # 整数1占用的内存为: 28 byte
print('变量a的地址为:', id(a), '\n') # 变量a的地址为: 139851337872608
b = 1
print('变量b的地址为:', id(b)) # 变量b的地址为: 139851337872608
print('为何id(a) = id(b)?', '\n') # 为何id(a) = id(b)?
m = 0.01
print('浮点数0.01占用的内存为:', m.__sizeof__(), 'byte') # 浮点数0.01占用的内存为: 24 byte
s = 'python 内存占用'
print('字符串占用的内存:', s.__sizeof__(), 'byte', '\n') # 字符串占用的内存: 96 byte
list_item = [a, b, m, s]
print('列表占用的内存:', list_item.__sizeof__(), 'byte', '\n') # 列表占用的内存: 72 byte
# 为何由整数、浮点数、字符串构成的列表所占用的内存,小于这些元素占用内存的总和?
s = 'python 内存占用 增加字符串的长度'
print('新的字符串内容占用的内存:', s.__sizeof__(), 'byte', '\n') # 新的字符串内容占用的内存: 114 byte
list_item = [a, b, m, s]
print('列表占用的内存:', list_item.__sizeof__(), 'byte', '\n') # 列表占用的内存: 72 byte
# 为何列表中的字符串占用的内存增加了,但列表的占用内存不变?
python的sys模块中的getsizeof()、以及内置方法__sizeof__()对于python中的常规对象如int、float、string,能够正确的获取其占用的内存大小,但对于list、dict等,只会计算该对象的“指针”所占的空间,并不会把所指向的内容的内存大小也计算在内
对此,官方文档给出了一种途径,即定义一个函数,通过递归的寻找容器中的所有引用并计算其内存占用,加总后返回即可,并且也给出了定义函数的源码,源码如下:
from __future__ import print_function
from sys import getsizeof, stderr
from itertools import chain
from collections import deque
try:
from reprlib import repr
except ImportError:
pass
def total_size(o, handlers={}, verbose=False):
""" Returns the approximate memory footprint an object and all of its contents.
Automatically finds the contents of the following builtin containers and
their subclasses: tuple, list, deque, dict, set and frozenset.
To search other containers, add handlers to iterate over their contents:
handlers = {SomeContainerClass: iter,
OtherContainerClass: OtherContainerClass.get_elements}
"""
dict_handler = lambda d: chain.from_iterable(d.items())
all_handlers = {tuple: iter,
list: iter,
deque: iter,
dict: dict_handler,
set: iter,
frozenset: iter,
}
all_handlers.update(handlers) # user handlers take precedence
seen = set() # track which object id's have already been seen
default_size = getsizeof(0) # estimate sizeof object without __sizeof__
def sizeof(o):
if id(o) in seen: # do not double count the same object
return 0
seen.add(id(o))
s = getsizeof(o, default_size)
if verbose:
print(s, type(o), repr(o), file=stderr)
for typ, handler in all_handlers.items():
if isinstance(o, typ):
s += sum(map(sizeof, handler(o)))
break
return s
return sizeof(o)
print('列表占用的内存:', total_size(list_item), 'byte', '\n') # 列表占用的内存: 262 byte
2、对于dict对象,看一下dict_obj.__sizeof__()和字典实际占用的内存对比
p1_dict = {"name": 'zhao', "age": 60, "gender": 0, "decs": 'mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm'}
print('p1_dict 内存占用对比:', p1_dict.__sizeof__(), 'byte vs', total_size(p1_dict), 'byte')
# p1_dict 内存占用对比: 216 byte vs 641 byte
p2_dict = {"name": 'sun', "age": 6, "gender": 1, "decs": 'mmmmmmmmmmmmmmmnnnnnmnmn'}
print('p2_dict 内存占用对比:', p2_dict.__sizeof__(), 'byte vs', total_size(p2_dict), 'byte')
# p2_dict 内存占用对比: 216 byte vs 634 byte
p3_dict = {"name": 'li', "age": 18, "gender": 0, "decs": 'nnn'}
print('p3_dict 内存占用对比:', p3_dict.__sizeof__(), 'byte vs', total_size(p3_dict), 'byte', '\n')
# p3_dict 内存占用对比: 216 byte vs 608 byte
3、对比字典与类对象的内存占用
class Person:
def __init__(self, name, age, gender, decs):
self.name = name
self.age = age
self.gender = gender
self.decs = decs
p1_obj = Person('zhao', 60, 0, 'mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm')
print('p1_obj 内存占用对比:', p1_obj.__sizeof__(), 'byte vs', total_size(p1_obj), 'byte')
# p1_obj 内存占用对比: 32 byte vs 56 byte
p2_obj = Person('sun', 6, 1, 'mmmmmmmmmmmmmmmnnnnnmnmn')
print('p2_obj 内存占用对比:', p2_obj.__sizeof__(), 'byte vs', total_size(p2_obj), 'byte')
# p2_obj 内存占用对比: 32 byte vs 56 byte
p3_obj = Person('li', 18, 0, 'nnn')
print('p3_obj 内存占用对比:', p3_obj.__sizeof__(), 'byte vs', total_size(p3_obj), 'byte', '\n')
# p3_obj 内存占用对比: 32 byte vs 56 byte
可见,对于类实例对象,不管是__size__()方法,或者官方给的定义函数total_size()都无法准确算出类实例对象占用的实际内存
4、trancemalloc
python标准库trancemalloc,可以统计内存使用情况
python中使用字典能够提高查询效率,不过是用空间复杂度换取的时间复杂度
import tracemalloc
class Person:
def __init__(self, name, age, gender, decs):
self.name = name
self.age = age
self.gender = gender
self.decs = decs
tracemalloc.start() # start
p1_obj = Person('zhao', 60, 0, 'mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm')
# p2_obj = Person('sun', 6, 1, 'mmmmmmmmmmmmmmmnnnnnmnmn')
# p3_obj = Person('li', 18, 0, 'nnn')
# end
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('filename')
for stat in top_stats[:4]:
print(stat)
# /home/10333752@zte.intra/team/program_learning/learning/temp1.py:0: size=96 B, count=2, average=48 B
import tracemalloc
class PersonA:
__slots__ = ['name', 'age', 'gender', 'decs']
def __init__(self, name, age, gender, decs):
self.name = name
self.age = age
self.gender = gender
self.decs = decs
tracemalloc.start() # start
p1_obj = PersonA('zhao', 60, 0, 'mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm')
# p2_obj = PersonA('sun', 6, 1, 'mmmmmmmmmmmmmmmnnnnnmnmn')
# p3_obj = PersonA('li', 18, 0, 'nnn')
# end
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('filename')
for stat in top_stats[:4]:
print(stat)
# /home/10333752@zte.intra/team/program_learning/learning/temp3.py:0: size=72 B, count=1, average=72 B
__slots__的使用会阻止类实例产生__dict__来保存实例的属性