如何将类型注释添加到动态创建的 类?

How can I add type-annotations to dynamically created classes?

在一个应用程序中,我有生成动态 classes 的代码,这大大减少了重复代码的数量。但是为 mypy 检查添加类型提示会导致错误。考虑以下示例代码(已简化以关注相关位):

class Mapper:

    @staticmethod
    def action() -> None:
        raise NotImplementedError('Not yet implemnented')


def magic(new_name: str) -> type:

    cls = type('%sMapper' % new_name.capitalize(), (Mapper,), {})

    def action() -> None:
        print('Hello')

    cls.action = staticmethod(action)
    return cls


MyCls = magic('My')
MyCls.action()

用 mypy 检查这个会导致以下错误:

dynamic_type.py:15: error: "type" has no attribute "action"
dynamic_type.py:21: error: "type" has no attribute "action"

mypy 显然无法判断 type 调用的 return 值是 Mapper 的子 class,因此它抱怨 "type" 当我分配给它时没有属性 "action"。

请注意,该代码运行完美并按预期运行,但 mypy 仍然抱怨。

有没有办法将 cls 标记为 Mapper 的一种类型?我试图简单地将 # type: Mapper 附加到创建 class:

的行
cls = type('%sMapper' % new_name.capitalize(), (Mapper,), {})  # type: Mapper

但随后出现以下错误:

dynamic_type.py:10: error: Incompatible types in assignment (expression has type "type", variable has type "Mapper")
dynamic_type.py:15: error: Cannot assign to a method
dynamic_type.py:15: error: Incompatible types in assignment (expression has type "staticmethod", variable has type "Callable[[], None]")
dynamic_type.py:16: error: Incompatible return value type (got "Mapper", expected "type")
dynamic_type.py:21: error: "type" has no attribute "action"

一个可能的解决方案基本上是:

  1. 使用预期的输入和输出类型键入您的 magic 函数
  2. 通过明智地使用 Any# type: ignore
  3. 动态输入 magic 函数的内容

例如,像这样的东西会起作用:

class Mapper:
    @staticmethod
    def action() -> None:
        raise NotImplementedError('Not yet implemnented')


def magic(new_name: str) -> Mapper:

    cls = type('%sMapper' % new_name.capitalize(), (Mapper,), {})

    def action() -> None:
        print('Hello')

    cls.action = staticmethod(action)  # type: ignore
    return cls  # type: ignore


MyCls = magic('My')
MyCls.action()

让你的代码库的一部分动态类型化似乎有点令人反感,但在这种情况下,我认为没有什么可以避免的:mypy(和 PEP 484 类型生态系统)故意不尝试处理像这样的超级动态代码。

相反,您能做的最好的事情就是清楚地记录 "static" 接口,添加单元测试,并将代码的动态部分限制在尽可能小的区域内。