Uso de "aumento de" Python

196

¿Cuál es la diferencia entre raisey raise fromen Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

cuyos rendimientos

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

y

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

cuyos rendimientos

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError
darkfeline
fuente
9
¿Has leído PEP-3134 ?
jonrsharpe
44
Ahora use raise IndexError from None, digamos.
Martijn Pieters
11
Je raise IndexError from Falseplantea un TypeError, no un IndexError. Me alegró el día.
Físico loco
No estoy seguro si este es el lugar correcto para mencionarlo, pero para cualquiera que use Spyder: toda esta construcción no funciona allí. Ha sido un problema durante más de 3 años ( github.com/spyder-ide/spyder/issues/2943 ), pero parecen pensar que no hay necesidad de excepciones encadenadas.
Emil Bode

Respuestas:

227

La diferencia es que cuando usa from, el __cause__atributo se establece y el mensaje indica que la excepción fue causada directamente por . Si omite, fromentonces no __cause__se establece, pero el __context__atributo también se puede establecer, y el rastreo luego muestra el contexto como durante el manejo de que sucedió algo más .

Establecer lo que __context__sucede si lo usó raiseen un controlador de excepciones; si lo usó en raisecualquier otro lugar, tampoco __context__está configurado.

Si __cause__se establece a, __suppress_context__ = Truetambién se establece un indicador en la excepción; cuando __suppress_context__se establece en True, __context__se ignora al imprimir un rastreo.

Cuando se plantea desde un manejador de excepción en el que no desea mostrar el contexto (no quiero una durante la manipulación otra excepción ocurrió mensaje), a continuación, utilizar raise ... from Noneal conjunto __suppress_context__a True.

En otras palabras, Python establece un contexto de excepciones para que pueda introspectar dónde se generó una excepción, permitiéndole ver si otra excepción fue reemplazada por ella. También puede agregar una causa a una excepción, haciendo que el rastreo sea explícito sobre la otra excepción (use una redacción diferente), y el contexto se ignora (pero aún puede ser introspectado al depurar). El uso le raise ... from Nonepermite suprimir el contexto que se imprime.

Vea la raisedocumentación de la declaración :

La fromcláusula se usa para el encadenamiento de excepciones: si se proporciona, la segunda expresión debe ser otra clase o instancia de excepción, que luego se adjuntará a la excepción generada como el __cause__atributo (que se puede escribir). Si no se maneja la excepción planteada, se imprimirán ambas excepciones:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Un mecanismo similar funciona implícitamente si se genera una excepción dentro de un controlador de excepciones o una finallycláusula: la excepción anterior se adjunta como el __context__atributo de la nueva excepción :

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Consulte también la documentación de Excepciones incorporadas para obtener detalles sobre el contexto y la información de causa adjunta a las excepciones.

Martijn Pieters
fuente
11
¿Hay alguna razón para encadenar explícitamente las excepciones usando fromy __cause__en lugar de lo implícito __context__? ¿Hay algún caso en el que se adjunte una excepción diferente a la capturada por el except?
darkfeline
13
@darkfeline: Digamos que su API de base de datos admite la apertura de bases de datos de varias fuentes, incluida la web y el disco. Su API siempre generará un DatabaseErrorerror si la apertura de la base de datos falla. Pero si la falla es el resultado de un IOErrorporque un archivo no se pudo abrir o HTTPErrorporque una URL no funcionó, entonces ese es el contexto que desea incluir explícitamente, por lo que el desarrollador que usa la API puede depurar por qué esto es así. En ese momento lo usas raise DatabaseError from original_exception.
Martijn Pieters
44
@darkfeline: Si que el desarrollador está envolviendo el uso de la API de base de datos en su propio API y quería transmitir que IOErrorni HTTPErrora sus consumidores, entonces tendrían que su uso raise NewException from databaseexception.__cause__, ahora usando una excepción diferente de la DatabaseExceptionque sólo atrapados.
Martijn Pieters
2
@ dan3: no, no lo hay. El encadenamiento de excepciones es puramente una característica de Python 3.
Martijn Pieters
55
@ laike9m: ¿quieres decir cuando estás manejando una excepción fooy quieres generar una nueva excepción bar? Entonces puede usar raise bar from fooy tener el estado de Python que foo causó directamentebar . Si no lo usa from foo, Python seguirá imprimiendo ambos, pero indique que durante el manejo foo, barse generó un mensaje diferente, destinado a marcar un posible error en el manejo del error.
Martijn Pieters