2011-01-19 12 views
13

Creo una clase cuyos objetos se inicializan con un montón de código XML. La clase tiene la capacidad de extraer varios parámetros de ese XML y almacenarlos en caché dentro de las variables de estado del objeto. La cantidad potencial de estos parámetros es grande y, probablemente, el usuario no necesitará la mayoría de ellos. Es por eso que he decidido realizar una inicialización "floja".Getter con efecto secundario

En el siguiente caso de prueba, tal parámetro es title. Cuando el usuario intenta acceder a él por primera vez, la función getter analiza el XML, adecuadamente inicializa la variable de estado y devolver su valor:

class MyClass(object):  
    def __init__(self, xml=None): 
     self.xml = xml 
     self.title = None 

    def get_title(self): 
     if self.__title is None: 
      self.__title = self.__title_from_xml() 
     return self.__title 

    def set_title(self, value): 
     self.__title = value 

    title = property(get_title, set_title, None, "Citation title") 

    def __title_from_xml(self): 
     #parse the XML and return the title 
     return title   

Esto se ve bien y funciona bien para mí. Sin embargo, me molesta un poco el hecho de que la función getter es en realidad una "setter" en el sentido de que tiene un efecto secundario muy significativo en el objeto. ¿Es esto una preocupación legítima? Si es así, ¿cómo debería abordarlo?

+3

Independientemente de cuál sea la respuesta real a la pregunta, no debe usar guiones bajos iniciales principales. Empiezan a crear nombres, es decir, muchos dolores potenciales y ganancia cero. Solo usa un solo guion bajo. – delnan

+1

No veo por qué es un problema. –

+1

Propuesta de refactorización secundaria: no inicialice 'self._title' en el constructor, y reemplace la condición en el getter por' not hasattr (self, "_title") '. –

Respuesta

4

Si bien el captador ciertamente tiene un efecto secundario, tradicionalmente no es lo que uno consideraría un efecto secundario negativo. Como el captador siempre devuelve lo mismo (salvo que haya cambios intermedios en el estado), no tiene efectos secundarios visibles para el usuario. Este es un uso típico de las propiedades, por lo que no hay nada de qué preocuparse.

+0

No estoy de acuerdo: en este caso, analizar el xml puede generar excepciones, y nadie espera que el acceso a un atributo genere alguna excepción de análisis XML. Tuve un caso muy similar en un proyecto que tomé y reescribí esta parte del código para que el análisis ocurra en la instanciación, por lo que no hay excepciones que ocurran en una etapa posterior si el xml se rompe de alguna manera. Una propiedad de get NO debe generar ninguna excepción. ¿Esperas que un atributo de atributo plano eleve algo (suponiendo que el atributo exista, por supuesto)? Un atributo calculado debe ser tan seguro como uno simple. –

14

Este patrón de diseño se llama Lazy initialization y tiene un uso legítimo.

+0

cf mi comentario sobre la respuesta aceptada - la inicialización lenta está bien, pero eso no significa que se deba permitir el acceso a una propiedad para generar algo.Si su clase utiliza una inicialización lenta, asegúrese de que esto nunca implique ninguna excepción, o no induzca al usuario a pensar que está haciendo un acceso seguro de atributos y haga que su getter sea un método explícito, documentando el hecho de que puede plantear esto o esa excepción –

0

Algunos años después pero bien: aunque la inicialización lenta es buena en sí misma, definitivamente no pospongo el análisis xml hasta que alguien acceda al objeto title. Se supone que los atributos calculados se comportan como atributos simples, y un acceso de atributo simple nunca raise (suponiendo que el atributo existe, por supuesto).

FWIW Tuve un caso muy similar en algún proyecto que asumí, con errores de análisis XML ocurriendo en los lugares más inesperados, debido a que el desarrollador anterior usó las propiedades de la misma manera que en el ejemplo OP, y tuvo que arreglarlo al poner la parte de análisis y validación en el momento de instanciación.

lo tanto, utilizar las propiedades de inicialización perezosa sólo si y cuando se sabe el primer acceso se nunca aumento. En realidad, nunca use una propiedad para cualquier cosa que pueda aumentar (al menos cuando se obtiene - la configuración es una situación diferente). De lo contrario, no use una propiedad, haga del getter un método explícito y documente claramente que podría plantear esto o aquello.

NB: usar una propiedad para almacenar en caché algo no es el problema aquí, esto solo está bien.

Cuestiones relacionadas