Raymond Hettinger présentait en 2013 (vidéo slides) les outils intégrés de Python permettant de créer des classes.
Du temps est passé depuis, mais ça reste un classique.
Voici mes notes.
1) Hériter d'object
(en Python 2)
L'ajout de classes au langage Python a été essentiellement une réflexion après coup.
Les New-Style Classes sont arrivées en Python 2 et nécessitent d'hériter d'object
pour corriger les bogues des Classic-Style Classes.
En Python 3 il n'y a plus que des New-Style Classes, plus besoin d'hériter explicitement de object
.
Vous pouvez lire la prose de Guido van Rossum dans The Inside Story on New-Style Classes et New-style Classes pour avoir tous les détails.
2) __init__
n'est pas un constructeur
La méthode dunder init définit comment les nouvelles instances sont initialisées après leur création.
Son rôle est de peupler les variables d'instance.
3) self
comme premier argument explicite
Les méthodes normales des classes ont self
comme premier argument explicite.
L'appellation self
est une convention culturelle en Python.
Le caractère explicite de self
est expliqué dans la documentation et défendu dans Why explicit self has to stay.
4) Variables d'instance vs variables de classe
Les variables d'instance référencent des données uniques à une instance.
Les variables de classe référencent des données partagées par toutes les instances.
5) Exposer des attributs est courant et normal
Les variables de classe sont déclarées directement dans le corps de la classe.
Elles sont ensuite accessibles comme des attributs depuis la classe elle-même.
Exemple en Django :
class Aircraft(models.Model):
ENGINE_TURBOPROP = '1'
ENGINE_JET = '2'
ENGINE_PISTON = '3'
ENGINE_CHOICES = (
(ENGINE_TURBOPROP, "Turboprop"),
(ENGINE_JET, "Jet"),
(ENGINE_PISTON, "Piston"),
)
# ...
dict(Aircraft.ENGINE_CHOICES)
{'1': 'Turboprop', '2': 'Jet', '3': 'Piston'}
6) Pas de variables privées, rôle de __
Il n'existe pas de variables privées en Python.
C'est culturel. Ça choque parfois les gens qui ont appris l'objet avec Java ou C++, mais :
Il existe tout de même un cas d'usage légitime pour les variable privées : éviter des conflits de noms avec des sous-classes. Python utilise le name mangling pour ça, détecté en préfixant les variables concernées avec un dunder (__
).
7) @classmethod
pour créer des constructeurs alternatifs
import math
class Circle:
def __init__(self, radius):
self.radius = radius
@classmethod
def from_bbd(cls, bbd):
radius = bbd / 2.0 / math.sqrt(2.0)
return cls(radius)
c = Circle.from_bbd(25.1)
c.radius
8.874190103891172
Il est important de passer cls
comme premier argument explicite pour supporter l'héritage !
Quelques exemples : dict.fromkeys()
, int.from_bytes()
, datetime.utcnow()
, datetime.fromtimestamp()
, datetime.now()
etc.
8) @staticmethod
attache des fonctions à des classes
Si vous devez créer une instance juste pour appeler une fonction, c'est que vous avez besoin de @staticmethod
!
@staticmethod
attache des fonctions à des classes.
Pas de self
en premier argument ! La méthode statique peut être invoquée soit depuis la classe, soit depuis une instance.
On s'en sert surtout pour améliorer la repérabilité et pour s'assurer que la méthode soit utilisée dans un contexte approprié.
9) @property
invoque une méthode lors de l'accès à un attribut
En décorant une méthode avec @property
, on peut y accéder comme un simple attribut, sans les ()
.
Ça permet de s'affranchir de l'écriture verbeuse de getters
et de setters
.
Ça donne aussi la possibilité de pouvoir appliquer des traitements sur un attribut de classe a posteriori sans que les utilisateurs de la classe n'aient besoin de modifier leur code.
Who learned something new? :)