Sollevare eccezioni e restituire Nessuno nelle funzioni?


87

Qual è la pratica migliore in una funzione definita dall'utente in Python: raiseun'eccezione o return None? Ad esempio, ho una funzione che trova il file più recente in una cartella.

def latestpdf(folder):
    # list the files and sort them
    try:
        latest = files[-1]
    except IndexError:
        # Folder is empty.
        return None  # One possibility
        raise FileNotFoundError()  # Alternative
    else:
        return somefunc(latest)  # In my case, somefunc parses the filename

Un'altra opzione è lasciare l'eccezione e gestirla nel codice del chiamante, ma immagino sia più chiaro trattare con a FileNotFoundErrorche con IndexError. O è una cattiva idea sollevare nuovamente un'eccezione con un nome diverso?



3
Sono propenso a sollevare un'eccezione, quindi sono costretto a gestire l'eccezione nella funzione chiamante. Se dimentico di controllare se l'output è Nessuno nella funzione chiamante, potrei avere un bug latente. Se hai restituito None, si spera che la riga successiva nella funzione chiamante solleverà un'eccezione AttributeError. Tuttavia, se il valore restituito viene aggiunto a un dizionario e quindi 100 chiamate di funzione in un file sorgente diverso viene sollevato un'eccezione AttributeError, ti divertirai a cercare il motivo per cui quel valore era Nessuno.
IceArdor

In generale, evito anche valori che hanno un significato speciale o che hanno più firme per una funzione (potrebbe restituire una stringa o Nessuno).
IceArdor

Risposte:


92

È davvero una questione di semantica. Cosa foo = latestpdf(d) significa ?

È perfettamente ragionevole che non ci siano file più recenti? Quindi certo, restituisci semplicemente Nessuno.

Ti aspetti di trovare sempre un file più recente? Sollevare un'eccezione. E sì, rilanciare nuovamente un'eccezione più appropriata va bene.

Se questa è solo una funzione generale che dovrebbe essere applicata a qualsiasi directory, farei la prima e restituirei None. Se la directory è, ad esempio, intesa per essere una directory di dati specifica che contiene l'insieme noto di file di un'applicazione, solleverò un'eccezione.


3
Un altro punto da considerare: se si solleva un'eccezione, è possibile allegare un messaggio, ma non possiamo farlo al ritorno None.
kawing-chiu

10

Vorrei dare un paio di suggerimenti prima di rispondere alla tua domanda in quanto potrebbe rispondere alla domanda per te.

  • Assegna sempre un nome descrittivo alle funzioni. latestpdfsignifica molto poco per nessuno, ma guardando oltre la tua funzione latestpdf()ottieni l'ultimo pdf. Suggerirei di chiamarlo getLatestPdfFromFolder(folder).

Non appena l'ho fatto è diventato chiaro cosa dovrebbe restituire .. Se non c'è un pdf solleva un'eccezione. Ma aspetta di più ..

  • Mantieni le funzioni chiaramente definite. Dal momento che non è evidente cosa dovrebbe fare somefuc e non è (apparentemente) ovvio come si relaziona all'ottenimento dell'ultimo pdf, ti suggerirei di spostarlo. Ciò rende il codice molto più leggibile.

for folder in folders:
   try:
       latest = getLatestPdfFromFolder(folder)
       results = somefuc(latest)
   except IOError: pass

Spero che sia di aiuto!


1
Oppure get_latest_pdf_from_folder. Infatti, Pep8: "I nomi delle funzioni dovrebbero essere minuscoli, con le parole separate da trattini bassi se necessario per migliorare la leggibilità."
PatrickT

7

Di solito preferisco gestire le eccezioni internamente (cioè try / tranne all'interno della funzione chiamata, possibilmente restituendo None) perché python è tipizzato dinamicamente. In generale, lo considero una chiamata di giudizio in un modo o nell'altro, ma in un linguaggio digitato dinamicamente, ci sono piccoli fattori che fanno pendere la bilancia a favore di non passare l'eccezione al chiamante:

  1. Chiunque chiami la tua funzione non viene informato delle eccezioni che possono essere generate. Diventa un po 'una forma d'arte sapere che tipo di eccezione stai cercando (e dovrebbero essere evitati i blocchi generici eccetto).
  2. if val is Noneè un po 'più facile di except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace. Seriamente, odio dover ricordare di digitare from django.core.exceptions import ObjectDoesNotExistall'inizio di tutti i miei file django solo per gestire un caso d'uso molto comune. In un mondo digitato staticamente, lascia che sia l'editor a farlo per te.

Onestamente, però, è sempre una chiamata di giudizio, e la situazione che stai descrivendo, in cui la funzione chiamata riceve un errore che non può aiutare, è un ottimo motivo per rilanciare un'eccezione che è significativa. Hai esattamente l'idea giusta, ma a meno che tu non sia un'eccezione, fornirai informazioni più significative in una traccia dello stack rispetto a

AttributeError: 'NoneType' object has no attribute 'foo'

che, nove volte su dieci, è ciò che il chiamante vedrà se restituisci un Nessuno non gestito, non preoccuparti.

(Tutto questo mi fa desiderare che le eccezioni python avessero gli causeattributi di default, come in java, che ti consente di passare le eccezioni in nuove eccezioni in modo da poter lanciare nuovamente tutto ciò che desideri e non perdere mai la fonte originale del problema.)


L'argomento che le possibili eccezioni non sono definite e quindi è difficile da catturare è un argomento molto valido per Python.
snorberhuis

4

con la digitazione di Python 3.5 :

la funzione di esempio quando si restituisce None sarà:

def latestpdf(folder: str) -> Union[str, None]

e quando si solleva un'eccezione sarà:

def latestpdf(folder: str) -> str 

l'opzione 2 sembra più leggibile e pitonica

(+ opzione per aggiungere un commento all'eccezione come affermato in precedenza.)


5
Union[str, None]dovrebbe essereOptional[str]
Georgy

2
una stenografia, ma hai ragione, è più leggibile. non modificare quindi entrambe le opzioni sono qui.
Asaf

1
2 è potenzialmente più leggibile ma (sfortunatamente?) I suggerimenti sul tipo non indicano che potrebbe essere generata un'eccezione. Recentemente ho scoperto che 1 aiuterà a rilevare più errori poiché sei costretto a gestire un ritorno Nessuno.
jonespm

2

In generale, direi che dovrebbe essere lanciata un'eccezione se si è verificato qualcosa di catastrofico che non può essere recuperato (ovvero la tua funzione si occupa di alcune risorse Internet a cui non è possibile connettersi), e dovresti restituire None se la tua funzione dovesse davvero restituire qualcosa ma non sarebbe appropriato restituire nulla (ad esempio "Nessuno" se la funzione cerca di trovare una corrispondenza con una sottostringa in una stringa, ad esempio).

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.