Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Initialisation d’un attribut par une fonction dans __init__

+1 vote

Qu’est-ce qui est le plus pythonique:

class LaClasseAmericaine:
    def __init__(self):
        self.attribut = self._set_attribut()

    def _set_attribut(self):
        print("Je fais plein de choses pour créer cet attribut")
        return 42

Ou bien:

class LaClasseAction:
    def __init__(self):
        self._set_attribut()

    def _set_attribut(self):
        print("Je fais plein de choses pour créer cet attribut")
        self.attribut = 42

?

demandé 24-Nov-2016 par Ryzz (174 points)

J'ai envie de dire aucun des deux, et ce n'est pas intrinsèquement lié à Python : ce n'est pas une bonne chose d'avoir des traitements "lourds" dans un initialisateur de classe.

3 Réponses

+1 vote

Même remarque que debnet. En revanche, le camel case n’est pas très pythonique et on préférera écrire Laclasseaction plutôt que LaClasseAction.

répondu 24-Nov-2016 par etno712 (288 points)

Le camelcase c’est comme ça que j’interprète la PEP8:

Class names should normally use the CapWords convention.

Entièrement d'accord avec Ryzz. Le CamelCase est réservé justement pour les identifiants de classes. Le code est pep8 compliant.

+3 votes

Je suis d'accord avec debnet : ce n'est pas spécifique à python, et la réponse est ça dépend.

Personnellement, je choisis généralement la première pour les raisons suivantes :

  • l'assignation de l'attribut dans l'init est explicite, du coups rien qu'en le lisant on a une connaissance exacte de tous les attributs de l'objet. (bonus : le Zen of Python est content)
  • il est plus intuitif de manipuler la valeur, par exemple pour ajouter un paramètre au constructeur pour assigner la valeur directement, plutôt que faire appel à la fonction:

    def __init__(self, attribut=None):
        self.attribut = attribut or self._calc_attribut()
    
  • du point de vue de la programmation fonctionnelle, la fonction _set_attribut dépends moins du contexte que dans la seconde solution. Elle peut être réutilisée et redéfinie plus facilement par une classe fille par exemple.

Plus pragmatiquement, la meilleure solution des deux est : celle qui est préférée dans la codebase dans laquelle tu travaille.

répondu 24-Nov-2016 par lucas (1,878 points)
edité 1-Dec-2016 par lucas

Tout a fait d'accord, mais il faut préciser un point : dans le premier cas, la méthode ne devrait pas s'appeler _set_attribut mais quelque chose comme _calc_attribut.

Effectivement, j'ai updaté la réponse en conséquence.

Dans une des prochaines versions de python, il sera possible d'utiliser l'évaluation paresseuse.

On pourra donc avoir le beurre et l'argent du beurre :

def __init__(self, attribut=None):
    self.attribut = attribut or lazy self._calc_attribut()
+3 votes

Dans ce genre de cas, je passerai plutôt par une propriété et une lazy initialisation.

class LaClasseAction:
    def __init__(self):
        self._attribut = None

    @property
    def attribut(self):
        if self._attribut is None:
            print("Je fais plein de choses pour créer cet attribut")
            self._attribut = 42
        return self._attribut

Les avantages :

  • Il n'y a pas de traitement lourd dans le constructeur.

  • De plus, si _attribut est construit à partir d'autres attributs, ils seront tous connus après être sorti de init.

  • Ça évite d'avoir une méthode protégée qui complique ton API. On sait directement que l'on doit utiliser la propriété attribut et on sait comment l'utiliser.

  • Pour reprendre l'un des commentaires, tu peux toujours initialiser _attribut dans la classe.

  • Tu peux toujours modifier le comportement de la propriété dans une classe fille.

  • Tu fais ton traitement lourd seulement si tu en as besoin.

répondu 9-Fev par Johjo (220 points)
...