2016-01-12 12 views
23

ho scritto un semplice gestore di contesto in Python per la gestione di unit test (e cercare di imparare i gestori di contesto):Python con ... come per il contesto direttore personalizzato

class TestContext(object): 
    test_count=1 
    def __init__(self): 
     self.test_number = TestContext.test_count 
     TestContext.test_count += 1 

    def __enter__(self): 
     pass 

    def __exit__(self, exc_type, exc_value, exc_traceback): 
     if exc_value == None: 
      print 'Test %d passed' %self.test_number 
     else: 
      print 'Test %d failed: %s' %(self.test_number, exc_value) 
     return True 

Se scrivo un test come segue, tutto funziona bene.

test = TestContext() 
with test: 
    print 'running test %d....' %test.test_number 
    raise Exception('this test failed') 

Tuttavia, se si tenta di utilizzare con ... come, non ottengo un riferimento all'oggetto TestContext(). Esecuzione di questo:

with TestContext() as t: 
    print t.test_number 

Solleva l'eccezione 'NoneType' object has no attribute 'test_number'.

Dove sto andando male?

risposta

26

Partendo dal presupposto che è necessario accedere al gestore contesto creato nel la dichiarazione with, __enter__ needs to return self. Se non hai bisogno di accedervi, __enter__ può restituire tutto ciò che desideri.

L'istruzione with associa il valore restituito di questo metodo al/ai target specificato nella clausola as dell'istruzione, se presente.

Questo funzionerà.

class TestContext(object): 
    test_count=1 
    def __init__(self): 
     self.test_number = TestContext.test_count 
     TestContext.test_count += 1 

    def __enter__(self): 
     return self 

    def __exit__(self, exc_type, exc_value, exc_traceback): 
     if exc_value == None: 
      print 'Test %d passed' % self.test_number 
     else: 
      print 'Test %d failed: %s' % (self.test_number, exc_value) 
     return True 
+0

Si noti che non esiste * requisito * per un gestore di contesto restituire 'self'. È un valore predefinito molto utile, in modo che sia possibile creare il gestore di contesto nell'istruzione 'with', ma un gestore di contesto è libero di restituire ciò che gli piace da' __enter__'. Per il caso specifico dell'OP, in cui 'TestContext()' viene creato nell'istruzione 'with', restituire' self 'è davvero la strada da percorrere. –

+0

@MartijnPieters Hai completamente ragione. Ho aggiunto un disclaimer a riguardo. –

7
def __enter__(self): 
    return self 

funzionerà. Il valore restituito da questo metodo verrà assegnato alla variabile as.

Vedi anche the Python doc:

Se un obiettivo è stato incluso nella dichiarazione with, il valore restituito da __enter__() viene assegnato ad esso.

Se avete solo bisogno il numero, si può anche cambiare la logica del gestore contesto di

class TestContext(object): 
    test_count=1 
    def __init__(self): 
     self.test_number = TestContext.test_count 
     TestContext.test_count += 1 

    def __enter__(self): 
     return self.test_number 

    def __exit__(self, exc_type, exc_value, exc_traceback): 
     if exc_value == None: 
      print 'Test %d passed' % self.test_number 
     else: 
      print 'Test %d failed: %s' % (self.test_number, exc_value) 
     return True 

e poi fare

with TestContext() as test_number: 
    print test_number 
4

Secondo PEP 343, il with EXPR as VAR dichiarazione non significa assegnare al VAR il risultato di EXPR, ma piuttosto il risultato di EXPR.__enter__(). Il primo esempio ha funzionato perché hai fatto riferimento alla variabile test stessa.

Problemi correlati