fast_eval
For Python 3.6+
I would suggest to use the following declaration of fast_eval
as defined below, which is the fastest possible implementation which can satisfy the ask in the question above.
def fast_eval(val: Any, annotation: str) -> bool:
cls_name = val.__class__.__name__
if '|' in annotation:
for tp in annotation.split('|'):
if tp.strip() == cls_name:
return True
return False
return annotation.strip() == cls_name
Examples:
>>> fast_eval("hey", "int | str")
True
>>> fast_eval("hey", "int | bool")
False
>>> fast_eval("world", "int | list | datetime | BLAH")
False
>>> fast_eval(["world"], "int | list | datetime | BLAH")
True
Performance
Studies show, that it's more than 10x faster than the most straightforward implementation of:
Note: The following works in Python 3.10+ only!
isinstance("hey", eval("int | str"))
Benchmark code:
# Requires: Python 3.10+
import builtins
from datetime import datetime
from timeit import timeit
from typing import Any
def fast_eval(val: Any, annotation: str) -> bool:
cls_name = val.__class__.__name__
if '|' in annotation:
for tp in annotation.split('|'):
if tp.strip() == cls_name:
return True
return False
return annotation.strip() == cls_name
def eval_with_type(val: Any, annotation: str, __globals=None, t=None) -> bool:
if __globals is None:
__globals = {}
if t is None:
t = type(val)
if '|' in annotation:
for tp in annotation.split('|'):
if eval_with_type(val, tp, __globals, t):
return True
return False
annotation = annotation.strip()
try: # is a BUILTIN? like (str, bool)
return t is getattr(builtins, annotation)
except AttributeError:
# NO, sir! instead, like (datetime, UUID) or user-defined class
try:
return t is __globals[annotation]
except KeyError as k:
raise TypeError(f'no such type is defined (in globals): {k}')
# asserts
assert True is fast_eval("hey", "int | datetime | str") is eval_with_type("hey", "int | datetime | str", globals())
assert False is fast_eval("hey", "int | datetime | bool") is eval_with_type("hey", "int | datetime | bool", globals())
# timings
print('fast_eval: ', timeit('fast_eval("hey", "int | datetime | str")', globals=globals()))
print('eval_with_type: ', timeit('eval_with_type("hey", "int | datetime | str", globals())', globals=globals()))
print('eval: ', timeit('isinstance("hey", eval("int | datetime | str", globals()))', globals=globals()))
Results on my Mac OS:
fast_eval: 0.3017798329819925
eval_with_type: 1.1461862919968553
eval: 3.5175461250473745