Quale eccezione dovrei sollevare su combinazioni di argomenti errati / illegali in Python?


545

Mi chiedevo quali fossero le migliori pratiche per indicare combinazioni di argomenti non valide in Python. Mi sono imbattuto in alcune situazioni in cui hai una funzione del genere:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

L'unico fastidio è che ogni pacchetto ha il suo, di solito leggermente diverso BadValueError. So che in Java esiste java.lang.IllegalArgumentException- è chiaro che tutti creeranno i propri BadValueErrorin Python o esiste un altro metodo preferito?

Risposte:


609

Vorrei solo aumentare ValueError , a meno che tu non abbia bisogno di un'eccezione più specifica ..

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

Non ha davvero senso farlo class BadValueError(ValueError):pass: la tua classe personalizzata è identica in uso a ValueError , quindi perché non usarla?


65
> "quindi perché non usarlo?" - Specificità. Forse voglio catturare un livello esterno "MyValueError", ma non tutti / tutti "ValueError".
Kevin Little,

7
Sì, quindi parte della questione della specificità è dove altro viene sollevato ValueError. Se alla funzione di chiamata piacciono i tuoi argomenti ma chiama math.sqrt (-1) internamente, un chiamante potrebbe catturare ValueError aspettandosi che i suoi argomenti fossero inappropriati. Forse hai solo controllato il messaggio in questo caso ...
cdleary,

3
Non sono sicuro che l'argomento sia valido: se qualcuno sta chiamando math.sqrt(-1), è comunque un errore di programmazione che deve essere corretto. ValueErrornon è destinato a essere catturato durante la normale esecuzione del programma o ne deriverebbe RuntimeError.
ereOn

2
Se l'errore si trova sul NUMERO di argomenti, per una funzione con un numero variabile di argomenti ... ad esempio una funzione in cui gli argomenti devono essere un numero pari di argomenti, è necessario generare un TypeError, per essere coerenti. E non creare la tua classe a meno che a) non hai un caso d'uso o b) stai esportando la libreria per essere utilizzata da altri. La funzionalità prematura è la morte del codice.
Erik Aronesty,

104

Vorrei ereditare da ValueError

class IllegalArgumentError(ValueError):
    pass

A volte è meglio creare le tue eccezioni, ma ereditarne una incorporata, che è il più vicino possibile a ciò che desideri.

Se devi rilevare questo errore specifico, è utile avere un nome.


26
Smetti di scrivere lezioni ed eccezioni personalizzate - pyvideo.org/video/880/stop-writing-classes
Hamish Grubijan

40
@HamishGrubijan quel video è terribile. Quando qualcuno ha suggerito un buon uso di una classe, ha semplicemente battuto "Non usare le classi". Brillante. Le lezioni sono buone. Ma non crederci sulla parola .
Rob Grant,

12
@RobertGrant No, non lo capisci. Quel video non riguarda letteralmente "non usare le classi". Si tratta di non complicare eccessivamente le cose.
RayLuo,

15
@RayLuo potresti aver verificato la sanità mentale di ciò che dice il video e convertirlo in un messaggio alternativo ragionevole e ragionevole, ma è quello che dice il video, ed è quello che qualcuno che non ha molta esperienza e buon senso verrà via con.
Rob Grant,

3
@ SamuelSantana, come ho detto, ogni volta che qualcuno alza la mano e dice "che ne dici di X?" dove X era una buona idea, ha appena detto, "non fare un'altra lezione". Abbastanza chiaro Sono d'accordo che la chiave è l'equilibrio; il problema è che è troppo vago per vivere davvero :-)
Rob Grant il

18

Penso che il modo migliore per gestirlo sia il modo in cui lo stesso Python lo gestisce. Python genera un TypeError. Per esempio:

$ python -c 'print(sum())'
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: sum expected at least 1 arguments, got 0

Il nostro sviluppatore junior ha appena trovato questa pagina in una ricerca su Google per "argomenti sbagliati sull'eccezione di Python" e sono sorpreso che la risposta ovvia (per me) non sia mai stata suggerita nel decennio da quando è stata posta questa domanda.


8
Nulla mi sorprende, ma sono d'accordo al 100% che TypeError è l'eccezione corretta se il tipo è errato su alcuni degli argomenti passati alla funzione. Un ValueError sarebbe adatto se le variabili sono di tipo corretto ma i loro contenuti e valori non hanno senso.
user3504575

Penso che questo sia probabilmente per argomenti mancanti o non richiesti, mentre la domanda riguarda argomenti che sono forniti correttamente, ma non sono corretti su un livello di astrazione più elevato che coinvolge il valore dell'argomento dato. Ma dato che in realtà stavo cercando la prima, quindi ho comunque un voto.
Nessuno il

2
Come dicevano @ user3504575 e @Nobody, TypeError viene utilizzato se gli argomenti non corrispondono alla firma della funzione (numero errato di argomenti posizionali, argomenti di parole chiave con il nome sbagliato, tipo di argomento errato), ma quando viene chiamata la funzione viene utilizzato un ValueError corrisponde alla firma ma i valori dell'argomento non sono validi (ad es. chiamata int('a')). fonte
goodmami,

