2012-05-23 16 views
17

In una classe Python, quale tipo di errore dovrei sollevare da un metodo di istanza quando alcuni degli altri attributi della classe devono essere modificati prima di eseguire quel metodo?Che errore si genera quando lo stato della classe non è valido?

Provengo da uno sfondo C# in cui vorrei usare InvalidOperationException, "l'eccezione che viene generata quando una chiamata di metodo non è valida per lo stato corrente dell'oggetto", ma non ho trovato un equivalente built-in exception in Python.

Sto sollevando ValueError ("generato quando un'operazione o una funzione incorporata riceve un argomento che ha il tipo giusto ma un valore inappropriato") quando il problema riguarda i parametri della funzione. Suppongo che questo sia tecnicamente un valore non valido per il parametro self; è questo il modo giusto per trattarlo? Ad esempio, è questo idiomatico: raise ValueError("self.foo must be set before running self.bar()")?

+1

ValueError mi sembra buono. È abbastanza vicino perché un utente possa identificarne l'associazione con il problema. Inoltre non è come il pitone che ti schiaffeggia al polso per usare l'eccezione sbagliata. Questo è abbastanza vicino da separare da altri errori. – jdi

+1

Perché non creare la propria eccezione se si sente la necessità di fornire maggiori dettagli? –

+1

@LattyWare: ho visto i dialoghi canticchiare la sottoclasse di nuovi tipi di eccezioni. Suggeriscono che ci sono molti builtins e aggiunge solo complessità. – jdi

risposta

17

ValueError è la cosa migliore da sollevare in questo caso. Per Python, dovresti preferire l'uso di built-in exception types per creare il tuo. Dovresti creare solo nuovi tipi di eccezioni quando ti aspetti che dovrai prenderli e comportarti in modo molto diverso da come ti comporteresti quando prendi i tipi di build. In questo caso, la situazione non dovrebbe sorgere - non ti aspetti di prenderla perché potrebbe indicare un errore nell'uso della classe in questione. Per questo non vale la pena creare un nuovo tipo solo per fargli avere un altro nome - questo è ciò che è la stringa di messaggio che si passa a ValueError().

È possibile ristrutturare la classe in modo tale che uno stato non valido non sia possibile?

+2

Grazie - Immagino che il punto importante sia che nessuno si preoccuperà di rilevare l'eccezione (sarebbe una ["eccezione a boneheaded"] (http://blogs.msdn.com/b/ericlippert/archive/2008/09 /10/vexing-exceptions.aspx) nel client) – Justin

+1

Se nessuno lo cattura e funge solo da controllo interno, è possibile utilizzare "asserire". Quando l'espressione in un'asserzione è False, viene sollevata un'eccezione AssertionError. – MarioVilas

+3

Un buon esempio per sollevare "ValueError' su una chiamata in uno stato non valido può essere trovato in Python stesso: http://hg.python.org/cpython/file/v3.3.1/Lib/subprocess.py#l881 –

1
class InvalidOperationException(Exception): 
    pass 

SYS_STATE = 1 

def something_being_run(): 
    if SYS_STATE < 2: 
     raise InvalidOperationException 

Qualcosa del genere intendi? Non vedo alcun motivo per cui non dovresti eseguire un'eccezione di sottoclasse per creare i tuoi tipi di eccezione, ma potrebbe essere solo il vecchio Oracle PL/SQL Dev in me che esce ...

+1

Se tutto quello che stai facendo è creare sottoclassi per un nome e passare, non è necessario. – jdi

+2

È necessario se qualcuno prenderà quell'eccezione più tardi. Mi sembra una soluzione corretta, anche se sono d'accordo che OP chiedesse invece un'eccezione integrata. – MarioVilas

3

Penso che il modo pitone non lasci l'oggetto in uno stato tale in cui una chiamata al metodo non si arresti in modo anomalo nonostante si trovi in ​​uno stato errato. Questi sono i bug più difficili da trovare, dato che il punto in cui il programma finisce definitivamente non è dove si è verificato l'errore.

es.

