web-dev-qa-db-fra.com

Python - toutes les variables membres doivent-elles être initialisées dans __init__

C’est peut-être plus une question de style que de question technique, mais j’ai une classe python avec plusieurs variables membres et je veux qu’elle fonctionne de sorte que certaines des variables membres soient initialisées lorsque l’utilisateur crée pour la première fois une instance de la classe dans la fonction __init__) et je veux que les autres variables membres soient définies à partir des arguments des fonctions membres qui seront appelées ultérieurement. Ma question est donc la suivante: devrais-je initialiser toutes les variables membres dans la fonction __init__ (et définir celles qui seront définies ultérieurement sur des valeurs nominales) ou en initialiser certaines dans la fonction __init__ et d'autres dans des fonctions ultérieures? Je réalise que cela peut être difficile à comprendre alors voici quelques exemples.

Dans cet exemple, var3 est initialement défini sur 0 dans la fonction __init__, puis sur la valeur souhaitée ultérieurement dans la fonction my_funct.

class myClass(object):
   def __init__(self,var1,var2):
        self.var1=var1
        self.var2=var2
        self.var3=0

  def my_funct(self,var3):
       self.var3=var3

et dans cet exemple, var3 n'est pas défini du tout dans la fonction __init__ 

class myClass(object):
   def __init__(self,var1,var2):
        self.var1=var1
        self.var2=var2

  def my_funct(self,var3):
       self.var3=var3

