使用mypy检查代码时赋值类方法引用报错的原因与疑问
mypy检查Python代码时的方法赋值报错问题
问题场景
类B是抽象类A的子类,必须实现抽象方法foo。由于foo的实际逻辑依赖B的初始化参数shouldUseG,因此在__init__中通过工厂方法_factory_to_generate_function为self.foo赋值,同时在类中声明了空的foo方法(第25行)。运行mypy检查时,第17行出现报错:
原代码
from typing import Callable from abc import ABCMeta, abstractmethod class A(metaclass = ABCMeta): def __init__(self): pass @abstractmethod def foo(self): raise NotImplementedError class B(A): def __init__(self, shouldUseG: bool): super().__init__() self.foo: Callable = self._factory_to_generate_function(shouldUseG) # line 17 def _factory_to_generate_function(self, shouldUseG: bool) -> Callable: read_data_function: Callable = {True: self.g, False: self.h}[shouldUseG] return read_data_function def foo(self): # line 25 pass def g(self): print('This is g.') def h(self): print('This is h.')
mypy报错信息
test.py:17: error: Cannot assign to a method [assignment] Found 1 error in 1 file (checked 1 source file)
删除第25行的foo方法声明后,报错消失,想明确两个问题:
- 该报错为何出现?
- 是否可以通过第17行的赋值方式实现抽象方法,无需再声明
foo方法?
问题解答
1. 报错原因
当你在类B中声明def foo(self): pass时,这个foo是类层面的方法属性。而在__init__中执行self.foo = ...,是给实例属性赋值,此时实例属性会隐藏类的同名方法。mypy会将这种“给已存在的类方法赋值实例属性”的行为判定为错误,因为它违背了Python中方法的定义逻辑(方法属于类,实例调用时自动绑定),所以抛出Cannot assign to a method的报错。
2. 可以通过赋值实现抽象方法,无需声明空方法
只需调整类型注解,让mypy识别foo是实例属性而非类方法即可,步骤如下:
- 删除第25行的空
foo方法声明 - 在类
B中添加foo: Callable的类型注解,明确告知mypy这是一个实例属性,类型为可调用对象
修改后的代码
from typing import Callable from abc import ABCMeta, abstractmethod class A(metaclass=ABCMeta): def __init__(self): pass @abstractmethod def foo(self): raise NotImplementedError class B(A): foo: Callable # 明确标注实例属性类型 def __init__(self, shouldUseG: bool): super().__init__() self.foo = self._factory_to_generate_function(shouldUseG) def _factory_to_generate_function(self, shouldUseG: bool) -> Callable: read_data_function: Callable = {True: self.g, False: self.h}[shouldUseG] return read_data_function def g(self): print('This is g.') def h(self): print('This is h.')
补充说明
- 若使用Python 3.10+,可以用
typing.InstanceVar更精准地标注实例属性:
这样mypy会更清晰地区分实例属性和类属性,避免混淆。from typing import Callable, InstanceVar # ... class B(A): foo: InstanceVar[Callable] - 修改后,mypy会认可这种实现方式,因为抽象类
A只要求实例拥有可调用的foo属性,不管它是类方法还是实例属性。
内容的提问来源于stack exchange,提问作者thomas_chang
相关产品推荐
相关产品推荐

