Après avoir regardé le discours sur la gestion de la mémoire Python de Nina Zahkarenko à Pycon2016 ( lien ), il semblait que la méthode dunder __slots__
était un outil pour réduire la taille de l'objet et accélérer la recherche d'attributs.
Je m'attendais à ce qu'une classe normale soit la plus grande, tandis qu'un __slots __ code L'approche> /
namedtuple
permettrait d'économiser de l'espace. Cependant, une expérience rapide avec sys.getsizeof ()
semble suggérer le contraire:
$ python3.7 example.py Class: 56 Slots: 72 Named Tuple: 80
Sortie du terminal:
from collections import namedtuple from sys import getsizeof class Rectangle: '''A class based Rectangle, with a full __dict__''' def __init__(self, x, y, width, height): self.x = x self.y = y self.width = width self.height = height class SlotsRectangle: '''A class based Rectangle with __slots__ defined for attributes''' __slots__ = ('x', 'y', 'width', 'height') def __init__(self, x, y, width, height): self.x = x self.y = y self.width = width self.height = height NamedTupleRectangle = namedtuple('Rectangle', ('x', 'y', 'width', 'height')) NamedTupleRectangle.__doc__ = 'A rectangle as an immutable namedtuple' print(f'Class: {getsizeof(Rectangle(1,2,3,4))}') print(f'Slots: {getsizeof(SlotsRectangle(1,2,3,4))}') print(f'Named Tuple: {getsizeof(NamedTupleRectangle(1,2,3,4))}')
3 Réponses :
La fonction sys.getsizeof ()
ne fait probablement pas ce que vous pensez qu'elle fait; cela ne fonctionne pas pour les objets complexes, comme les classes personnalisées.
Regardez cette réponse pour une méthode de calcul de la mémoire taille des objets; peut-être que cela vous aide. J'ai copié le code de cette réponse ici, mais l'explication complète se trouve dans la réponse que j'ai liée.
import sys from numbers import Number from collections import Set, Mapping, deque try: # Python 2 zero_depth_bases = (basestring, Number, xrange, bytearray) iteritems = 'iteritems' except NameError: # Python 3 zero_depth_bases = (str, bytes, Number, range, bytearray) iteritems = 'items' 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 return elif isinstance(obj, (tuple, list, Set, deque)): size += sum(inner(i) for i in obj) elif isinstance(obj, Mapping) or hasattr(obj, iteritems): size += sum(inner(k) + inner(v) for k, v in getattr(obj, iteritems)()) # Check for custom object instances - may subclass above too if 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 size return inner(obj_0)
Merci pour la réponse @Ralf, je pense que sys.getsizeof ()
remplit mon cas d'utilisation. Mon objet rectangle contient quatre variables qui peuvent faire référence à des objets ailleurs sur le tas managé de Python. L'empreinte mémoire de x
n'est pas pertinente pour moi, cela pourrait être une liste []
avec des milliers d'éléments. J'ai cru comprendre que l'attribut x
n'était qu'une référence. Ce que je m'attendais à voir était une réduction de la charge mémoire lors de l'utilisation des slots par rapport à une classe sans.
"Canaliser mon Raymond H intérieur", +1
Le problème avec les slots est que vous devez lire à propos des slots .
L'autre chose est qu'ils affectent la taille de la classe
:
rect = Rectangle(1,2,3,4) rect.extra_field = dict() # wild right? print(f'(Object) Class: {getsizeof(rect)}') # still 56
Il existe une variante plus compacte avec la bibliothèque recordclass :
from recordclass import dataobject class Rectangle(dataobject): x:int y:int width:int height:int >>> r = Rectangle(1,2,3,4) >>> print(sys.getsizeof(r)) 48
Il a moins d'empreinte mémoire que celui basé sur __slots__
car il ne participe pas au ramasse-miettes cyclique (l'indicateur Py_TPFLAGS_HAVE_GC n'est pas défini, donc PyGC_Head
(24 octets) ne pas besoin du tout).
getsizeof
ne rapporte pas la taille des structures de données référencées par l'objet de classe; seulement l'objet de classe lui-même.