Python中如何实现自动为列表属性生成均值和标准差属性的装饰器
实现方案
你可以通过「自定义property标记类+元类」的方式实现需求,该方案会将自动生成的mean、std属性真实注入到类中,支持IDE自动补全,对原有代码侵入极小。
前置修正
首先修正你原有代码的小问题:calc_mean、calc_std方法需要接收待计算的列表作为入参,原定义缺少参数:
# 修正后的方法定义 def calc_mean(self, data: list[float]) -> float: # 你的均值计算逻辑 pass def calc_std(self, data: list[float]) -> float: # 你的标准差计算逻辑 pass
完整实现代码
import statistics # 自定义标记用property子类,不修改原生property的行为 class stat_property(property): pass # 元类:类创建时自动扫描stat_property属性,注入对应的mean、std属性 class StatMeta(type): def __new__(cls, name, bases, attrs): # 收集所有被stat_property装饰的属性名 stat_prop_names = [ attr_name for attr_name, attr_val in attrs.items() if isinstance(attr_val, stat_property) ] # 为每个统计属性生成对应的mean、std只读property for prop_name in stat_prop_names: # 用默认参数prop=prop_name解决闭包延迟绑定问题 def mean_getter(self, prop=prop_name): return self.calc_mean(getattr(self, prop)) attrs[f"{prop_name}_mean"] = property(mean_getter) def std_getter(self, prop=prop_name): return self.calc_std(getattr(self, prop)) attrs[f"{prop_name}_std"] = property(std_getter) return super().__new__(cls, name, bases, attrs) # 你的Data类仅需要指定metaclass=StatMeta即可 class Data(metaclass=StatMeta): def __init__(self): self._list_a = [1.0, 2.0, 3.0, 4.0, 5.0] self._list_b = [2.0, 4.0, 6.0, 8.0, 10.0] def calc_mean(self, data: list[float]) -> float: return statistics.mean(data) def calc_std(self, data: list[float]) -> float: return statistics.stdev(data) if len(data) >= 2 else 0.0 @stat_property def list_a(self) -> list[float]: return self._list_a @stat_property def list_b(self) -> list[float]: return self._list_b
使用效果测试
d = Data() print(d.list_a) # 输出 [1.0, 2.0, 3.0, 4.0, 5.0] print(d.list_a_mean) # 输出 3.0 print(d.list_a_std) # 输出 ~1.5811 print(d.list_b_mean) # 输出 6.0 print(d.list_b_std) # 输出 ~3.1623
轻量无元类实现(可选)
如果不想修改类的元类,可以用__getattr__的方式实现,无需改动类的继承结构,缺点是自动生成的属性不会被IDE自动补全:
import statistics class Data: # 存储所有被stat_property装饰的属性名 _stat_props = set() @classmethod def stat_property(cls, func): """自定义装饰器,标记属性同时收集名称""" prop = property(func) cls._stat_props.add(func.__name__) return prop def __init__(self): self._list_a = [1.0, 2.0, 3.0, 4.0, 5.0] self._list_b = [2.0, 4.0, 6.0, 8.0, 10.0] def calc_mean(self, data: list[float]) -> float: return statistics.mean(data) def calc_std(self, data: list[float]) -> float: return statistics.stdev(data) if len(data) >= 2 else 0.0 def __getattr__(self, name: str): """访问不存在的属性时自动匹配mean、std规则""" if name.endswith("_mean"): base_prop = name[:-5] if base_prop in self._stat_props: return self.calc_mean(getattr(self, base_prop)) elif name.endswith("_std"): base_prop = name[:-4] if base_prop in self._stat_props: return self.calc_std(getattr(self, base_prop)) raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") @stat_property def list_a(self) -> list[float]: return self._list_a @stat_property def list_b(self) -> list[float]: return self._list_b
内容的提问来源于stack exchange,提问作者user107511
相关产品推荐
相关产品推荐

