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.