Reputation: 3693
%%time
for i in range(10000000):
try:
x = (type.__abstractmethods__)
except:
y, z = 1, 2
CPU times: user 3.59 s, sys: 0 ns, total: 3.59 s
Wall time: 3.6 s
%%time
for i in range(10000000):
try:
x = (type.__abstractmethods__)
except AttributeError as e:
y, z = 1, 2
CPU times: user 5.74 s, sys: 0 ns, total: 5.74 s
Wall time: 5.86 s
on one more example,
%%time
for i in range(10000000):
try:
print(1/0)
except:
y, z = 1, 2
CPU times: user 4.22 s, sys: 1.72 ms, total: 4.22 s
Wall time: 4.23 s
%%time
for i in range(10000000):
try:
print(1/0)
except ZeroDivisionError:
y, z = 1, 2
CPU times: user 5.14 s, sys: 4.88 ms, total: 5.14 s
Wall time: 5.16 s
clearly not specifying the exception type gives an improvement in time, then why would I ever want to specify the exception type (assume I am not using the exception type in the except block)?
Upvotes: 1
Views: 135
Reputation: 2967
Consider Snippet 1 and 2 and the associated timings (below). The reason Snippet 2 is slower is because Snippet 1 has an expression-less except
clause whereas Snippet 2's except
clause does have an expression. If an except
clause has an expression, it needs to be evaluated and tested against the raised exception. An expression-less except
clause does not have to do this additional step. From the documentation:
An expression-less except clause, if present, must be last; it matches any exception. For an except clause with an expression, that expression is evaluated, and the clause matches the exception if the resulting object is “compatible” with the exception.
In Snippet 1, when an AttributeError
is raised, the only except
clause is this expression-less except
clause which matches any exception and so it is entered immediately. In Snippet 2, when an AttributeError
is raised, the only except
clause is one with the expression AttributeError
. The expression after the except
, AttributeError
, has to be evaluated and tested against the raised exception. This does not happen in Snippet 1. Consequently, Snippet 1 will be faster than Snippet 2.
why would I ever want to specify the exception type (assume I am not using the exception type in the except block)?
The try
suite (the block of code under the try
) may raise different types of exceptions. Depending on the type of exception, you may want to handle it differently. If you specify an expression-less except
clause, if that except
clause is reached, it will be entered regardless of the type of the exception.
If you know you want to handle every exception the same way, you can choose not to specify exception types. Some try to handle any exception by using except BaseException
(or handle a subset of these exceptions using except Exception
), but specifying BaseException
and Exception
yields virtually identical timings to Snippet 2 below (see, for example, Snippet 3) for the simple reason that BaseException
and Exception
also need to be evaluated and tested against the raised exception.
Here is a toy example that prints a different message to the user depending on the type of the exception that was raised.
while True:
try:
x = int(input("Enter x: "))
y = int(input("Enter y: "))
z = x / y
except ZeroDivisionError:
print("Undefined. Zero division error.")
continue
except ValueError:
print("Please enter an integer!")
continue
print(f"x / y = {z}")
break
Snippet 1
try:
# generates AttributeError
x = type.__abstractmethods__
except:
y, z = 1, 2
Snippet 2
try:
# generates AttributeError
x = type.__abstractmethods__
except AttributeError:
y, z = 1, 2
Snippet 3 (bonus)
try:
# generates AttributeError
x = type.__abstractmethods__
except BaseException:
y, z = 1, 2
Timings
# snippet 1
221 ns ± 0.782 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
# snippet 2
242 ns ± 0.905 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
# snippet 3
241 ns ± 1.23 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
Upvotes: 1