2012-01-03 11 views
7

In this question, ho definito un gestore di contesto che contiene un gestore di contesto. Qual è il modo più semplice per realizzare questo annidamento? Ho finito per chiamare self.temporary_file.__enter__() in self.__enter__(). Tuttavia, in self.__exit__, sono abbastanza sicuro di dover chiamare self.temporary_file.__exit__(type_, value, traceback) in un blocco finally nel caso in cui venga sollevata un'eccezione. Devo impostare i parametri type_, value e traceback se qualcosa va storto in self.__exit__? Ho controllato contextlib, ma non ho trovato alcuna utilità per aiutare con questo.Gestori di contesto di Nesting Python

Codice originale da domanda:

import itertools as it 
import tempfile 

class WriteOnChangeFile: 
    def __init__(self, filename): 
     self.filename = filename 

    def __enter__(self): 
     self.temporary_file = tempfile.TemporaryFile('r+') 
     self.f = self.temporary_file.__enter__() 
     return self.f 

    def __exit__(self, type_, value, traceback): 
     try: 
      try: 
       with open(self.filename, 'r') as real_f: 
        self.f.seek(0) 
        overwrite = any(
         l != real_l 
         for l, real_l in it.zip_longest(self.f, real_f)) 
      except IOError: 
       overwrite = True 
      if overwrite: 
       with open(self.filename, 'w') as real_f: 
        self.f.seek(0) 
        for l in self.f: 
         real_f.write(l) 
     finally: 
      self.temporary_file.__exit__(type_, value, traceback) 

risposta

9

Il modo più semplice per creare i gestori di contesto è con contextlib.contextmanager. Qualcosa del genere:

@contextlib.contextmanager 
def write_on_change_file(filename): 
    with tempfile.TemporaryFile('r+') as temporary_file: 
     yield temporary_file 
     try: 
      ... some saving logic that you had in __exit__ ... 

Quindi utilizzare with write_on_change_file(...) as f:.
Il corpo dell'istruzione with verrà eseguito "invece di" yield. Avvolgi lo yield stesso in un blocco try se desideri rilevare eventuali eccezioni che si verificano nel corpo.

Il file temporaneo verrà sempre chiuso correttamente (quando termina il blocco with).

+0

Questo è veramente bello. Ho intenzione di lasciare aperta la domanda per un po 'nel caso in cui questa domanda produca altre buone risposte. –

+3

L'uso di '@ contextlib.contextmanager' è comodo, ma ci sono ancora casi in cui è appropriato usare una classe con i metodi' __enter__' e '__exit__' definiti manualmente. Hai qualche consiglio per farlo? – Zearin

+0

Bene, fallo quando è più conveniente, ad esempio quando l'oggetto deve fare qualcosa di più che essere semplicemente un gestore di contesto (anche se in tal caso dovresti anche considerare di aggiungere un metodo @ contextlib.contextmanager). –

Problemi correlati