在一个应用程序中,我有生成动态类的代码,这大大减少了重复代码的数量。但是为 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()
Run Code Online (Sandbox Code Playgroud)
使用 mypy 进行检查将导致以下错误:
dynamic_type.py:15: error: "type" has no attribute "action"
dynamic_type.py:21: error: "type" has no attribute "action"
Run Code Online (Sandbox Code Playgroud)
mypy 显然无法判断type调用的返回值是 的子类Mapper,因此它抱怨“type”在我分配给它时没有属性“action”。
请注意,代码功能完美并完成了它应该做的事情,但 mypy 仍然抱怨。
有没有办法标记cls为一种类型Mapper?我试图简单地附加# type: Mapper到创建类的行:
cls = type('%sMapper' % new_name.capitalize(), (Mapper,), {}) # type: Mapper
Run Code Online (Sandbox Code Playgroud)
但后来我收到以下错误:
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"
Run Code Online (Sandbox Code Playgroud)
一种可能的解决方案基本上是:
magic使用预期的输入和输出类型键入您的函数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()
Run Code Online (Sandbox Code Playgroud)
保留一部分代码库动态类型似乎有点令人反感,但在这种情况下,我认为没有什么可以避免的:mypy(和 PEP 484 类型生态系统)故意不尝试处理像这样的超级动态代码这个。
相反,您能做的最好的事情就是清楚地记录“静态”接口,添加单元测试,并将代码的动态部分限制在尽可能小的区域内。
| 归档时间: |
|
| 查看次数: |
2995 次 |
| 最近记录: |