2013-04-11 3 views
5

Quindi c'è stato un sacco di odio sui singleton in python. Generalmente vedo che avere un singleton è di solito non va bene, ma per quanto riguarda le cose che hanno effetti collaterali, come usare/interrogare un database? Perché dovrei creare una nuova istanza per ogni query semplice, quando potrei riutilizzare una connessione attuale già impostata di nuovo? Quale sarebbe un approccio pitonico/alternativo a questo?Classe DB-Connections come Singleton in Python

Grazie!

risposta

6

In genere, si dispone di un tipo di oggetto che rappresenta la cosa che utilizza un database (ad esempio un'istanza di MyWebServer) e si effettua la connessione al database come membro di tale oggetto.

Se si dispone invece di tutta la logica in una specie di funzione, rendere la connessione locale a tale funzione. (Questo non è troppo comune in molti altri linguaggi, ma in Python ci sono spesso buoni modi per concludere lavori di stato a più stadi in una singola funzione di generatore.)

Se si dispone di tutta la documentazione di database sparsi tutti sopra il posto, quindi basta usare una variabile globale invece di un singleton. Sì, i globali sono cattivi, ma i singleton sono altrettanto cattivi e più complicati. Ci sono alcuni casi in cui sono utili, ma molto rari. (Questo non è necessariamente vero per altre lingue, ma è per Python.) E il modo per sbarazzarsi del globale è ripensare la progettazione. C'è una buona probabilità che stai in modo efficace utilizzando un modulo come un (singolo) oggetto, e se si pensa che attraverso, probabilmente si può trovare una buona classe o funzione per avvolgere in su in.


Ovviamente semplicemente spostando tutti i tuoi globali in attributi di classe e @classmethod ti sta dando solo globali con uno spazio dei nomi differente. Ma trasferirli negli attributi di istanza è una storia diversa. Questo ti dà un oggetto che puoi passare e, se necessario, un oggetto puoi avere 2 (o forse anche 0 in alcune circostanze), collegare un blocco, serializzare, ecc.

In molti tipi di applicazioni , stai per finire con una singola istanza di qualcosa - ogni app Qt della GUI ha esattamente uno MyQApplication, quasi ogni server Web ha esattamente uno MyWebServer, ecc. Non importa come lo chiami, è effettivamente un singleton o globale. E se vuoi, puoi semplicemente spostare tutto in attributi di quell'oggetto divino.

Ma solo perché è possibile fare ciò non significa che si . Hai ancora parametri di funzione, variabili locali, globali in ogni modulo, altre classi (non megalitiche) con i loro attributi di istanza, ecc., E dovresti usare qualunque cosa sia appropriata per ogni valore.

Ad esempio, supponiamo che il tuo MyWebServer crei una nuova istanza ClientConnection per ogni nuovo client che si connette a te. Potresti fare in modo che le connessioni scrivano MyWebServer.instance.db.execute ogni volta che vogliono eseguire una query SQL ... ma potresti anche solo passare il self.db al costruttore ClientConnection, e ogni connessione poi fa solo self.db.execute. Quindi qual è il migliore? Bene, se lo fai in questo modo, rende il tuo codice molto più facile da estendere e refactoring. Se si desidera eseguire il bilanciamento del carico su 4 database, è sufficiente modificare il codice in un'unica posizione (dove inizializza ogni ClientConnection) anziché 100 (ogni volta che ClientConnection accede al database). Se si desidera convertire la propria app Web monolitica in un contenitore WSGI, non è necessario modificare alcun codice ClientConnection eccetto forse il costruttore. E così via.

+0

tipo di app = class.webapp(); app.dbconn = class.dbconn()? Alla fine è davvero così diverso da un singleton? – AlessandroEmm

+0

@AlessandroMeyer: fammi modificare la risposta per rispondere. – abarnert

1

Se si utilizza un approccio orientato agli oggetti, il suggerimento di abamet di collegare i parametri di connessione del database come attributi di classe ha senso per me. La classe può quindi stabilire una singola connessione al database che tutti i metodi della classe si riferiscono a self.db_connection, ad esempio.

Se non si utilizza un approccio orientato agli oggetti, un modulo di connessione al database separato può fornire un equivalente in stile funzionale. Dedicare un modulo per stabilire una connessione al database e semplicemente importare quel modulo ovunque tu voglia utilizzarlo. Il tuo codice può quindi fare riferimento alla connessione come db.connection, ad esempio. Dato che i moduli sono effettivamente singleton e il codice del modulo viene eseguito solo sull'importazione prima, si utilizzerà di nuovo la stessa connessione di database ogni volta.

+0

@JeffryFroman Il mio problema con l'utilizzo di un modulo è che mi piace l'idea di avere una sorta di stato interno globale, ma ancora single-class. – AlessandroEmm

+0

Come faresti ad es. risolvere la "memorizzazione" dei parametri di connessione DB? Il solo fatto di averli negli stessi moduli sarebbe un po 'disordinato e averli in un modulo di impostazione generale renderebbe il modulo Database strettamente dipendente da quello. – AlessandroEmm