class PseudoTuple(object): 
    """ 
The sum method of PseudoTuple will raise an AttributeError if either x or y have 
not been set 
""" 
    def setX(self, x): 
     self.x = x 

    def setY(self, y): 
     self.y = y 

    def sum(self): 
     """ 
In the documentation it should be made clear that x and y need to have been set 
for sum to work properly 
""" 
     return self.x + self.y 

class AnotherPseudoTuple(PseudoTuple): 
    """ 
For AnotherPseudoTuple sum will now raise a TypeError if x and y have not been 
properly set 
""" 
    def __init__(self, x=None, y=None): 
     self.x = x 
     self.y = y 

Quello che non si deve fare è qualcosa di simile

class BadPseudoTuple(PseudoTuple): 
    """ 
In BadPseudoTuple -1 is used to indicate an invalid state 
""" 
    def __init__(self, x=-1, y=-1): 
     self.x = x 
     self.y = y 

    def sum(self): 
     if self.x == -1 or self.y == -1: 
      raise SomeException("BadPseudoTuple in invalid state") 
     else: 
      return self.x + self.y 

Credo che questo avviene sotto il motto divinatorio di:

E 'più facile chiedere perdono che è di ottenere il permesso

Se lo stato eccezionale è qualcosa che può essere si prevede che accada durante il normale corso dell'esecuzione piuttosto che essere un errore dell'utente abusando della classe, quindi sembra ragionevole che tu debba creare la tua eccezione. StopIteration e iterators ne sono un esempio.

+2

Prendo il punto che "Nessuno" è un indicatore migliore di stato non valido di valore speciale come '-1'. Nel mio caso, la condizione che sto verificando è piuttosto complicata che coinvolge quattro attributi diversi, quindi vorrei includere un messaggio di errore per guidare l'utente della classe, comunque. Lo scopo del metodo è di inviare comandi a un sistema esterno, quindi non posso semplicemente ignorare i valori non validi. – Justin

+1

Forse potresti voler usare 'IOError' allora. Dopo tutto l'errore è dire che c'è qualcosa riguardo allo stato dell'oggetto che significa che la comunicazione non può essere eseguita. – Dunes

-1

Penso che si dovrebbe aumentare un ValueError quando il problema è con i parametri di funzione, come si sta facendo, e un AttributeError quando il problema è con un attributo che deve essere impostato.

Inoltre, è possibile creare sottoclasse dello AttributeError per creare un'eccezione più specifica, ma non è necessario. L'eccezione AttributeError con il tuo messaggio di errore è abbastanza chiara.

+1

AttributeError è quando manca un attributo, non ha nulla a che fare con lo stato logico interno degli oggetti. – MarioVilas

0

ValueError va bene per me, ma penso che AssertionError sia più appropriato. Fondamentalmente, viola l'asserzione fatta dal progettista dell'API.

+1

Penso che "AssertionError" sia principalmente riservato a 'assert' e costrutti simili, quindi probabilmente useresti semplicemente l'istruzione 'assert' per sollevarla. Ma a volte questa affermazione viene ignorata a seconda della configurazione dell'interprete, poiché "assert" è di solito destinato al testing, non alla produzione. Quindi penso che "AssertionError" non sia la scelta migliore per questo stato, poiché interferirà con il test delle eccezioni. – MarSoft

5

Io trovo il RuntimeError il più appropriato di tutte le eccezioni built-in per segnalare lo stato non valido.

Vedi questo esempio di come viene utilizzato in CPython:

Python 2.7.10 (default, Jul 13 2015, 12:05:58) 
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from threading import Thread 
>>> Thread().join() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 938, in join 
    raise RuntimeError("cannot join thread before it is started") 
RuntimeError: cannot join thread before it is started 

E 'importante notare che anche l'attuazione CPython sé non è coerente circa l'uso di specifici tipi di eccezione tra le librerie. A volte viene usato lo ValueError, ma a mio avviso la sua descrizione dalla documentazione di Python mostra che il suo uso è riservato ad altre situazioni. RuntimeError è un'eccezione più generale e dovrebbe essere utilizzata quando un pezzo di codice non può comportarsi correttamente se viene fornito un input appropriato, che è in qualche modo simile alla situazione in cui un oggetto si trova in uno stato non valido.

Problemi correlati