Difference between "raise" and "raise e"?
Question:
In python, is there a difference between raise
and raise e
in an except block?
dis
is showing me different results, but I don’t know what it means.
What’s the end behavior of both?
import dis
def a():
try:
raise Exception()
except Exception as e:
raise
def b():
try:
raise Exception()
except Exception as e:
raise e
dis.dis(a)
# OUT: 4 0 SETUP_EXCEPT 13 (to 16)
# OUT: 5 3 LOAD_GLOBAL 0 (Exception)
# OUT: 6 CALL_FUNCTION 0
# OUT: 9 RAISE_VARARGS 1
# OUT: 12 POP_BLOCK
# OUT: 13 JUMP_FORWARD 22 (to 38)
# OUT: 6 >> 16 DUP_TOP
# OUT: 17 LOAD_GLOBAL 0 (Exception)
# OUT: 20 COMPARE_OP 10 (exception match)
# OUT: 23 POP_JUMP_IF_FALSE 37
# OUT: 26 POP_TOP
# OUT: 27 STORE_FAST 0 (e)
# OUT: 30 POP_TOP
# OUT: 7 31 RAISE_VARARGS 0
# OUT: 34 JUMP_FORWARD 1 (to 38)
# OUT: >> 37 END_FINALLY
# OUT: >> 38 LOAD_CONST 0 (None)
# OUT: 41 RETURN_VALUE
dis.dis(b)
# OUT: 4 0 SETUP_EXCEPT 13 (to 16)
# OUT: 5 3 LOAD_GLOBAL 0 (Exception)
# OUT: 6 CALL_FUNCTION 0
# OUT: 9 RAISE_VARARGS 1
# OUT: 12 POP_BLOCK
# OUT: 13 JUMP_FORWARD 25 (to 41)
# OUT: 6 >> 16 DUP_TOP
# OUT: 17 LOAD_GLOBAL 0 (Exception)
# OUT: 20 COMPARE_OP 10 (exception match)
# OUT: 23 POP_JUMP_IF_FALSE 40
# OUT: 26 POP_TOP
# OUT: 27 STORE_FAST 0 (e)
# OUT: 30 POP_TOP
# OUT: 7 31 LOAD_FAST 0 (e)
# OUT: 34 RAISE_VARARGS 1
# OUT: 37 JUMP_FORWARD 1 (to 41)
# OUT: >> 40 END_FINALLY
# OUT: >> 41 LOAD_CONST 0 (None)
# OUT: 44 RETURN_VALUE
Answers:
There is no difference in this case. raise
without arguments will always raise the last exception thrown (which is also accessible with sys.exc_info()
).
The reason the bytecode is different is because Python is a dynamic language and the interpreter doesn’t really “know” that e
refers to the (unmodified) exception that is currently being handled. But this may not always be the case, consider:
try:
raise Exception()
except Exception as e:
if foo():
e = OtherException()
raise e
What is e
now? There is no way to tell when compiling the bytecode (only when actually running the program).
In simple examples like yours, it might be possible for the Python interpreter to “optimize” the bytecode, but so far no one has done this. And why should they? It’s a micro-optimization at best and may still break in subtle ways in obscure conditions. There is a lot of other fruit that is hanging a lot lower than this and is more nutritious to boot 😉
It is possible to clear the “last exception” (i.e. the result of sys.exc_info()
) info with sys.exc_clear()
. For instance, this would happen if the catch block called a function foo()
, which itself has special error handling.
In that case, raise
with and without an argument would mean diffent things. raise e
would still have a reference to the exception caught a few lines above, while the raise
shorthand would try to raise None
, which is an error.
There is a difference in the backtraces that the two forms generate. The difference is different in Python 2 and 3.
Using raise
:
try:
int("hello")
except ValueError as e:
raise
The code gives the following backtrace in Python 2 or Python 3:
Traceback (most recent call last):
File "myfile.py", line 2, in <module>
int("hello")
ValueError: invalid literal for int() with base 10: 'hello'
Using raise e
:
try:
int("hello")
except ValueError as e:
raise e
The code gives the following backtrace in Python 2:
Traceback (most recent call last):
File "myfile.py", line 4, in <module>
raise e
ValueError: invalid literal for int() with base 10: 'hello'
The code gives the following backtrace in Python 3:
Traceback (most recent call last):
File "myfile.py", line 4, in <module>
raise e
File "myfile.py", line 2, in <module>
int("hello")
ValueError: invalid literal for int() with base 10: 'hello'
In Python 2:
In the raise
case, the original source of the exception is quoted in the backtrace. In the raise e
case, the traceback references the raise e
line not the original cause. Therefore, I recommend always using raise
rather than raise e
.
In Python 3:
The original source of the exception is always quoted. The difference is whether the raise
/raise e
line is also quoted. It depends on one’s taste and the context of the raise
.
In python, is there a difference between raise
and raise e
in an except block?
dis
is showing me different results, but I don’t know what it means.
What’s the end behavior of both?
import dis
def a():
try:
raise Exception()
except Exception as e:
raise
def b():
try:
raise Exception()
except Exception as e:
raise e
dis.dis(a)
# OUT: 4 0 SETUP_EXCEPT 13 (to 16)
# OUT: 5 3 LOAD_GLOBAL 0 (Exception)
# OUT: 6 CALL_FUNCTION 0
# OUT: 9 RAISE_VARARGS 1
# OUT: 12 POP_BLOCK
# OUT: 13 JUMP_FORWARD 22 (to 38)
# OUT: 6 >> 16 DUP_TOP
# OUT: 17 LOAD_GLOBAL 0 (Exception)
# OUT: 20 COMPARE_OP 10 (exception match)
# OUT: 23 POP_JUMP_IF_FALSE 37
# OUT: 26 POP_TOP
# OUT: 27 STORE_FAST 0 (e)
# OUT: 30 POP_TOP
# OUT: 7 31 RAISE_VARARGS 0
# OUT: 34 JUMP_FORWARD 1 (to 38)
# OUT: >> 37 END_FINALLY
# OUT: >> 38 LOAD_CONST 0 (None)
# OUT: 41 RETURN_VALUE
dis.dis(b)
# OUT: 4 0 SETUP_EXCEPT 13 (to 16)
# OUT: 5 3 LOAD_GLOBAL 0 (Exception)
# OUT: 6 CALL_FUNCTION 0
# OUT: 9 RAISE_VARARGS 1
# OUT: 12 POP_BLOCK
# OUT: 13 JUMP_FORWARD 25 (to 41)
# OUT: 6 >> 16 DUP_TOP
# OUT: 17 LOAD_GLOBAL 0 (Exception)
# OUT: 20 COMPARE_OP 10 (exception match)
# OUT: 23 POP_JUMP_IF_FALSE 40
# OUT: 26 POP_TOP
# OUT: 27 STORE_FAST 0 (e)
# OUT: 30 POP_TOP
# OUT: 7 31 LOAD_FAST 0 (e)
# OUT: 34 RAISE_VARARGS 1
# OUT: 37 JUMP_FORWARD 1 (to 41)
# OUT: >> 40 END_FINALLY
# OUT: >> 41 LOAD_CONST 0 (None)
# OUT: 44 RETURN_VALUE
There is no difference in this case. raise
without arguments will always raise the last exception thrown (which is also accessible with sys.exc_info()
).
The reason the bytecode is different is because Python is a dynamic language and the interpreter doesn’t really “know” that e
refers to the (unmodified) exception that is currently being handled. But this may not always be the case, consider:
try:
raise Exception()
except Exception as e:
if foo():
e = OtherException()
raise e
What is e
now? There is no way to tell when compiling the bytecode (only when actually running the program).
In simple examples like yours, it might be possible for the Python interpreter to “optimize” the bytecode, but so far no one has done this. And why should they? It’s a micro-optimization at best and may still break in subtle ways in obscure conditions. There is a lot of other fruit that is hanging a lot lower than this and is more nutritious to boot 😉
It is possible to clear the “last exception” (i.e. the result of sys.exc_info()
) info with sys.exc_clear()
. For instance, this would happen if the catch block called a function foo()
, which itself has special error handling.
In that case, raise
with and without an argument would mean diffent things. raise e
would still have a reference to the exception caught a few lines above, while the raise
shorthand would try to raise None
, which is an error.
There is a difference in the backtraces that the two forms generate. The difference is different in Python 2 and 3.
Using raise
:
try:
int("hello")
except ValueError as e:
raise
The code gives the following backtrace in Python 2 or Python 3:
Traceback (most recent call last):
File "myfile.py", line 2, in <module>
int("hello")
ValueError: invalid literal for int() with base 10: 'hello'
Using raise e
:
try:
int("hello")
except ValueError as e:
raise e
The code gives the following backtrace in Python 2:
Traceback (most recent call last):
File "myfile.py", line 4, in <module>
raise e
ValueError: invalid literal for int() with base 10: 'hello'
The code gives the following backtrace in Python 3:
Traceback (most recent call last):
File "myfile.py", line 4, in <module>
raise e
File "myfile.py", line 2, in <module>
int("hello")
ValueError: invalid literal for int() with base 10: 'hello'
In Python 2:
In the raise
case, the original source of the exception is quoted in the backtrace. In the raise e
case, the traceback references the raise e
line not the original cause. Therefore, I recommend always using raise
rather than raise e
.
In Python 3:
The original source of the exception is always quoted. The difference is whether the raise
/raise e
line is also quoted. It depends on one’s taste and the context of the raise
.