如何创建可根据成员类属性自动新增聚合方法的Python集合类
方案1:用__getattr__动态生成聚合属性(零维护成本,最省心)
这个方案不需要手动新增任何Team类的代码,只要Player新增property,Team会自动支持对应4种聚合属性:
from statistics import mean, median from functools import lru_cache class Player: def __init__(self, name, age, score=None): self.name = name self._age = age self._score = score @property def age(self): return self._age # 新增property完全不用改Team代码 @property def score(self): return self._score # 提前收集所有公开的property名称,用于合法性校验 @classmethod def get_properties(cls): return [k for k, v in vars(cls).items() if isinstance(v, property)] class Team: def __init__(self, players): self.players = players self._player_properties = set(Player.get_properties()) # 定义聚合规则:前缀 -> 计算函数 self._agg_rules = { 'all_': lambda values: {p.name: v for p, v in zip(self.players, values)}, 'total_': sum, 'average_': mean, 'median_': median } def __getattr__(self, attr_name): # 匹配前缀和属性名 for prefix, agg_func in self._agg_rules.items(): if attr_name.startswith(prefix): prop_name = attr_name[len(prefix):] # 处理单复数匹配,比如all_ages对应age,all_scores对应score if prop_name.endswith('s'): prop_name = prop_name[:-1] if prop_name in self._player_properties: # 提取所有玩家对应的属性值 values = [getattr(p, prop_name) for p in self.players] return agg_func(values) # 没有匹配的属性走默认报错逻辑 raise AttributeError(f"'Team' object has no attribute '{attr_name}'") # 测试代码 p1 = Player('John', 8, 90) p2 = Player('Tim', 9, 85) p3 = Player('Annie', 11, 95) team = Team([p1, p2, p3]) # 原有age属性正常使用 print(team.all_ages) # {'John':8, 'Tim':9, 'Annie':11} print(team.total_age) # 28 # 新增的score属性自动支持 print(team.all_scores) # {'John':90, 'Tim':85, 'Annie':95} print(team.average_score) # 90
这个方案的优势是完全适配Player属性的新增/修改/删除,不需要对Team类做任何调整,适合属性频繁迭代的业务场景。如果担心重复计算性能问题,可以给__getattr__加缓存,或者在第一次计算后把属性动态绑定到实例上。
方案2:预生成property(支持IDE自动补全,对开发者更友好)
如果需要IDE能识别所有聚合属性,可以用类装饰器在定义Team类的时候自动批量生成所有property:
def generate_agg_properties(player_cls): agg_rules = { 'all_': lambda self, prop: {p.name: getattr(p, prop) for p in self.players}, 'total_': lambda self, prop: sum(getattr(p, prop) for p in self.players), 'average_': lambda self, prop: mean(getattr(p, prop) for p in self.players), 'median_': lambda self, prop: median(getattr(p, prop) for p in self.players), } def decorator(team_cls): # 获取所有Player的property player_props = [k for k, v in vars(player_cls).items() if isinstance(v, property)] for prop in player_props: for prefix, func in agg_rules.items(): # 生成属性名,all_前缀加复数s attr_name = f"{prefix}{prop}s" if prefix == 'all_' else f"{prefix}{prop}" # 绑定property setattr(team_cls, attr_name, property(lambda self, p=prop, f=func: f(self, p))) return team_cls return decorator # 使用装饰器修饰Team类 @generate_agg_properties(Player) class Team: def __init__(self, players): self.players = players
后续只要Player新增了property,重新加载代码后Team就会自动生成对应的聚合属性,IDE可以正常提示补全。
内容的提问来源于stack exchange,提问作者j_becker
相关产品推荐
相关产品推荐

