Python - asserisce vs if & return


12

Sto scrivendo una sceneggiatura che fa qualcosa in un file di testo (ciò che fa è irrilevante per la mia domanda). Quindi, prima di fare qualcosa per il file, voglio verificare se il file esiste. Posso farlo, nessun problema, ma il problema è più quello dell'estetica.

Ecco il mio codice, implementando la stessa cosa in due modi diversi.

def modify_file(filename):
    assert os.path.isfile(filename), 'file does NOT exist.'


Traceback (most recent call last):
  File "clean_files.py", line 15, in <module>
    print(clean_file('tes3t.txt'))
  File "clean_files.py", line 8, in clean_file
    assert os.path.isfile(filename), 'file does NOT exist.'
AssertionError: file does NOT exist.

o:

def modify_file(filename):
    if not os.path.isfile(filename):
        return 'file does NOT exist.'


file does NOT exist.

Il primo metodo produce un output per lo più banale, l'unica cosa che mi interessa è che il file non esista.

Il secondo metodo restituisce una stringa, è semplice.

La mia domanda è: quale metodo è migliore per far sapere all'utente che il file non esiste? L'uso del assertmetodo sembra in qualche modo più pitonico.

Risposte:


33

Andresti invece con una terza opzione: usare raisee un'eccezione specifica. Questa può essere una delle eccezioni integrate oppure è possibile creare un'eccezione personalizzata per il lavoro.

In questo caso, userei IOError, ma ValueErrorpotrebbe anche adattarsi:

def modify_file(filename):
    if not os.path.isfile(filename):
        raise IOError('file does NOT exist.')

L'uso di un'eccezione specifica consente di generare altre eccezioni per diverse circostanze eccezionali e consente al chiamante di gestire l'eccezione con garbo.

Ovviamente, molte operazioni sui file (come open()) stesseOSError già aumentano ; esplicitamente prima testare se il file esiste può essere ridondante qui.

Non usare assert; se esegui python con il -Oflag, tutte le asserzioni vengono rimosse dal codice.


punto interessante sulla ridondanza! Consiglieresti di evitarlo? cioè "fallirà comunque più tardi"
Ciprian Tomoiagă

1
@ CiprianTomoiagă: perché raddoppiare i test? Se la riga successiva modify_file()è with open(filename) as f:, allora IOErrorverrebbe anche sollevata. E versioni più recenti di Python hanno fornito maggiori dettagli in sottoclassi di IOError(in FileNotFoundErrorparticolare mi viene in mente) che potrebbero essere utili a uno sviluppatore che utilizza questa API. Se il codice esegue i propri controlli e aumenta, i IOErrordettagli utili andrebbero persi.
Martijn Pieters,

@MartijnPieters sarebbe una risposta accettabile a "perché raddoppiare i test?" essere quando il primo controllo è più veloce dell'eccezione sollevata quando open () fallisce? ad esempio, quando si verifica l'esistenza di un file è più veloce che tentare di aprire e alla fine non riuscire a farlo.
Marcel Wilson,

1
@MarcelWilson: No, perché saresti micro-ottimizzato rispetto a un metodo che esegue l'I / O. Nessuna modifica in pochi minuti della semantica di Python renderà gli I / O più veloci e danneggerà solo la leggibilità e la manutenibilità. Concentrati su aree di maggiore impatto, direi.
Martijn Pieters,

12

assertè inteso per i casi in cui il programmatore che chiama la funzione ha commesso un errore, al contrario dell'utente . L'utilizzo assertin tale circostanza consente di assicurarsi che un programmatore stia utilizzando correttamente la propria funzione durante i test, ma poi lo elimina durante la produzione.

Il suo valore è alquanto limitato poiché devi assicurarti di esercitare quel percorso attraverso il codice e spesso vuoi gestire ulteriormente il problema con ifun'istruzione separata in produzione. assertè molto utile in situazioni come "Voglio risolvere il problema in modo utile se un utente lo risolve, ma se uno sviluppatore lo colpisce, voglio che si blocchi improvvisamente in modo da correggere il codice che chiama questa funzione in modo errato".

Nel tuo caso particolare, un file mancante è quasi certamente un errore dell'utente e dovrebbe essere gestito sollevando un'eccezione.


5

Da UsingAssertionsEffectively

Controllare isinstance () non dovrebbe essere abusato: se si rompe come un'anatra, forse non è necessario indagare troppo a fondo se lo è davvero. A volte può essere utile passare valori che non erano previsti dal programmatore originale.

Luoghi da considerare mettendo affermazioni:

checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable 

Il punto generale è che se qualcosa va storto, vogliamo renderlo completamente ovvio il prima possibile.

È più facile rilevare dati errati nel punto in cui si trova piuttosto che capire come sono arrivati ​​in seguito quando causano problemi.

Le asserzioni non sostituiscono test unitari o test di sistema, ma piuttosto un complemento. Poiché le asserzioni sono un modo chiaro per esaminare lo stato interno di un oggetto o di una funzione, forniscono "gratuitamente" un'assistenza chiara a un test black-box che esamina il comportamento esterno.

Le asserzioni non devono essere utilizzate per verificare casi di errore che possono verificarsi a causa di input dell'utente errato o errori del sistema operativo / ambiente, ad esempio un file non trovato. Invece, dovresti sollevare un'eccezione o stampare un messaggio di errore o qualunque cosa sia appropriata. Un motivo importante per cui le asserzioni dovrebbero essere utilizzate solo per gli autotest del programma è che le asserzioni possono essere disabilitate al momento della compilazione.

Se Python viene avviato con l'opzione -O, le asserzioni verranno rimosse e non valutate. Quindi, se il codice usa asserzioni pesanti, ma è critico per le prestazioni, allora c'è un sistema per disattivarle nelle build di rilascio. (Ma non farlo a meno che non sia veramente necessario. È stato scientificamente provato che alcuni bug si manifestano solo quando un cliente usa la macchina e vogliamo che anche affermazioni aiutino lì. :-))

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.