Python 如何向动态创建的类添加类型注释?
在一个应用程序中,我有生成动态类的代码,这大大减少了重复代码的数量。但是为mypy检查添加类型提示会导致错误。考虑下面的示例代码(简化为关注相关位): 使用mypy检查此选项将导致以下错误:Python 如何向动态创建的类添加类型注释?,python,mypy,Python,Mypy,在一个应用程序中,我有生成动态类的代码,这大大减少了重复代码的数量。但是为mypy检查添加类型提示会导致错误。考虑下面的示例代码(简化为关注相关位): 使用mypy检查此选项将导致以下错误: dynamic_type.py:15: error: "type" has no attribute "action" dynamic_type.py:21: error: "type" has no attribute "action" mypy显然无法判断type调用的返回值是否是Mapper的子类,
dynamic_type.py:15: error: "type" has no attribute "action"
dynamic_type.py:21: error: "type" has no attribute "action"
mypy显然无法判断type
调用的返回值是否是Mapper
的子类,因此它抱怨在分配给“type”时,“type”没有属性“action”
请注意,代码功能完美,并完成了它应该做的事情,但mypy仍在抱怨
有没有办法将cls
标记为映射器的一种类型?我试图简单地将#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"
一种可能的解决方案基本上是:
键入带有预期输入和输出类型的magic
函数
明智地使用Any
和#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(和pep484类型生态系统)故意不尝试像这样处理超级动态代码
相反,您能做的最好的事情是清晰地记录“静态”接口,添加单元测试,并将代码的动态部分限制在尽可能小的区域内。我认为这是必须的;)实际上,“magic”函数的定义应该具有返回类型typing.type[Mapper]
。这样就可以在返回
行中删除类型: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()