Python组合@classmethod与@property调用返回绑定方法而非预期值
问题解答
成因
- 装饰器组合使用逻辑错误:
@classmethod与@property直接叠加会破坏property的描述符逻辑,装饰后的方法不会自动执行取值逻辑,访问时只会返回类绑定方法对象,就是你看到的<bound method ...>输出。 - 数据描述符优先级问题:
@property装饰后的对象属于数据描述符,优先级高于普通类属性,即使子类定义了同名的name、version类属性,访问时也会优先触发父类的描述符逻辑,不会直接读取子类的属性值。
解决方案
方案1:自定义兼容全版本的类属性装饰器(最通用)
自行实现一个classproperty非数据描述符,完美支持类/实例访问属性的需求,兼容所有Python版本:
# 自定义类属性装饰器 class classproperty: def __init__(self, func): self.func = func def __get__(self, instance, owner): return self.func(owner) from abc import ABC class Preprocessor(ABC): @classproperty def name(cls): raise NotImplementedError @classproperty def version(cls): raise NotImplementedError @classproperty def feature_group_name(cls): return f'{cls.name}_{cls.version}' class Feature(Preprocessor): name = 'testFeature' version = 'v1' # 类、实例访问都正常 print(Feature.feature_group_name) # 输出 'testFeature_v1' print(Feature().feature_group_name) # 输出 'testFeature_v1'
方案2:使用实例属性(无需自定义装饰器)
如果你仅需要通过实例访问feature_group_name,可以直接去掉父类属性的@classmethod装饰,用普通实例property实现,逻辑更简单:
from abc import ABC class Preprocessor(ABC): name: str version: str @property def feature_group_name(self): return f'{self.name}_{self.version}' class Feature(Preprocessor): name = 'testFeature' version = 'v1' print(Feature().feature_group_name) # 输出 'testFeature_v1'
方案3:Python 3.11+ 原生写法
Python 3.11版本开始官方支持@classmethod与@property的叠加使用,直接调整抽象类定义即可:
from abc import ABC, abstractmethod class Preprocessor(ABC): @classmethod @property @abstractmethod def name(cls): ... @classmethod @property @abstractmethod def version(cls): ... @classmethod @property def feature_group_name(cls): return f'{cls.name}_{cls.version}' class Feature(Preprocessor): name = 'testFeature' version = 'v1' print(Feature().feature_group_name) # 输出 'testFeature_v1'
内容的提问来源于stack exchange,提问作者Leandro Acosta
相关产品推荐
相关产品推荐

