apostofes
apostofes

Reputation: 3693

any specific reason to specify the type of Exception?

%%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

Answers (1)

OTheDev
OTheDev

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

Related Questions