2011-01-19 21 views
13

Creo una classe i cui oggetti sono inizializzati con un gruppo di codice XML. La classe ha la capacità di estrarre vari parametri da quell'XML e di memorizzarli nella cache all'interno delle variabili di stato dell'oggetto. La quantità potenziale di questi parametri è ampia e molto probabilmente, l'utente non ne avrà più bisogno. Questo è il motivo per cui ho deciso di eseguire un'inizializzazione "pigra".Getter con effetto collaterale

Nel seguente caso di test un tale parametro è title. Quando l'utente tenta di accedere per la prima volta, la funzione getter analizza il XML, correttamente inizializza la variabile di stato e tornare il suo valore:

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   

Questo sembra bello e funziona bene per me. Tuttavia, sono disturbato un po 'dal fatto che la funzione getter è in realtà una "setter", nel senso che ha un effetto collaterale molto significativo sull'oggetto. È una preoccupazione legittima? Se sì, come dovrei affrontarlo?

+3

Indipendentemente da quale sia la risposta effettiva alla domanda, non utilizzare i doppi underscore principali. Iniziano il nome di maciullamento, cioè un sacco di dolore potenziale e guadagno zero. Basta usare un singolo trattino basso. – delnan

+1

Non vedo perché è un problema. –

+1

Proposta di refactoring minore: non inizializzare 'self._title' nel costruttore e sostituire la condizione nel getter con' not hasattr (self, "_title") '. –

risposta

4

Mentre il getter esegue certamente un effetto collaterale, non è tradizionalmente ciò che si potrebbe considerare un cattivo effetto collaterale. Dal momento che il getter restituisce sempre la stessa cosa (salvo eventuali cambiamenti intervenuti nello stato), non ha effetti collaterali visibili all'utente. Questo è un tipico uso delle proprietà, quindi non c'è nulla di cui preoccuparsi.

+0

Mi permetto di dissentire: in questo caso, l'analisi dell'xml può generare eccezioni e nessuno si aspetta che un attributo acceda sollevando qualche eccezione di parsing xml. Ho avuto un caso molto simile in un progetto che ho rilevato e ho riscritto questa parte del codice per far sì che l'analisi si verificasse durante l'instanciazione, quindi non ci sono eccezioni in una fase successiva se l'xml è rotto in qualche modo. Una proprietà get non dovrebbe mai sollevare alcuna eccezione. Ti aspetti che un semplice accesso agli attributi aumenti qualcosa (supponendo che l'attributo esista, naturalmente)? Un attributo calcolato dovrebbe essere sicuro come un semplice. –

14

Questo motivo di progettazione si chiama Lazy initialization e ha un uso legittimo.

+0

Cfr. Il mio commento sulla risposta accettata: l'inizializzazione pigra va bene, ma ciò non significa che un accesso alla proprietà dovrebbe essere autorizzato ad aumentare qualsiasi cosa.Se la tua classe sta usando l'inizializzazione pigra, assicurati che questo non implichi mai alcuna eccezione, o non induca l'utente a pensare che stia facendo un semplice accesso agli attributi sicuri e rendi il tuo getter un metodo esplicito, documentando il fatto che potrebbe sollevare questo o quell'eccezione. –

0

Parecchi anni più tardi ma bene: mentre l'inizializzazione pigra va bene di per sé, non rimanderò definitivamente l'analisi XML ecc. Finché qualcuno non accederà allo title dell'oggetto. Si suppone che gli attributi calcolati si comportino come normali attributi e un semplice accesso all'attributo sarà mai sollevato (supponendo che l'attributo esista, naturalmente).

FWIW Ho avuto un caso molto simile in alcuni progetti che ho rilevato, con errori di parsing xml che si verificano nei punti più inaspettati, a causa del precedente sviluppatore che utilizzava le proprietà nello stesso modo dell'esempio OP e dovevano risolvere inserendo la parte di parsing e validation al momento dell'istanza.

Quindi, utilizzare le proprietà per l'inizializzazione pigra solo se e quando si sa il primo accesso sarà mai rilancio. In realtà, non utilizzare mai una proprietà per qualsiasi cosa che potrebbe aumentare (almeno quando si ottiene - l'impostazione è una situazione diversa). Altrimenti, non utilizzare una proprietà, rendere il getter un metodo esplicito e documentare chiaramente che potrebbe sollevare questo o quello.

NB: l'utilizzo di una proprietà per memorizzare nella cache qualcosa non è un problema qui, di per sé va bene.

Problemi correlati