Je ne pense pas que l'une ou l'autre des solutions ferait une grande différence (peut-être une légère différence dans l'utilisation de la mémoire) Mais je me demandais si l’un d’eux était préféré à l’autre pour une raison quelconque.

34
user1893354

En programmation orientée objet, il appartient au développeur de s'assurer qu'un objet est toujours dans un état cohérent après l'instanciation et après la fin d'une méthode. En dehors de cela, vous êtes libre de développer le cours à votre guise (en gardant à l'esprit certains principes tels que le sous-classement/la substitution, etc.).

Un outil tel que Pylint vous avertira lorsque vous définissez des variables d'instance en dehors de __init__. On peut faire valoir que la définition de toutes les variables d'instance dans le __init__ est plus propre, mais ce n'est pas une règle à respecter en permanence.

23
Simeon Visser

En fait, je déconseillerais l'initialisation de variables dont vous n'avez pas toujours besoin dans __init__ avec une valeur par défaut arbitraire.

Je m'interroge sur votre utilisation de OO si tel est le cas, mais je suis sûr qu'il existe un cas valable et compréhensible dans lequel __init__ ne fera pas tout, et la classe voudra se modifier elle-même en ajoutant des attributs supplémentaires avec Autres méthodes.

À mon avis, la méthode appropriée pour vérifier si une variable a été définie lors de l’exécution d’une méthode susceptible de l’utiliser consiste à utiliser hasattr. C’est dans ce cas qu’il s’agit d’un moyen valide d’utiliser la méthode et que le test ne fait que modifier le comportement de manière judicieuse.

Une autre solution consisterait à essayer de l'utiliser, de gérer l'exception et de fournir des informations conviviales sur ce que l'utilisateur de votre classe fait de mal. C'est dans le cas où la méthode nécessite que l'attribut soit défini avant l'exécution.

c’est-à-dire Hey, vous avez initialisé la classe, mais vous devez vous assurer que l’attribut z existe en appelant la méthode z_init avant d’exécuter la méthode z_run.

Une autre façon, sans doute la méthode la plus pythonique, serait simplement de décrire comment utiliser la méthode dans la docstring, puis de laisser l’exception voler quand elle sera utilisée de manière incorrecte. C'est suffisant pour la première mise en œuvre de quelque chose et vous pouvez ensuite vous concentrer sur la tâche suivante. Ceci est dans la même situation que ci-dessus, la méthode nécessite que l'attribut soit défini.

La raison pour laquelle je n'aime pas l'idée d'initialiser des variables à des valeurs par défaut arbitraires est que cela peut prêter à confusion (parce que c'est arbitraire) et constitue du bruit de ligne.

Si la valeur est pas arbitraire et qu'il s'agit simplement d'une valeur par défaut pouvant être modifiée, vous devez utiliser une valeur par défaut dans la méthode __init__ pouvant être remplacée. En fait, il peut également s'agir d'un état initial valide, qui est également non arbitraire, et vous devez le définir dans la méthode __init__.

Donc, la vraie réponse est cela dépend de, et vous devriez probablement l'éviter et remettre en question votre utilisation de OO si vous le faites en ajoutant des attributs dans d'autres méthodes ou en initialisant des attributs à des valeurs arbitraires.

Alors que Simeon Visser dit de garder votre objet dans un état cohérent, il n’a aucune base sur laquelle la cohérence est basée sur votre exemple abstrait. Tandis que Pylint met en garde sur ce genre de chose, les avertissements des programmes de charpie sont simplement un moyen pour un relecteur de haut niveau d’être averti de choses qui habituellement indiquent une odeur de code. Je dis critique de haut niveau, car un vrai critique doit lire et comprendre tout votre code, et n'a donc pas vraiment besoin de Pylint.

Un exemple qui enfreint la règle de base:

class Mutant(object):
    """A mutant!"""

    def __init__(self):
        """A mutant is born with only 1 eye and 1 mouth"""

        self.eyes = 1
        self.mouth = 1
        self.location = 'Montana'

    def roll_to(self, location):
        """If they have limbs, running is less dangerous"""

        if hasattr(self, 'limbs'):
             print 'Your mutant broke its limbs off!!'
             del self.limbs

        self.location = location

    def run_to(self, location):
        """If they don't have limbs, running is not effective"""

        if not hasattr(self, 'limbs'):
             print 'Your mutant tries to run but he has no limbs.'
        else:
             self.location = location

    def grow_limbs(self, number_of_limbs):
         """Ah, evolution!"""

         assert number_of_limbs > 0, 'Cannot grow 0 or less limbs...'

         if hasattr(self, 'limbs'):
             self.limbs += number_of_limbs
         else:
             self.limbs = number_of_limbs
4
Derek Litz

Voici un extrait de sololearn.com (un site gratuit pour apprendre le python)

"Les propriétés permettent de personnaliser l'accès aux attributs d'instance. Elles sont créées en plaçant le décorateur de propriété au-dessus d'une méthode. Cela signifie que lorsque l'attribut d'instance portant le même nom que la méthode est utilisé, la méthode est appelée à la place.

Une utilisation courante d'une propriété est de rendre un attribut en lecture seule. "

Exemple (également de sololearn.com):

class Pizza:
    def __init__(self, toppings):
    self.toppings = toppings

    @property
    def pineapple_allowed(self):
       return False

   pizza = Pizza(["cheese", "tomato"])
   print(pizza.pineapple_allowed)
   pizza.pineapple_allowed = True

Résultat:

  >>>
 False
 AttributeError: can't set attribute
 >>>

Si var3 dépend de var1 et var2, vous pouvez le faire. 

class myClass:
    def __init__(self,var1,var2):
        self.var1=var1
        self.var2=var2
    @property
    def var3(self):
        return(self.var1+self.var2)  #var3 depends on var1 and var2
 m1=myClass(1,2)
 print(m1.var3)   # var3 is 3

var3 peut également être réglé sur ce que vous voulez en utilisant une fonction de définition. Notez que vous pouvez éviter de définir var3 sur une valeur arbitraire en utilisant None. 

class myClass2(object):
    def __init__(self,var1,var2):
        self.var1=var1
        self.var2=var2
        self._var3=None     # None or an initial value that makes sense
        @property
        def var3(self):
            return(self._var3)
        @var3.setter
        def var3(self,value):
            self._var3=value
   m2=myClass(1,2)
   print(m2.var3)        # var3 is none
   print(m2.var3(10))    # var3 is set to 10 
0
Cody Aldaz