Basé sur ce post sur CodeReview .
J'ai une classe Foo
in Python (3), qui comprend bien sûr une méthode __init__()
. Cette classe déclenche quelques invites et fait son travail . Disons que je veux pouvoir réinitialiser Foo
afin de pouvoir recommencer la procédure.
Quelle serait la mise en œuvre préférée?
Appel à nouveau de la méthode __init__()
def reset(self):
self.__init__()
ou créer une nouvelle instance?
def reset(self):
Foo()
Je ne sais pas si la création d'une nouvelle instance de Foo
laisse quelque chose qui pourrait affecter les performances si reset
est appelé plusieurs fois. D'un autre côté, __init__()
peut avoir des effets secondaires si tous les attributs ne sont pas (re) définis dans __init__()
.
Existe-t-il une façon préférée de procéder?
Les deux sont corrects mais la sémantique n'est pas implémentée de la même manière.
Pour pouvoir réinitialiser une instance, j'écrirais ceci (je préfère appeler une méthode personnalisée à partir de __init__
Que l'inverse, car __init__
Est une méthode spéciale, mais c'est principalement une question de goût):
class Foo:
def __init__(self):
self.reset()
def reset(self):
# set all members to their initial value
Vous l'utilisez de cette façon:
Foo foo # create an instance
...
foo.reset() # reset it
La création d'une nouvelle instance à partir de zéro est en fait plus simple car la classe ne doit implémenter aucune méthode spéciale:
Foo foo # create an instance
...
foo = Foo() # make foo be a brand new Foo
l'ancienne instance sera récupérée si elle n'est utilisée nulle part ailleurs
Les deux sens peuvent être utilisés pour les classes normales où toute l'initialisation est effectuée dans __init__
, Mais la deuxième façon est requise pour les classes spéciales qui sont personnalisé au moment de la création avec __new__
, par exemple des classes immuables.
Mais attention, ce code:
def reset(self):
Foo()
fera pas faire ce que vous voulez: il créera simplement une nouvelle instance, et la supprimera immédiatement car elle sortira du champ d'application à la fin de la méthode. Même self = Foo()
ne définirait que la référence locale qui serait la même hors de portée (et la nouvelle instance détruite) à la fin du méthos.
Vous pouvez conserver l'instance avec laquelle vous travaillez dans un attribut de classe. Chaque fois que vous souhaitez réinitialiser la classe, réaffectez une nouvelle instance à cet attribut. Voici comment je mettrais en œuvre cette approche:
class Foo:
instance = None # The single instance
def __init__(self, ...):
# Initialize the instance if Foo.instance does not exist, else fail
if type(self).instance is None:
# Initialization
type(self).instance = self
else:
raise RuntimeError("Only one instance of 'Foo' can exist at a time")
@classmethod
def reset(cls):
cls.instance = None # First clear Foo.instance so that __init__ does not fail
cls.instance = Foo(...) # Now the initialization can be called
Ensuite, vous pouvez accéder à l'instance en vous référant simplement à Foo.instance
.
J'ai choisi d'avoir le reset
comme méthode de classe, ainsi décorée par @classmethod
. Avec ce décorateur, l'instance peut être réinitialisée en appelant Foo.reset()
, et le paramètre cls
sera automatiquement transmis à la méthode.
Je préfère cette approche (qui est plus ou moins un modèle singleton) à celles que vous proposez, car dans votre situation, il semble logique d'avoir une seule instance de Foo
, puisque vous voulez réinitialiser ça. Par conséquent, je trouve plutôt intuitif de "forcer" l'utilisation d'une seule instance.
D'un autre côté, vous pourriez avoir une instance en dehors de la classe et utiliser une méthode d'instance reset
, définie:
def reset(self):
self.__init__()
Mais cela pourrait ne pas fonctionner si bien. Supposons que vous souhaitiez définir des attributs en dehors de la méthode __init__
. L'appel de __init__
Ne réinitialise pas ces attributs. Par conséquent, votre instance ne sera pas réinitialisée comme prévu. Maintenant, si vous détenez une seule instance et la réaffectez à une toute nouvelle, vous êtes certain qu'elle sera absolument propre.
En ce qui concerne ce que vous appelez "créer une nouvelle instance", c'est plus ou moins ce que j'ai choisi, mais la question est de savoir où le stocker. Je pense qu'il est logique de le garder au chaud dans la classe elle-même.
Soit dit en passant, il ne devrait pas y avoir de problème de performances (comme dans "fuite de mémoire") avec cette solution, car une seule instance Foo
est référencée à la fois, et la création d'une nouvelle annulera la référence le précédent.