2012-05-23 19 views
17

En una clase de Python, ¿qué tipo de error debo plantear desde un método de instancia cuando se deben cambiar algunos de los otros atributos de la clase antes de ejecutar ese método?¿Qué error generar cuando el estado de la clase no es válido?

Vengo de un fondo de C# donde usaría InvalidOperationException, "la excepción que se produce cuando una llamada de método no es válida para el estado actual del objeto", pero no pude encontrar un equivalente built-in exception in Python.

He estado planteando ValueError ("se produce cuando una función o función incorporada recibe un argumento que tiene el tipo correcto pero un valor inapropiado") cuando el problema es con los parámetros de la función. Supongo que técnicamente es un valor inválido para el parámetro self; ¿Es esa la forma correcta de tratarlo? Por ejemplo, ¿esto es idiomático: raise ValueError("self.foo must be set before running self.bar()")?

+1

ValueError me parece bien. Está lo suficientemente cerca para que un usuario identifique su asociación con el problema. Tampoco es como si Python te pegara en la muñeca por usar la excepción incorrecta. Esto es lo suficientemente cerca para separarse de otros errores. – jdi

+1

¿Por qué no hacer su propia excepción si siente la necesidad de dar más detalles? –

+1

@LattyWare: He visto las conversaciones engañosas sobre la subclasificación willy nilly de nuevos tipos de excepciones. Sugieren que hay muchas construcciones internas y solo agrega complejidad. – jdi

Respuesta

17

ValueError es lo mejor que puedo plantear en este caso. Para python, debería preferir usar el built-in exception types antes que crear el suyo. Solo debe crear nuevos tipos de excepciones cuando espera que tenga que atraparlos y comportarse de manera muy diferente de lo que se comportaría al capturar los tipos integrados. En este caso, la situación no debería surgir; no espera detectar esto porque indicaría un error al usar la clase en cuestión. Para esto, no vale la pena crear un nuevo tipo solo para que tenga otro nombre, para eso sirve la cadena de mensajes que pasas al ValueError().

¿Es posible reestructurar su clase para que tal estado inválido no sea posible?

+2

Gracias - Creo que el punto importante es que a nadie le importaría captar la excepción (sería una ["excepción descarada"] (http://blogs.msdn.com/b/ericlippert/archive/2008/09) /10/vexing-exceptions.aspx) en el cliente) – Justin

+1

Si nadie va a atraparlo y solo sirve como una verificación interna, puede usar "assert" en su lugar. Cuando la expresión en una afirmación es False, se genera una excepción AssertionError. – MarioVilas

+3

Un buen ejemplo para generar 'ValueError' en una llamada en un estado no válido se puede encontrar en Python mismo: 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 

¿Algo así te refieres? No veo ninguna razón por la que no deba hacer una excepción de subclase para crear sus propios tipos de Excepción, pero podría ser el viejo Oracle PL/SQL Dev en mi caso ...

+1

Si todo lo que hace es crear subclases para un nombre y pasar, no es necesario. – jdi

+2

Es necesario si alguien va a atrapar esa excepción más tarde. Me parece una solución correcta, aunque estoy de acuerdo en que OP estaba pidiendo una excepción incorporada. – MarioVilas

3

Creo que la forma pitónica no es dejar el objeto en un estado en el que una llamada a un método no se bloquee a pesar de estar en un estado erróneo. Estos son los errores más difíciles de encontrar, ya que el punto donde el programa finalmente se desploma no es donde ocurrió el error.

por ejemplo.

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 

Lo que no se debe hacer es algo así como

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 

Creo que esto viene bajo el lema Pythonic de:

Es más fácil pedir perdón que es para obtener el permiso

Si el estado excepcional es algo que puede ser se espera que suceda durante el curso normal de la ejecución en lugar de ser un error del usuario al hacer un uso incorrecto de la clase, entonces parece razonable que deba crear su propia excepción. StopIteration y los iteradores son un ejemplo de esto.

+2

Tomo el punto de que 'None' es un mejor marcador de estado inválido que un valor especial como '-1'. En mi caso, la condición que estoy verificando es bastante complicada y tiene cuatro atributos diferentes, por lo que me gustaría incluir un mensaje de error para guiar al usuario de la clase. El objetivo del método es enviar comandos a un sistema externo, por lo que no puedo ignorar los valores no válidos. – Justin

+1

Quizás le interese usar 'IOError' luego. Después de todo el error es decir que hay algo sobre el estado del objeto que significa que la comunicación no puede llevarse a cabo. – Dunes

-1

Creo que deberías plantear un ValueError cuando el problema está con los parámetros de la función, como lo estás haciendo, y un AttributeError cuando el problema es con un atributo que se debe establecer.

Además, podría subclasificar el AttributeError para hacer una excepción más específica, pero no lo veo necesario. La excepción AttributeError con su mensaje de error es lo suficientemente clara.

+1

AttributeError es cuando falta un atributo, no tiene nada que ver con el estado lógico interno de los objetos. – MarioVilas

0

ValueError está bien para mí, pero creo que AssertionError es más apropiado. Básicamente, viola la afirmación hecha por el diseñador de API.

+1

Creo que 'AssertionError' está mayormente reservado para' assert' y construcciones similares, por lo que probablemente solo use la declaración 'assert' para elevarlo. Pero esa declaración a veces se ignora dependiendo de la configuración del intérprete, ya que 'assert' generalmente se usa para pruebas, no para producción. Así que creo que 'AssertionError' no es la mejor opción para este estado, ya que interferirá con las excepciones de prueba. – MarSoft

5

Encuentro RuntimeError la más adecuada de todas las excepciones incorporadas para señalar el estado no válido.

ver este ejemplo de la forma en que se utiliza en 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 

Es importante notar que incluso la propia aplicación CPython no es coherente sobre el uso de tipos de excepciones específicas entre las bibliotecas. A veces se usa ValueError, sin embargo, en mi opinión, su descripción de la documentación de Python muestra que su uso está reservado para otras situaciones. RuntimeError es una excepción más general y se debe usar cuando un fragmento de código no se puede comportar correctamente si se le dio una entrada adecuada, que es algo similar a la situación cuando un objeto está en un estado no válido.

Cuestiones relacionadas