Weekly backup.
This commit is contained in:
99
20220527165926-l_attribut_de_classe_slots.org
Normal file
99
20220527165926-l_attribut_de_classe_slots.org
Normal file
@@ -0,0 +1,99 @@
|
||||
:PROPERTIES:
|
||||
:ID: 05864d6a-7a05-4c11-af14-4faebfdd1926
|
||||
:mtime: 20220527182207
|
||||
:ctime: 20220527165926
|
||||
:END:
|
||||
#+title: L'attribut de classe __slots__
|
||||
|
||||
* Introduction
|
||||
Par défaut, chaque objet possède l'attibut ~__dict__~ stockant la liste de ses attributs sous forme de ~dict~. Il est
|
||||
ainsi possible d'ajouter des attributs à un objet en dehors de la méthode ~__init__~ :
|
||||
|
||||
#+BEGIN_SRC python :results output
|
||||
from sys import getsizeof
|
||||
|
||||
class Dummy:
|
||||
|
||||
def __init__(self, a: int, b: int) -> None:
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
dummy = Dummy(1, 2)
|
||||
try:
|
||||
print(dummy.__slots__)
|
||||
except AttributeError as e:
|
||||
print(e)
|
||||
|
||||
print(f'{dummy.__dict__=}')
|
||||
|
||||
dummy.c = 3
|
||||
print(f'{dummy.__dict__=}')
|
||||
|
||||
print(f'{getsizeof(dummy) + getsizeof(dummy.__dict__)}')
|
||||
#+END_SRC
|
||||
#+RESULTS:
|
||||
: 'Dummy' object has no attribute '__slots__'
|
||||
: dummy.__dict__={'a': 1, 'b': 2}
|
||||
: dummy.__dict__={'a': 1, 'b': 2, 'c': 3}
|
||||
: 152
|
||||
|
||||
L'utilisation d'un ~dict~ pour stocker les attributs de chaque objet permet cette souplesse mais a pour conséquence un
|
||||
usage mémoire qui pourrait être amélioré.
|
||||
|
||||
C'est pour cela que l'attributs ~__slots__~ a été ajouté par Python3.
|
||||
|
||||
* Usage
|
||||
L'attribut de classe ~__slots__~ est un tuple (taille inférieure à celle d'un ~dict~ mais /imutable/) contenant les
|
||||
attributs des instances.
|
||||
#+BEGIN_SRC python :results output
|
||||
from sys import getsizeof
|
||||
|
||||
class Dummy:
|
||||
|
||||
__slots__ = ('a', 'b', 'c')
|
||||
|
||||
def __init__(self, a: int, b: int) -> None:
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
dummy = Dummy(1, 2)
|
||||
try:
|
||||
print(dummy.__dict__)
|
||||
except AttributeError as e:
|
||||
print(e)
|
||||
|
||||
print(f'{dummy.__slots__=}')
|
||||
|
||||
dummy.c = 3
|
||||
print(f'{dummy.__slots__=}')
|
||||
|
||||
try:
|
||||
dummy.d = 4
|
||||
except AttributeError as e:
|
||||
print(e)
|
||||
|
||||
print(f'{getsizeof(dummy) + getsizeof(dummy.__slots__)}')
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: 'Dummy' object has no attribute '__dict__'
|
||||
: dummy.__slots__=('a', 'b', 'c')
|
||||
: dummy.__slots__=('a', 'b', 'c')
|
||||
: 'Dummy' object has no attribute 'd'
|
||||
: 120
|
||||
** Avantages
|
||||
L'usage de ~__slots__~ permet de réduire la taille des objets. Néanmoins, l'écart se réduit plus le nombre d'attributs
|
||||
augmente. Les ~__slots__~ sont donc utiles pour les classes ayant peu d'attributs et un grand nombre d'instances.
|
||||
|
||||
Le principal avantage de ~__slots__~ réside dans les performances (environ 15% plus rapide), conséquence du temps
|
||||
d'accès plus rapide au contenu d'un ~tuple~ que d'un ~__dict__~.
|
||||
** Inconvénients
|
||||
L'absence de ~__dict__~ empèche l'ajout dynamique d'attributs.
|
||||
|
||||
L'usage de ~__slots__~ ajoute les règles suivantes quant à l'héritage :
|
||||
* Les slots d’une classe mère s’ajoutent à ceux de la classe fille,
|
||||
* Il ne peut exister qu’une seule classe mère avec une séquence de slots non vide,
|
||||
* Si une des classes membres de l'arbre d'héritage ne déclare pas de ~__slots__~, mêmes vides, les instances auront un attribut ~__dict__~ (perte de l'optimisation).
|
||||
|
||||
* Références
|
||||
* [[https://www.invivoo.com/les-slots-une-optimisation-meconnue/][Les slots, une optimisation méconnue - Invivoo]]
|
Reference in New Issue
Block a user