Uso di singleton in Python (Django)

2

Mi è stato suggerito di definire un singleton in Python nel modo seguente:

class Controller:
    pass
    # ...

controller = Controller()

Tuttavia, in questo modo, controller non può essere sostituito con una classe derivata.

Quali sono altri modi sensati per definire i singleton in Python Django?

Forse, quando definisco un'app riusabile, non dovrei definire l'istanza singleton direttamente nell'app, ma definirla nel programma che la usa? Quali sono i vantaggi e gli svantaggi di questo?

    
posta porton 15.10.2016 - 18:10
fonte

1 risposta

3

Using Singletons in Python

Che cos'è un singleton?

Un singleton è un oggetto di cui c'è solo uno, semanticamente, in memoria.

Singoletti incorporati

None è un singleton frequentemente usato. NotImplemented è un altro, molto meno usato. Possiamo testare l'identità di un singolo:

>>> maybe_None = None

E poi

maybe_None is None

restituisce True .

Creazione di un singleton semantico

Puoi semplicemente usare un'istanza di oggetto per crearne una che non ha bisogno di funzionalità nello spazio dei nomi globale di un modulo (tutto maiuscolo, perché è una costante globale):

SINGLETON = object() 

Fai attenzione a non sovrascrivere l'oggetto nello spazio dei nomi globale. Se gli dai un buon nome, dovrebbe essere ovvio quando qualcuno sta facendo qualcosa di sbagliato con esso.

Questo è simile all'esempio che hai dato. Tuttavia, non farei ciò che stai facendo se la creazione dell'oggetto è costosa - rende così costoso importare il modulo, anche se non vuoi usare l'oggetto.

Perché non desideri un singleton globale all'importazione

Dove non vuoi che un singleton creato durante l'importazione sia in qualsiasi momento desideri importare il modulo in modo economico.

Vuoi che i tuoi moduli siano poco costosi da importare quando si genera dinamicamente la documentazione, per esempio.

Un altro caso in cui dovresti preferire le importazioni poco costose sono quelle per l'esecuzione di un non test.

Inoltre, il metodo che usi non supporta l'ereditarietà.

Quindi ti mostrerò alcuni modi per ovviare a questo problema.

Un'istanza singleton

Dove vuoi avere una sola istanza di una classe, puoi farlo in vari modi:

personalizza __new__ e utilizza un attributo di classe, instance

class Controller(object):
    instance = None
    def __new__(cls):
        if cls.instance is not None:
            return cls.instance
        else:
            inst = cls.instance = super(Controller, cls).__new__()
            return inst

E nota che questo supporta l'ereditarietà.

memoize a factory function

È possibile creare un attributo di funzione per memoizzare la funzione

def get_controller():
    if hasattr(get_controller, 'instance'):
        return get_controller.instance
    else:
        inst = get_controller.instance = Controller()
        return inst

Potresti anche usare il modulo o lo spazio dei nomi della classe per mantenere l'istanza in - poiché anche la funzione e il modulo dovrebbero essere entrambi singleton.

Oppure usa lru_cache (dato che get_controller non accetta argomenti) per memorizzarlo.

from functools import lru_cache

@lru_cache
def get_controller():
    return Controller()

Si noti che se si aggiungono argomenti a get_controller , lru_cache restituirà un singleton per ogni combinazione univoca di argomenti, fino alla sua dimensione cache (teoricamente), dopo di che dimentica le istanze, quindi fai attenzione con questo se aggiungi argomenti - probabilmente meglio per implementare il tuo comportamento, come ho fatto con l'attributo function.

Questo può supportare l'ereditarietà, per esempio.

@lru_cache
def get_controller(controller_type): 
    if issubclass(controller_type, Controller):
        return controller_type()
    else:
        raise TypeError('must supply a Controller type')
    
risposta data 15.10.2016 - 19:02
fonte

Leggi altre domande sui tag