In Python, ci sono molte funzioni che funzionano sia come funzioni standard che come gestori di contesto. Per esempio open()
può essere chiamato sia come:Python: funzione standard e gestore contesto?
my_file=open(filename,'w')
o
with open(filename,'w') as my_file:
Entrambi dando un oggetto my_file
che può essere utilizzato per fare tutto ciò che è necessario. In generale, la successiva è preferibile, ma ci sono momenti in cui si potrebbe voler fare anche la prima.
Sono stato in grado di capire come scrivere un gestore di contesto, sia con la creazione di una classe con __enter__
e __exit__
funzioni o utilizzando il @contextlib.contextmanager
decoratore su una funzione e yield
piuttosto che return
. Tuttavia, quando lo faccio non riesco più a usare la funzione direttamente - utilizzando il decoratore, ad esempio, ottengo un oggetto _GeneratorContextManager
anziché il risultato desiderato. Ovviamente, se l'avessi fatto come una classe, avrei appena ottenuto un'istanza della classe del generatore, che presumo sia essenzialmente la stessa cosa.
Quindi, come è possibile progettare una funzione (o classe) che funzioni come una funzione, restituendo un oggetto o un gestore di contesto, restituendo un valore _GeneratorContextManager
o simile?
edit:
Ad esempio, dire ho una funzione simile alla seguente (questo è altamente semplificata):
def my_func(arg_1,arg_2):
result=arg_1+arg_2
return my_class(result)
Quindi la funzione accetta un numero di argomenti, fa cose con loro, e usa il risultato di quella roba per inizializzare una classe, che poi restituisce. Il risultato finale è che ho un'istanza di my_class
, proprio come avrei un oggetto file
se avessi chiamato open
. Se voglio essere in grado di utilizzare questa funzione come un contesto manager, posso modificarlo in questo modo:
@contextlib.contextmanager
def my_func(arg_1,arg_2):
result=arg_1+arg_2 # This is roughly equivalent to the __enter__ function
yield my_class(result)
<do some other stuff here> # This is roughly equivalent to the __exit__function
Il che funziona bene quando si chiama come un contesto manager, ma non ottengo più un'istanza di my_class
quando chiamare come una funzione diritta. Forse sto solo facendo qualcosa di sbagliato?
Edit 2:
Nota che io ho il pieno controllo su my_class
, tra cui la possibilità di aggiungere funzioni ad esso. Dalla risposta accettata di seguito, sono stato in grado di dedurre che la mia difficoltà derivava da un malinteso fondamentale: stavo pensando che qualsiasi cosa avessi chiamato (my_func
nell'esempio precedente) necessitava delle funzioni __exit__
e __enter__
. Questo non è corretto. In effetti, è solo ciò che la funzione restituisce (my_class
nell'esempio precedente) che richiede le funzioni per funzionare come gestore di contesto.
'open' è una funzione che restituisce un'istanza di una classe. Sia che tu usi 'myfile = open (filename)' o 'con open (filename) come myfile', rimarrà comunque un'istanza della stessa classe. Niente è cambiato. – zondo
@zondo Vero, come la funzione che sto cercando di scrivere. Tuttavia, quando avvolgo la funzione nel decoratore '@ contextlib.contextmanager' e la chiamo come una funzione standard, la classe che ottengo NON è la classe I" resa "dalla funzione. Solo quando lo chiamo come gestore di contesto ottengo quella classe. Aggiungerò un semplice esempio – ibrewster
Definisci un metodo '__call__' nella tua classe. –