如何动态定义“typing.Union”?

s-m*_*m-e 20 python type-hinting dynamic-typing python-typing

我在几个项目中使用Typeguard在 Python 运行时进行类型检查。它运作得很好。

我遇到过一种情况,函数参数的类型是typing.Union由一些动态收集的数据类型组成的。例如

def find_datatypes():
    # some stuff ...
    return (str, int) # dynamically generated list / tuple

datatypes = find_datatypes()
Run Code Online (Sandbox Code Playgroud)

现在我想生成一个typing.Unionfromdatatypes以便最终在函数中使用。我期望解包语法能够工作:

my_union = typing.Union[*datatypes]

@typeguard.typechecked
def some_function(param: my_union):
    pass
Run Code Online (Sandbox Code Playgroud)

然而,它并没有:

    my_union = typing.Union[*datatypes]
                            ^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)

我怎样才能实现我想要的目标?

use*_*ica 17

你可以这样做:

my_union = typing.Union[datatypes]
Run Code Online (Sandbox Code Playgroud)

在运行时,thing[x, y]已经相当于thing[(x, y)].

也就是说,有一些限制需要记住。特别是,当使用字符串注释时,my_union必须在some_function的全局命名空间typeguard或其他任何东西中可用,才能在运行时解析注释。这限制了很多闭包用例,以及很多动态添加注释的尝试。(字符串注释最终可能会成为默认值,但开发人员正在考虑其他选项,目前计划尚不清楚。)

另外,正如您所期望的,mypy 不会认为这些都是有效的。

  • @chepner:我很确定 PEP 563 曾经说过 4.0。关于 [tracker](https://bugs.python.org/issue38605) 的讨论看起来他们正在努力在 3.10 之前将其加入。 (2认同)

Tom*_*cik 5

我正在使用 pydantic 自动生成 OpenAPI 规范。

import typing

from pydantic import BaseModel


class Parent(BaseModel):
    @classmethod
    def get_subclasses(cls):
        return tuple(cls.__subclasses__())


class Child1(Parent):
    pass


class Child2(Parent):
    pass


datatypes = typing.Union[Parent.get_subclasses()]
Run Code Online (Sandbox Code Playgroud)

它之所以有效只是因为get_subclasses返回一个元组。我发布这个答案是因为__subclasses__调用返回一个列表,我挠了挠头,因为我无法理解为什么 @user2357112 解决方案在我的情况下不起作用。事实上,确实如此,但我没有注意到他们使用了元组。

否则,你会遇到

TypeError: Union[arg, ...]: each arg must be a type. Got [<class '__main__.Child1'>, <class '__main__.Child2'>].
Run Code Online (Sandbox Code Playgroud)