python 如何在运行时根据Union类型检查变量?

w41d8nur  于 12个月前  发布在  Python
关注(0)|答案(5)|浏览(123)

我正在尝试编写一个函数装饰器,它使用Python 3.6的类型提示来检查参数字典是否尊重类型提示,如果没有提出一个明确描述问题的错误,将用于HTTP API。
问题是,当函数有一个使用Union类型的参数时,我不能在运行时检查变量。
例如,我有一个函数

from typing import Union
def bark(myname: str, descr: Union[int, str], mynum: int = 3) -> str:
    return descr + myname * mynum

字符串
我可以做到:

isinstance('Arnold', bark.__annotations__['myname'])


但不是:

isinstance(3, bark.__annotations__['descr'])


因为Union不能与isinstanceissubclass一起使用。
我找不到使用type对象检查它的方法,我试着自己实现检查,但是当bark.__annotations__['descr']在REPL中显示为typing.Union[int, str]时,我无法在运行时访问类型列表,如果不使用检查bark.__annotations__['descr'].__repr__()的丑陋技巧的话。
是否有适当的方法来访问这些信息?或者是否有意使其在运行时不容易访问?

lnvxswe2

lnvxswe21#

在Python 3.8及更高版本中,MSeifertRichard Xia建议的方法可以通过不使用未文档化的属性__origin____args__来改进。新函数typing.get_args(tp)typing.get_origin(tp)提供了此功能:

>> from typing import Union, get_origin, get_args
>> x = Union[int, str]
>> get_origin(x), get_args(x)
(typing.Union, (<class 'int'>, <class 'str'>))
>> get_origin(x) is Union
True
>> isinstance(3, get_args(x))
True
>> isinstance('a', get_args(x))
True
>> isinstance([], get_args(x))
False

字符串

i86rm4rw

i86rm4rw2#

您可以使用Union__args__属性,该属性包含“可能的内容:

>>> from typing import Union

>>> x = Union[int, str]
>>> x.__args__
(int, str)
>>> isinstance(3, x.__args__)
True
>>> isinstance('a', x.__args__)
True

字符串
__args__参数没有文档记录,所以它可能被认为是“扰乱了实现细节”,但它似乎是比解析repr更好的方法。

xxslljrj

xxslljrj3#

MSeifert(https://stackoverflow.com/a/45959000/7433423)现有的公认答案并没有将Union与其他泛型类型区分开来,并且由于isinstance()issubclass()在参数化Union类型上的行为,在运行时很难确定类型注解是Union还是其他泛型类型(如Mapping)。
似乎泛型类型将有一个未记录的__origin__属性,该属性将包含对用于创建它的原始泛型类型的引用。一旦确认类型注解是参数化的Union,您就可以使用同样未记录的__args__属性来获取类型参数。

>>> from typing import Union
>>> type_anno = Union[int, str]
>>> type_anno.__origin__ is Union
True
>>> isinstance(3, type_anno.__args__)
True
>>> isinstance('a', type_anno.__args__)
True

字符串

gdrx4gfi

gdrx4gfi4#

您可以使用typeguard模块,它可以与pip一起安装。它为您提供了一个函数check_argument_types或函数装饰器@typechecked。它应该为您执行运行时类型检查:https://github.com/agronholm/typeguard

from typing import Union
from typeguard import check_argument_types, typechecked

def check_and_do_stuff(a: Union[str, int]) -> None:
    check_argument_types() 
    # do stuff ...

@typechecked
def check_decorator(a: Union[str, int]) -> None:
    # do stuff ...

check_and_do_stuff("hello")
check_and_do_stuff(42)
check_and_do_stuff(3.14)  # raises TypeError

字符串
如果你想检查单个变量的类型,你可以直接使用typeguard的check_type函数:

from typing import Union
from typeguard import check_type

MyType = Union[str, int]

check_type("arg", "string", MyType, None)  # OK
check_type("arg", 42, MyType, None)  # OK
check_type("arg", 3.5, MyType, None)  # raises TypeError


本例中未使用"arg"None参数。请注意,check_type函数未作为此模块的公共函数记录,因此其API可能会发生更改。

z9smfwbn

z9smfwbn5#

我想联合不是一个类型本身,而是一个类型的描述。
但是我们可以简单地使用type(Union)来请求类型(在Python 2.7中也可以)

>>> from typing import Union
>>> type(Union)
typing.Union
>>> x = Union[int, str]
>>> isinstance(x, type(Union))
True

字符串

相关问题