2012-08-22 14 views
6

Ho alcune classi che sono molto diffuse nella mia applicazione Python e che dovrebbero avere solo un'istanza globale (es. Logger, DbConnection). Python non supporta variabili/metodi statici in una classe, quindi il solito modo Java/C++ per creare un singleton qui non funziona. Ho cercato delle alternative per implementare singleton in Python. Voglio un semplice (non metaprogramming, se possibile) e l'implementazione pulita. Questo sembra buono:Globali e singleton in Python

class MyClass(object): 
    def a(): 
     pass 

singleton = MyClass() 

Utilizzando il singoletto sarebbe semplice come

import myClass 
myClass.singleton.a() 

L'assegnazione diretta potrebbe essere sostituito da una funzione di creazione se inizializzazione dell'oggetto non è così semplice.

Potrei anche creare un getInstance() nello scope del modulo e usarlo sempre per ottenere myObj.

Domanda 1) Funziona bene? Il codice del modulo (assegnazione myObj) viene eseguito solo la prima volta che viene importato in qualche altro modulo e myObj non verrà creato ogni volta che importerò questo modulo da qualche parte?

Un metodo alternativo che ho visto è l'utilizzo di un modulo globale. Qualcosa di simile:

from myClass1 import MyClass1 
from myClass2 import MyClass2 

myObj1 = MyClass1() 
myObj2 = MyClass2() 

Usando questo:

import globals 
globals.myObj1.a() 

tendo a preferire la prima alternativa.

Domanda 2) Tra le 2 soluzioni, cosa consiglia?

Domanda 3) Una terza soluzione sarebbe passare gli oggetti diffusi come Logger a diverse classi/funzioni, ma questa non è una buona soluzione imho. C'è una soluzione migliore non menzionata qui?

Sono consapevole degli svantaggi dell'utilizzo di variabili globali e singleton. Tuttavia, avere uno stato globale non è un grosso problema nella mia applicazione. Preferirò soluzioni che abbiano chiarezza del codice e siano semplici da usare.

+0

la premisità della tua domanda è sbagliata. per prima cosa, python supporta metodi e variabili statici. secondo, perché non stai usando le librerie standard esistenti per la registrazione e l'accesso al database? –

+0

Python supporta direttamente metodi/variabili statiche (no annotation hack)? La seconda domanda si discosta dal problema (implementazione singleton), sostituisce le classi di accesso/accesso DB di qualsiasi classe che sia singleton ed è ampiamente utilizzata in un'applicazione. –

risposta

6

Se si desidera avere una classe logger con una sola istanza, è sufficiente creare un modulo separato.

# In logging.py 
def log(msg): 
    print msg 

Quindi da qualsiasi script si desidera accedere.

from logging import log 
log("A critical error occured.") 
+1

Ho attributi nella mia classe Logger (come il nome file di registro). Se trasformo i miei metodi di classe in funzioni di modulo, gli attributi di classe precedenti diventerebbero variabili di modulo. Non è un problema? (per esempio, l'utente usa "dalla registrazione importazione *" (errato lo so, ma comunque), le mie variabili potrebbero ingombrare lo spazio variabile dell'applicazione –

+0

@ user1550682: Un modulo è in realtà solo un singleton in Python. c'è solo una sua istanza e puoi passarla come qualsiasi altro valore.Quindi, nessun problema –

+5

@ user1550682 - anteponi le tue variabili private con un singolo carattere di sottolineatura e non verranno importate con l'importazione * ' dichiarazione. es. '_filename =" bla.bla "' – Aesthete

-1

La risposta corretta a come fare singleton? Non farlo. Dovresti passare esplicitamente un riferimento a tutto ciò che deve usarlo. È possibile ridurre lo sforzo con funzioni di fabbrica, classi wrapper e simili.

Anche se c'è qualcosa, come lo schermo o il logger di natura globale, dovresti comunque passarlo esplicitamente per consentire il test dell'unità. Si noti che questo si applica solo alle cose destinate a far parte del progetto finale. Se il tuo logger è solo un rapido trucco per il debug, sentiti libero di renderlo globale.

+0

Come accennato nel mio post originale, sono a conoscenza degli aspetti negativi dell'uso dei singleton. Il loro uso è controverso da quando ne ho sentito parlare per la prima volta fino ad oggi. Passare una variabile del logger a tutte le classi e funzioni (se sono al di fuori di una classe) in un'applicazione mi sembra utopica, non ho mai visto un'applicazione reale che lo faccia. –

+0

L'ho fatto alcune volte, anche se devo ammettere che si tratta di script relativamente semplici. E ho intenzione di farlo nel mio attuale progetto una volta completato. Ma hai ragione che è un po 'utopico. – Antimony

+1

Alan: molte applicazioni utilizzano un'istanza del logger globale o la passano in un modulo di impostazione importato in altre parti dell'applicazione. Inoltre, molte applicazioni dipendono anche da un modulo app o settings, che memorizza variabili globali, istanze singleton o thread che devono essere utilizzati nell'intera applicazione. Django e Celery sono entrambi esempi di applicazioni che fanno questo. Utilizzando un modello di progettazione proxy e stato, semplifica la necessità di fare affidamento sui singleton, anche se, in definitiva, è ciò che sta accadendo, solo in una forma diversa. –

0

consideri creando la seguente:

  1. Uno stato classe classe
  2. Un proxy

La classe state creerà un filo che viene inizializzato in init. modulo py. La classe di stato avrà metodi che passano istanze di classe nel thread e ne salvano un weakref.

Successivamente, è possibile utilizzare una classe proxy per accedere alle istanze di classe nel thread. Se gli oggetti di classe non vengono creati, il proxy creerà un'istanza della classe desiderata e la salverà nel thread.

Utilizzando questo modello, è possibile limitare il numero di istanze di ogni classe a 1 e assicurarsi che ogni volta che una parte dell'applicazione è in esecuzione, qualsiasi istanza che deve essere utilizzata globalmente, ripetutamente, non debba essere ricreata .