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

时间:2018-01-11 12:51:54

标签: python mypy

在一个应用程序中,我有代码生成动态类,可以显着减少重复代码的数量。但是为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调用的返回值是Mapper的子类,所以它抱怨"键入"没有属性"动作"当我分配给它时。

请注意,代码功能完美且完成了预期,但mypy仍然抱怨。

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

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 个答案:

答案 0 :(得分:3)

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

  1. 使用预期的输入和输出类型
  2. 键入magic函数
  3. 谨慎使用magicAny
  4. ,动态输入# type: ignore函数的内容

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

    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打字生态系统)故意做不要尝试像这样处理超级动态代码。

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