Manera preferida de restablecer una clase en Python

Basado en este post en CodeReview .

Tengo una clase de Foo en Python (3), que por supuesto incluye un __init__() . Esta clase dispara un par de indicaciones y hace su trabajo. Digamos que quiero poder reiniciar Foo para poder comenzar de nuevo el procedimiento.

¿Cuál sería la implementación preferida?

Llamando nuevamente al método __init__()

 def reset(self): self.__init__() 

o creando una nueva instancia?

 def reset(self): Foo() 

No estoy seguro de que crear una nueva instancia de Foo deje atrás algo que pueda afectar el rendimiento si se llama al reset varias veces. Por otro lado, __init__() puede tener efectos secundarios si no todos los atributos están (re) definidos en __init__() .

¿Hay una forma preferida de hacer esto?

Ambos son correctos pero la semántica no se implementa de la misma manera.

Para poder restablecer una instancia, escribiría esto (prefiero llamar a un método personalizado desde __init__ a lo contrario, porque __init__ es un método especial, pero esto es principalmente una cuestión de gusto):

 class Foo: def __init__(self): self.reset() def reset(self): # set all members to their initial value 

Lo usas de esa manera:

 Foo foo # create an instance ... foo.reset() # reset it 

De hecho, crear una nueva instancia desde cero es más sencillo porque la clase no tiene que implementar ningún método especial:

 Foo foo # create an instance ... foo = Foo() # make foo be a brand new Foo 

la vieja instancia será recogida de basura si no se utiliza en ningún otro lugar

Se puede usar ambas formas para las clases normales , donde toda la inicialización se realiza en __init__ , pero la segunda forma es necesaria para las clases especiales que se personalizan en el momento de la creación con __new__ , por ejemplo, clases inmutables.


Pero cuidado, este código:

 def reset(self): Foo() 

no hará lo que desea: solo creará una nueva instancia y la eliminará de inmediato porque quedará fuera del scope al final del método. Incluso self = Foo() solo establecería la referencia local que igual saldría del scope (y la nueva instancia destruida) al final de los methos.

Podría mantener la instancia con la que trabaja en un atributo de clase. Siempre que desee restablecer la clase, vuelva a asignar una nueva instancia a ese atributo. Aquí es cómo implementaría este enfoque:

 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 

Luego, puede acceder a la instancia simplemente refiriéndose a Foo.instance .

Elegí tener el reset como método de clase, por lo tanto decorado por @classmethod . Con este decorador, la instancia se puede restablecer llamando a Foo.reset() , y el parámetro cls se pasará automáticamente al método.

Prefiero este enfoque (que es más o menos un patrón de singleton) sobre los que sugieres, porque en tu situación, parece lógico tener una sola instancia de Foo , ya que quieres restablecerla . Por lo tanto, me parece bastante intuitivo “forzar” el uso de una sola instancia.

Por otro lado, podría tener una instancia fuera de la clase y utilizar un método de instancia de reset , definido:

 def reset(self): self.__init__() 

Pero esto podría no funcionar tan bien. Supongamos que desea establecer atributos fuera del método __init__ . Llamar a __init__ no restablecerá esos atributos. Por lo tanto, su instancia no se restablecerá como se esperaba. Ahora, si tiene una sola instancia y la reasigna a una nueva, está seguro de que estará absolutamente limpio.

Con respecto a lo que usted llama “crear una nueva instancia” , eso es más o menos lo que elegí, pero la pregunta es dónde almacenarlo. Creo que tiene sentido mantenerlo caliente en la clase misma.

Por cierto, no debería haber ningún problema de rendimiento (como en “fuga de memoria”) con esta solución, ya que solo se hace referencia a una instancia de Foo a la vez, y la creación de una nueva hará referencia a la anterior.