是字符串代表类型的实例

问题描述 投票:0回答:3
isinstance("my string", "str | int")
isinstance("my string", "list")

有没有办法根据存储为字符串的有效类型来检查变量的类型(在本例中为

"my string"
)?

我不需要导入类型,我知道它是内置的(int / str / float / etc)。

天真的方法是调用

eval()
,但这当然是不安全的。除了使用正则表达式/字符串拆分来提取单个类型并将它们与巨大的 if/else 块中的评估类型匹配之外,还有其他安全的方法吗?

python eval isinstance
3个回答
3
投票

对于内置类型,您可以在

builtins
模块中进行查找:

>>> import builtins
>>> getattr(builtins, 'str')
<class 'str'>

对于您在自己的代码中定义的类,有

globals()
字典:

>>> class Foo:
...     pass
...
>>> globals()['Foo']
<class '__main__.Foo'>

请注意,这不处理联合或通用类型。


0
投票

这里有一个相当复杂的方法来避免使用

eval
(直接;见下文)并使用现有的机制来评估类型提示。

def foo(x):
    pass

foo.__annotations__['x'] = "str|int"
assert typing.get_type_hints(foo)['x'] == (str|int)

最终,

typing.ForwardRef
本身使用
eval
"str|int"
评估为
str|int
,但要么

  1. 有防止评估任意表达式的保护措施,这比直接使用
    eval
  2. 更安全
  3. 没有安全措施,但您将受益于
    typing
    模块的任何未来更改。

0
投票

fast_eval

对于 Python 3.6+

我建议使用下面定义的

fast_eval
声明,这是可以满足上述问题的最快实现方式。

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

例子:

>>> fast_eval("hey", "int | str")
True
>>> fast_eval("hey", "int | bool")
False

性能

研究表明,它比最直接的实施快10x

注意:以下仅适用于

Python 3.10+

isinstance("hey", eval("int | str"))
基准代码:

# 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: return t is getattr(builtins, annotation.strip()) except AttributeError: return t is __globals.get(annotation, None) # asserts assert fast_eval("hey", "int | datetime | str") is eval_with_type("hey", "int | datetime | str", globals()) is True assert fast_eval("hey", "int | datetime | bool") is eval_with_type("hey", "int | datetime | bool", globals()) is False # 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()))
我的 Mac OS 上的结果:

fast_eval: 0.29372966598020867 eval_with_type: 1.2097240000148304 eval: 3.5093690829817206
    
© www.soinside.com 2019 - 2024. All rights reserved.