Python混合abstractmethod、classmethod与property装饰器的异常行为问询
核心原理说明
为什么Bar.x会被意外调用
这一现象是ABC抽象类的校验逻辑和装饰器叠加后的属性访问逻辑共同导致的:
- 装饰器的执行顺序是从下到上,你代码里
Foo.x的包装顺序是:原始方法 →@abstractmethod包装 →@property包装为描述符 →@classmethod修改为类级别的属性描述符。最终Foo.x是一个绑定到类的getter属性,只要访问类的.x属性,就会自动触发getter执行。 - ABC元类在构造子类
Bar的时候,会自动校验所有继承自父类的抽象方法是否被正确覆盖。校验过程中需要读取Bar.x的属性值,判断其是否覆盖了父类的抽象成员,这一读取操作直接触发了@property对应的getter逻辑,就输出了你看到的两行打印内容。 - 如果调换
@classmethod和@property的顺序,最终得到的Bar.x是普通的实例property对象,ABC读取该属性时拿到的是property对象本身,不会触发getter执行,因此不会出现打印输出。 - 如果你移除三个装饰器中的任意一个,要么
x不再是property类型(不会触发自动调用),要么不再是抽象类成员(ABC不会主动读取校验),自然就不会出现意外调用的情况。
警告出现的原因
两类警告都是PyCharm的静态类型检查识别到装饰器混用不符合内置装饰器的设计预期导致的:
Property 'self' cannot be deleted警告:
内置@property的设计默认是绑定实例的,其内部逻辑预期接收的第一个参数是实例对象self。当你和@classmethod混用后,实际传入getter的是类对象cls,静态检查无法匹配这种非常规用法,误以为你在非法操作实例的self属性,就抛出了该警告。This decorator will not receive a callable it may expect警告:
当你调换装饰器顺序将@property放在@classmethod上方时,先被@classmethod包装后的对象是classmethod类型的特殊对象,不是普通可调用的函数,而@property装饰器预期接收的入参是普通的实例方法,入参类型不匹配,因此触发该警告。
补充说明:Python 3.11版本才正式支持
@classmethod和@property的直接联用,更低版本中二者混用本身就属于未定义行为,出现异常表现和警告属于预期内的情况。
内容的提问来源于stack exchange,提问作者tomasz
相关产品推荐
相关产品推荐

