如何将类型注释添加到动态创建的 类?
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"
一个可能的解决方案基本上是:
- 使用预期的输入和输出类型键入您的
magic
函数
- 通过明智地使用
Any
和 # type: ignore
动态输入 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" 接口,添加单元测试,并将代码的动态部分限制在尽可能小的区域内。
在一个应用程序中,我有生成动态 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"
一个可能的解决方案基本上是:
- 使用预期的输入和输出类型键入您的
magic
函数 - 通过明智地使用
Any
和# type: ignore
动态输入
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" 接口,添加单元测试,并将代码的动态部分限制在尽可能小的区域内。