Pregunta Python: ¿deberían inicializarse todas las variables miembro en __init__?


Tal vez esto sea más una cuestión de estilo que técnica, pero tengo una clase python con varias variables miembro y quiero que funcione para que algunas de las variables miembro se inicialicen cuando el usuario crea una instancia de la clase (es decir, en el __init__ función) y quiero que las otras variables miembro se definan a partir de los argumentos de las funciones miembro que se llamarán más adelante. Entonces mi pregunta es si debo inicializar todas las variables miembro en el __init__ función (y configure los que se definirán más adelante en valores ficticios) o inicialice algunos en el __init__ función y algunos en funciones posteriores. Me doy cuenta de que esto puede ser difícil de entender, así que aquí hay un par de ejemplos.

Este ejemplo tiene var3 establecer a 0 inicialmente en el __init__ función, luego configure el valor deseado más adelante en la función 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

y en este ejemplo, var3 no está definido en absoluto en el __init__ función

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

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

No creo que de ninguna manera suponga una gran diferencia (tal vez una pequeña diferencia en el uso de la memoria). Pero me preguntaba si uno de estos se prefiere sobre el otro por alguna razón.


32
2017-12-18 14:58


origen


Respuestas:


En la programación orientada a objetos, depende del desarrollador asegurarse de que un objeto esté siempre en un estado consistente después de la instanciación y después de que finalice un método. Aparte de eso, eres libre de desarrollar la clase como desees (teniendo en cuenta ciertos principios con subclassing / overriding, etc.).

Una herramienta como Pylint advertirá cuando establezca variables de instancia fuera __init__. Se puede argumentar que establecer todas las variables de instancia en el __init__ es más limpio, pero no es una regla que debe respetarse en todo momento.


22
2017-12-18 15:01



De hecho, desalentaría las variables de inicialización que no siempre necesitas en __init__ a un valor predeterminado arbitrario.

Sí cuestiono su uso de OO si este es el caso, pero estoy seguro de que hay un caso válido y comprensible donde __init__ no hará todo, y la clase querrá modificarse añadiendo atributos adicionales con otros métodos.

La forma correcta, en mi opinión, de probar si se estableció una variable mientras se ejecutaba un método que podría querer usarla sería usar hasattr. En este caso, esta es una forma válida de usar el método y la prueba simplemente cambia el comportamiento de una manera sensata.

Otra forma sería tratar de usarlo y manejar la excepción y proporcionar información amigable sobre lo que el usuario de su clase está haciendo mal. Esto es en el caso de que el método necesite que el atributo se establezca antes de ejecutarse.

es decir, hombre, inicializaste la clase, pero debes asegurarte de que z atributo existe llamando al z_init método antes de ejecutar el z_run método.

Otra, podría decirse que es la manera más pitónica, sería simplemente documentar cómo usar el método en la cadena de documentación y luego dejar que la excepción vuele cuando se usa de forma incorrecta. Esto es lo suficientemente bueno para la primera implementación de algo y luego puede enfocarse en la próxima tarea. Esto está en la misma situación que la anterior, el método necesita que se establezca el atributo.

La razón por la que no me gusta la idea de inicializar variables a los valores predeterminados arbitrarios es que esto puede ser confuso (porque es arbitrario) y es ruido de línea.

Si el valor es no arbitrario y simplemente un valor predeterminado que se puede cambiar, debe utilizar un valor predeterminado en el __init__ método que puede ser anulado. También puede ser un estado inicial válido, que también es no arbitrario y debe configurarlo en el __init__ método.

Entonces la verdadera respuesta es depende, y probablemente debería evitarlo y cuestionar su uso de OO si está haciendo esto agregando atributos en otros métodos o inicializando atributos a valores arbitrarios.

Si bien Simeon Visser dice que debe mantener su objeto en un estado constante, no tiene ninguna base para la coherencia basada en su ejemplo abstracto. Mientras Pylint advierte sobre este tipo de cosas, las advertencias de los programas de pelusa son simplemente para que un revisor de alto nivel pueda ser alertado de cosas que generalmente indicar el olor del código Digo revisor de alto nivel porque un crítico real debe leer y comprender todo su código, y por lo tanto no necesita realmente Pylint.

Un ejemplo que rompe la regla de oro:

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

3
2017-12-18 15:54



Aquí hay un extracto de sololearn.com (un sitio gratuito para aprender Python)

"Las propiedades proporcionan una forma de personalizar el acceso a los atributos de la instancia. Se crean colocando el decorador de propiedades sobre un método, lo que significa que cuando se accede al atributo de instancia con el mismo nombre que el método, se llamará al método en su lugar.

Un uso común de una propiedad es hacer un atributo de solo lectura ".

Ejemplo (también 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

Resultado:

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

Si var3 depende de var1 y var2, podrías hacer

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 también se puede establecer en lo que quieras utilizando una función setter. Tenga en cuenta que puede evitar establecer var3 en un valor arbitrario al usar 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
2018-06-19 06:29