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.
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.
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
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