"""
Simple function for determining the memory usage of common Python
values and objects.
"""
from __future__ import annotations
from typing import Any
import doctest
import sys
import struct
import collections.abc
[docs]def arch() -> int:
"""
Determine the architecture.
>>> arch() in [32, 64]
True
"""
return struct.calcsize('P') * 8
[docs]def sizeof(obj: Any, deep: bool = False, _exclude: set[int] = None) -> int:
"""
Estimate the memory consumption of a Python object (either
the root object itself exclusively or, in the case of a deep
traversal, the entire tree of objects reachable from it).
>>> sys.getsizeof([]) == sizeof([])
True
>>> sys.getsizeof(123) == sizeof(123)
True
>>> sys.getsizeof(123.0123) == sizeof(123.0123)
True
>>> sys.getsizeof(complex(2, -3)) == sizeof(complex(2, -3))
True
>>> sys.getsizeof(True) == sizeof(True)
True
>>> sys.getsizeof(None) == sizeof(None)
True
>>> sizeof('ab') == sizeof('a') + 1
True
>>> sizeof(bytes([1, 2])) + 1 == sizeof(bytes([1, 2, 3]))
True
>>> sizeof(bytearray([1, 2])) <= sizeof(bytearray([1, 2, 3]))
True
>>> mv0 = memoryview(bytes([1, 2]))
>>> mv1 = memoryview(bytes([1, 2, 3]))
>>> sizeof(mv0) == sizeof(mv1)
True
>>> xs = [1, 2, 3]
>>> ys = {'a':1, 'b':2, 'c':3}
>>> zs = {frozenset([1, 2, 3]): [1, 2, 3], frozenset(['a']): 'bc'}
>>> sys.getsizeof(xs) == sizeof(xs)
True
>>> sys.getsizeof(range(0,10)) == sizeof(range(0,10))
True
When the ``deep`` argument is ``True``, the total amount of
memory consumed by the object and all of its descendants by
reference is calculated (with deduplication).
>>> sys.getsizeof(xs) < sizeof(xs, deep=True)
True
>>> sizeof(set(xs), deep=True) > sys.getsizeof(set(xs))
True
>>> sizeof({123}) == sys.getsizeof({123})
True
>>> sizeof({123}, deep=True) > sys.getsizeof({123})
True
>>> sizeof([xs], deep=True) > sys.getsizeof([xs])
True
>>> sizeof([xs, ys, zs], deep=True) > 2 * sizeof([xs, xs, xs], deep=True)
True
>>> sizeof((xs, ys, zs), deep=True) > 2 * sizeof((xs, xs, xs), deep=True)
True
>>> sizeof([xs, xs], deep=True) < sizeof([xs, [1, 2, 3]], deep=True)
True
>>> sizeof([xs], deep=True, _exclude=set([id(xs)])) == sizeof([xs])
True
>>> sizeof(range(0,10), deep=True) > sizeof(range(0,10))
True
"""
if not deep or isinstance(obj, (
int, float, complex, bool,
str, bytes, bytearray, memoryview
)):
return sys.getsizeof(obj)
_exclude = set() if _exclude is None else _exclude
if id(obj) in _exclude:
return 0
if isinstance(obj, collections.abc.Mapping):
_exclude.add(id(obj))
iterator = obj.items() if isinstance(obj, dict) else obj.iteritems()
return sys.getsizeof(obj) + sum(
sizeof(k, deep=True, _exclude=_exclude) +\
sizeof(v, deep=True, _exclude=_exclude)
for (k, v) in iterator
)
if isinstance(obj, collections.abc.Container):
_exclude.add(id(obj))
return sys.getsizeof(obj) + sum(
sizeof(v, deep=True, _exclude=_exclude)
for v in obj
)
raise ValueError( # pragma: no cover
'object not supported by current version of sizeof'
)
if __name__ == '__main__':
doctest.testmod() # pragma: no cover