Skip to content

Instantly share code, notes, and snippets.

@ZaxR
Last active July 30, 2021 19:40
Show Gist options
  • Save ZaxR/1e571968aaae93519fa1e710261286bd to your computer and use it in GitHub Desktop.
Save ZaxR/1e571968aaae93519fa1e710261286bd to your computer and use it in GitHub Desktop.
Get total memory size footprint of an object in python, in a human-readable format
# OTHER COOL FUNCS #
import collections
from itertools import chain
from sys import getsizeof
from typing import List, Optional, Tuple, Union
def get_scaled_units(number: Union[float, int], unit_scale_map: Optional[List[Tuple]] = None) -> str:
"""Scale `number` to units that result in the lowest number above 1, given a `unit_scale_map`.
Args:
number: Any number which should scaled to units.
unit_scale_map: Mappings of numbers to the units they reflect.
Order must be monotonic increasing.
Defaults to data storage units of measure.
"""
DEFAULT_UNIT_SCALE_MAP = [
(1024 ** 0, 'B'),
(1024 ** 1, 'KB'),
(1024 ** 2, 'MB'),
(1024 ** 3, 'GB'),
(1024 ** 4, 'TB'),
(1024 ** 5, 'PB')
]
unit_scale_map = DEFAULT_UNIT_SCALE_MAP if not unit_scale_map else unit_scale_map
i = 0
while number > unit_scale_map[i + 1][0] and i < len(unit_scale_map) - 1:
i += 1
number /= unit_scale_map[i][0]
units = unit_scale_map[i][1]
return f"{round(number, 2)} {units}"
def get_total_size(object, human_readable=True, handlers=None):
"""Returns the approximate memory footprint of an object and all of its contents.
Useful because sys.getsizeof does not count nested objects.
Args:
object (see notes): Python object for which you want to find the size in bytes.
Container objects and or containers within container objects
are only supported for certain types. See `handlers`.
human_readable (bool): Determines if unit suffixes are used. Unit possibilities:
B (Byte), KB (Kilobyte), MB (Megabyte), GB (Gigabyte),
TB (Terabyte), and PB (Petabyte). Each step is 1024 of prior units.
handlers (dict): Instructions on how to handle container objects.
Default `handlers` include tuple, list, set, frozenset, and
collections.Mappings (such as dicts).
To add container types not available by default,
add handlers to iterate over their contents.
E.g. handlers={SomeContainerClass: iter,
OtherContainerClass: OtherContainerClass.get_elements}
Returns:
Byte size of `object` as an integer.
"""
handlers = handlers if handlers else {}
all_handlers = {**dict.fromkeys([tuple, list, set, frozenset], iter),
collections.Mapping: lambda d: chain.from_iterable(d.items())}
all_handlers.update(handlers) # user handlers take precedence
seen = set() # track which object id's have already been seen
default_size = getsizeof(0) # estimate size_of object without __sizeof__
def size_of(object):
if id(object) in seen: # do not double count the same object
return 0
seen.add(id(object))
size = getsizeof(object, default_size)
for typ, handler in all_handlers.items():
if isinstance(object, typ):
size += sum(map(size_of, handler(object)))
break
return size
size = size_of(object)
if human_readable:
return get_scaled_units(size)
return str(size)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment