Python try/except Can Be a Performant Way to Check for Type

Say, you want to do some math with a variable. Math implies that the variable in the operation is a numerical value (we’ll say in the typical case… don’t mean tweet me…). Before doing math operations, you might want to sanity check the variable so that your code will not blow up if the input is not numerical.

isinstance is a go-to for asking this generally. You just use it like isinstance(some_variable, Type_Or_Class) or to check a variable against multiple instance types you would just change the second argument to a tuple like isinstance(some_variable, (Type_Or_Class1, Type_Or_Class2)).

So, the usage would be as a pre-check to validation:

def validate(foobar):
    if not isinstance(foobar, (int, float)):
        raise ValueError("This input must be a number!")
    
    return foobar + 10

As a fun-fact: int works here since it’s in the method resolution order (mro) for the value.

type(123).__mro__
> (<class 'int'>, <class 'object'>)

An opposite way to look at this is to ask for forgiveness rather than permission.

def validate(foobar):
    try:
        return foobar + 10
    except:
        raise ValueError("This input must be a number!")

Whenever I stumble upon something like this, I like to break out timeit to compare.

import timeit


N_ITERATIONS = 10000000

setup = """
# code that sets stuff up, that won't be included in profiling
from datetime import datetime
unix_epoch = datetime.now().timestamp()
"""

# good 'ol isinstance
test = """
isinstance(unix_epoch, (int, float))
"""
print(timeit.timeit(setup=setup, stmt=test, number=N_ITERATIONS))

# try to add to a number
test = """
try:
    unix_epoch += 1
except TypeError:
    raise ValueError("This input must be a number!")
"""
print(timeit.timeit(setup=setup, stmt=test, number=N_ITERATIONS))

# just to be silly, also use divide
test = """
try:
    unix_epoch / 2
except TypeError:
    raise ValueError("This input must be a number!")
"""
print(timeit.timeit(setup=setup, stmt=test, number=N_ITERATIONS))

On a random EC2 instance I SSH’ed into:

1.5472514729946852
0.43879494071006775
0.43832130171358585

My 2.7 GHz Quad-Core Intel Core i7 MacBook Pro:

1.748314122
0.5496298039999998
0.5205081649999999

Funny enough, the silly thing i tried was actually the fastest. Since I was using this code in a REST API serializer that gets millions of hits a day, I went with faster.