Dato che la domanda del PO si riferiva a "combinazioni di argomenti non valide", sembra che TypeError sarebbe appropriato in quanto questo sarebbe un caso in cui la firma della funzione è essenzialmente errata per gli argomenti passati.
J Bones,

Il tuo esempio chiama sum()senza argomenti, che è a TypeError, ma l'OP si preoccupava di combinazioni "illegali" di valori di argomento quando i tipi di argomento sono corretti. In questo caso, entrambi savee recursesono bool, ma se recurseè Trueallora savenon dovrebbe essere False. Questo è un ValueError. Concordo sul fatto che una certa interpretazione del titolo della domanda avrebbe avuto una risposta TypeError, ma non per l'esempio presentato.
goodmami,


8

Dipende da quale sia il problema con gli argomenti.

Se l'argomento ha il tipo sbagliato, sollevare un TypeError. Ad esempio, quando ottieni una stringa anziché uno di quei booleani.

if not isinstance(save, bool):
    raise TypeError(f"Argument save must be of type bool, not {type(save)}")

Si noti, tuttavia, che in Python raramente effettuiamo controlli come questo. Se l'argomento non è valido, una funzione più approfondita probabilmente ci farà lamentare. E se controlliamo solo il valore booleano, forse un utente del codice in seguito lo inserirà semplicemente in una stringa sapendo che le stringhe non vuote sono sempre True. Potrebbe salvargli un cast.

Se gli argomenti hanno valori non validi, aumentare ValueError. Questo sembra più appropriato nel tuo caso:

if recurse and not save:
    raise ValueError("If recurse is True, save should be True too")

O in questo caso specifico, avere un valore True di recurse implica un valore True di save. Dal momento che considererei questo un recupero da un errore, potresti anche lamentarti nel registro.

if recurse and not save:
    logging.warning("Bad arguments in import_to_orm() - if recurse is True, so should save be")
    save = True

Penso che questa sia la risposta più accurata. Questo è ovviamente sottovalutato (7 voti finora incluso il mio).
Siu Ching Pong -Asuka Kenji-

-1

Non sono sicuro che sono d'accordo con l'eredità dal ValueError- la mia interpretazione della documentazione è che ValueErrorè solo suppone che sia sollevata dal builtins ... ereditando da esso o sollevare da soli sembra errato.

Generato quando un'operazione o una funzione incorporata riceve un argomento che ha il tipo giusto ma un valore inappropriato e la situazione non è descritta da un'eccezione più precisa come IndexError.

- Documentazione ValueError


Confronta google.com/codesearch?q=lang:python+class \ + \ w Errore (([^ E] \ w * | E [^ x] \ w )): con google.com/codesearch?q=lang: python + class \ + \ w * Errore (eccezione):
Markus Jarderot

13
Quel blurb significa semplicemente che i built-in lo aumentano e non solo i built-in possono aumentarlo. In questa istanza non sarebbe del tutto appropriato per la documentazione di Python parlare di ciò che le librerie esterne generano.
Ignacio Vazquez-Abrams,

5
Ogni pezzo di software Python che abbia mai visto ha usato ValueErrorper questo genere di cose, quindi penso che tu stia cercando di leggere troppo nella documentazione.
James Bennett,

6
Err, se useremo le ricerche del codice di Google per argomentare questo: google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66.300 casi di raccolta di ValueError, inclusi Zope, xen, Django, Mozilla (e è solo dalla prima pagina dei risultati). Se si adatta un'eccezione incorporata,
usala

7
Come detto, la documentazione è ambigua. Avrebbe dovuto essere scritto come "Sollevato quando riceve un'operazione incorporata o una funzione incorporata" o come "Sollevato quando riceve una funzione o operazione incorporata". Ovviamente, qualunque sia l'intento originale, la pratica attuale l'ha vinta (come sottolinea @dbr). Quindi dovrebbe essere riscritto come la seconda variante.
Eponimo

-1

Concordo con il suggerimento di Markus di presentare la propria eccezione, ma il testo dell'eccezione dovrebbe chiarire che il problema si trova nell'elenco degli argomenti, non i singoli valori dell'argomento. Proporrei:

class BadCallError(ValueError):
    pass

Utilizzato quando mancano gli argomenti delle parole chiave richiesti per la chiamata specifica o i valori degli argomenti sono individualmente validi ma incoerenti tra loro. ValueErrorsarebbe comunque giusto quando un argomento specifico è giusto, ma fuori range.

Non dovrebbe essere un'eccezione standard in Python?

In generale, vorrei che lo stile Python fosse un po 'più nitido nel distinguere input errati in una funzione (errore del chiamante) da risultati negativi all'interno della funzione (errore mio). Quindi potrebbe esserci anche un BadArgumentError per distinguere gli errori di valore negli argomenti dagli errori di valore nei locali.


Solleverei KeyErrorper parola chiave non trovata (poiché una parola chiave esplicita mancante è semanticamente identica a una parola **kwargsche manca quella chiave).
Cowbert,
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.