EmptyBytes type scaling notes28 int +4 bytes about every 30 powers of 237 bytes +1 byte per additional byte49 str +1-4 per additional character (depending on max width)48 tuple +8 per additional item64 list +8 for each additional224 set 5th increases to 736; 21nd, 2272; 85th, 8416; 341, 32992240 dict 6th increases to 368; 22nd, 1184; 43rd, 2280; 86th, 4704; 171st, 9320136 func def does not include default args and other attrs1056 class def no slots56 class inst has a __dict__ attr, same scaling as dict above888 class def with slots16 __slots__ seems to store in mutable tuple-like structurefirst slot grows to 48, and so on.
/* This over-allocates proportional to the list size, making room* for additional growth. The over-allocation is mild, but is* enough to give linear-time amortized behavior over a long* sequence of appends() in the presence of a poorly-performing* system realloc().* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...* Note: new_allocated won't overflow because the largest possible value* is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.*/new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
历史数据
Python 2.7分析,用guppy.hpy和sys.getsizeof确认:
Bytes type empty + scaling notes24 int NA28 long NA37 str + 1 byte per additional character52 unicode + 4 bytes per additional character56 tuple + 8 bytes per additional item72 list + 32 for first, 8 for each additional232 set sixth item increases to 744; 22nd, 2280; 86th, 8424280 dict sixth item increases to 1048; 22nd, 3352; 86th, 12568 *120 func def does not include default args and other attrs64 class inst has a __dict__ attr, same scaling as dict above16 __slots__ class with slots has no dict, seems to store inmutable tuple-like structure.904 class def has a proxy __dict__ structure for class attrs104 old class makes sense, less stuff, has real dict though.
import sysfrom types import ModuleType, FunctionTypefrom gc import get_referents
# Custom objects know their class.# Function objects seem to know way too much, including modules.# Exclude modules as well.BLACKLIST = type, ModuleType, FunctionType
def getsize(obj):"""sum size of object & members."""if isinstance(obj, BLACKLIST):raise TypeError('getsize() does not take argument of type: '+ str(type(obj)))seen_ids = set()size = 0objects = [obj]while objects:need_referents = []for obj in objects:if not isinstance(obj, BLACKLIST) and id(obj) not in seen_ids:seen_ids.add(id(obj))size += sys.getsizeof(obj)need_referents.append(obj)objects = get_referents(*need_referents)return size
import sysfrom numbers import Numberfrom collections import dequefrom collections.abc import Set, Mapping
ZERO_DEPTH_BASES = (str, bytes, Number, range, bytearray)
def getsize(obj_0):"""Recursively iterate to sum size of object & members."""_seen_ids = set()def inner(obj):obj_id = id(obj)if obj_id in _seen_ids:return 0_seen_ids.add(obj_id)size = sys.getsizeof(obj)if isinstance(obj, ZERO_DEPTH_BASES):pass # bypass remaining control flow and returnelif isinstance(obj, (tuple, list, Set, deque)):size += sum(inner(i) for i in obj)elif isinstance(obj, Mapping) or hasattr(obj, 'items'):size += sum(inner(k) + inner(v) for k, v in getattr(obj, 'items')())# Check for custom object instances - may subclass above tooif hasattr(obj, '__dict__'):size += inner(vars(obj))if hasattr(obj, '__slots__'): # can have __slots__ with __dict__size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))return sizereturn inner(obj_0)
import sys
def get_size(obj, seen=None):"""Recursively finds size of objects"""size = sys.getsizeof(obj)if seen is None:seen = set()obj_id = id(obj)if obj_id in seen:return 0# Important mark as seen *before* entering recursion to gracefully handle# self-referential objectsseen.add(obj_id)if isinstance(obj, dict):size += sum([get_size(v, seen) for v in obj.values()])size += sum([get_size(k, seen) for k in obj.keys()])elif hasattr(obj, '__dict__'):size += get_size(obj.__dict__, seen)elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):size += sum([get_size(i, seen) for i in obj])return size
import pygame as pgimport osimport psutilimport time
process = psutil.Process(os.getpid())pg.init()vocab = ['hello', 'me', 'you', 'she', 'he', 'they', 'we','should', 'why?', 'necessarily', 'do', 'that']
font = pg.font.SysFont("monospace", 100, True)
dct = {}
newMem = process.memory_info().rss # don't mind this lineStr = f'store ' + f'Nothing \tsurface use about '.expandtabs(15) + \f'0\t bytes'.expandtabs(9) # don't mind this assignment too
usedMem = process.memory_info().rss
for word in vocab:dct[word] = font.render(word, True, pg.Color("#000000"))
time.sleep(0.1) # wait a moment
# get total used memory of this script:newMem = process.memory_info().rssStr = f'store ' + f'{word}\tsurface use about '.expandtabs(15) + \f'{newMem - usedMem}\t bytes'.expandtabs(9)
print(Str)usedMem = newMem
在我的windows 10, python 3.7.3上,输出是:
store hello surface use about 225280 bytesstore me surface use about 61440 bytesstore you surface use about 94208 bytesstore she surface use about 81920 bytesstore he surface use about 53248 bytesstore they surface use about 114688 bytesstore we surface use about 57344 bytesstore should surface use about 172032 bytesstore why? surface use about 110592 bytesstore necessarily surface use about 311296 bytesstore do surface use about 57344 bytesstore that surface use about 110592 bytes