2011-11-21 3 views
5

[Mi scuso per il titolo inetto; Non potrei inventarmi niente di meglio. I suggerimenti per un titolo migliore sono i benvenuti.]Come dare una funzionalità simile alla dichiarazione con la classe?

Desidero implementare un'interfaccia per i file HDF5 che supporta la concorrenza a livello multiprocesso tramite il blocco dei file. L'ambiente previsto per questo modulo è un cluster Linux che accede a un disco condiviso su NFS. L'obiettivo è abilitare l'accesso simultaneo (su NFS) allo stesso file da più processi paralleli in esecuzione su diversi host differenti.

Mi piacerebbe essere in grado di implementare la funzionalità di blocco attraverso una classe wrapper per la classe h5py.File. (h5py offre già il supporto per filo concorrenza -livello, ma la biblioteca HDF5 sottostante non è thread-safe.)

Sarebbe bello se potessi fare qualcosa nello spirito di questa:

class LockedH5File(object): 
    def __init__(self, path, ...): 
     ... 
     with h5py.File(path, 'r+') as h5handle: 
      fcntl.flock(fcntl.LOCK_EX) 
      yield h5handle 
     # (method returns) 

Mi rendo conto che il codice sopra riportato è sbagliato, ma spero che trasmetta l'idea principale: vale a dire che l'espressione LockedH5File('/path/to/file') invii un handle aperto al codice client, che può quindi eseguire varie operazioni di lettura/scrittura arbitrarie su di esso. Quando questo handle esce dall'ambito, il suo distruttore chiude la maniglia, rilasciando in tal modo il blocco.

L'obiettivo che motiva questo accordo è duplice:

  1. disaccoppiare la creazione del manico (dal codice della libreria) dalle operazioni che vengono successivamente richiesti sul manico (da parte del cliente codice), e

  2. mantenere la maniglia è chiusa e la serratura rilasciato, non importa cosa succede durante l' esecuzione del codice intermedio (es eccezioni, gestite segnali, Python errori interni).

Come posso ottenere questo effetto in Python?

Grazie!

risposta

6

Per rendere questo lavoro, la classe deve implementare il context manager protocol. In alternativa, scrivere una funzione generatore utilizzando il decoratore contextlib.contextmanager.

La classe potrebbe grosso modo simile a questa (i dettagli di h5py utilizzo sono probabilmente terribilmente sbagliato):

class LockedH5File(object): 
    def __init__(self, path, ...): 
     self.h5file = h5py.File(path, 'r+') 
    def __enter__(self): 
     fcntl.flock(fcntl.LOCK_EX) 
     return self.h5file 
    def __exit__(self, exc_type, exc_val, exc_tb): 
     self.h5file.close() 
2

Beh, un gestore di contesto e with dichiarazione. In generale, i distruttori in Python non sono garantiti per eseguire su tutti, quindi non si deve fare affidamento su di essi come qualcosa di diverso da una pulizia sicura. Fornire __enter__ e __exit__ metodi, e usarlo come

with LockedFile(...) as fp: 
    # ... 
8

oggetti che possono essere utilizzati in with dichiarazioni sono chiamati gestori di contesto; e implementano una semplice interfaccia.Devono fornire due metodi, un metodo __enter__, che non accetta argomenti e può restituire qualsiasi cosa (che verrà assegnato alla variabile nella porzione as) e un metodo __exit__, che accetta tre argomenti (che verranno completati con il risultato di sys.exc_info()) e restituisce un valore diverso da zero per indicare che è stata gestita un'eccezione. Il tuo esempio sarà probabilmente il seguente:

class LockedH5File(object): 
    def __init__(self, path, ...): 
     self.path = path 

    def __enter__(self): 
     self.h5handle = h5handle = h5py.File(self.path, 'r+') 
     fcntl.flock(fcntl.LOCK_EX) 
     return h5handle 

    def __exit__(self, exc_type, exc_info, exc_tb): 
     self.h5handle.close()