Python : Getters et setters passez votre chemin !

Les properties Python

Vous le savez tous, les développeurs Python accordent beaucoup de valeur à la lisibilité de leur code. C'est l'essence même d'un code réputé Pythonique. La lisibilité doit être considérée comme beaucoup plus importante que la durée d'écriture de code. Un code est écrit une fois, mais sera certainement lu des dizaines voire des centaines de fois dans la vie d'un logiciel.

Dans les objets Python il n'existe pas de propriété privée et nous n'avons jamais besoin des getters ou des setters. Tous les objets sont fait pour qu'on accède directement à leurs propriétés. J'entends déjà les cris des développeurs JAVA : "Mais et l'encapsulation des données ??? Si on veut refactoriser et contrôler la propriété comment on fait sans getter et setter ??? ".

En Python il existe ce qu'on appelle les properties (je ne mets pas le mot français, car vous aurez de bien meilleurs résultats dans vos recherches Google avec le mot anglais). Voici les avantages de ce système :

Plus simplement, les properties reviennent à ajouter vos getters et setters lorsque vous en avez besoin et pas avant. C'est une solution autrement plus élégante que les getters et setters.

La pratique

Pour les manipulations sur les objets suivants, nous allons définir un montant de 100 pour l'utilisateur. Nous ajouterons 10 à ce montant puis nous l'afficherons. Le montant ici ne signifie rien, l'exemple est uniquement technique et non sémantique.

Prenons un exemple, un code Python qu'on aura codé à la sauce JAVA (notez la syntaxe des noms de méthodes aussi à la sauce JAVA, avec Python préférez le no_camel_case pour les fonctions et méthodes):

class User():
def __init__(self, amount):
self.amount = amount

def getAmount(self):
return self.amount

def setAmount(self, amount):
self.amount = amount

Et le code de la manipulation :

user = User(100)
user.setAmount(user.getAmount()+10)
print (user.getAmount()

Ce qui donnerait en code Python Pythonique :

class User():
def __init__(self, amount):
self.amount = amount

Et le code de la manipulation (que je trouve plus lisible) :

user = User(100)
user.amount += 10
print (user.amount)

Disons que plus tard dans le développement on veuille enregistrer un montant 10 fois plus élevé, mais qu'on veuille afficher le montant 10 fois plus faible que le montant enregistré (soit le même montant que celui d'origine). Là les développeurs Java se diront "Nous avons bien fait d'encapsuler la variable amount car nous n'avons que les getters et setters à modifier".

class User():
def __init__(self, amount):
self.amount = amount

def getAmount(self):
return self.amount/10

def setAmount(self, amount):
self.amount = amount * 10

En Python tant qu'on n'a pas besoin d'encapsuler nos variables on le fait pas. Mais lorsqu'on a besoin de le faire, on n'a pas besoin de refactoriser tout notre code. Voici la nouvelle classe qui va utiliser les properties pour encapsuler notre variable amount :

class User():
def __init__(self, amount):
self.__amount = amount

@property
def amount(self):
return self.__amount

@amount.setter
def amount(self, amount):
self.__amount = amount

Les codes de manipulations sont restés les mêmes pour les deux types de code Java et Python.

En définitive, en Python on peut encapsuler nos propriétés uniquement lorsque c'est nécessaire. Cela permet d'économiser du temps en terme de développement ou de génération de getters et setters. Cela rend également le code plus lisible ce qui est tout de même primordial pour nous !