如何创建一个基于继承类自动添加新的聚合方法的集合类?



我有一个类(Team),它是另一个类(Player)的集合。在最小的示例中,我只有一个property对应Player,但是我的真实世界代码有几十个更复杂的属性,我经常添加更多的属性。

目前每次我添加一个新的propertyPlayer,我需要手动添加四个新的属性到Team,但我知道必须有一个更好的方法来做到这一点。我想有Team类自动添加这四个属性,每次我在Player中创建一个新的property,大大减少了我需要编写的代码,但我不确定最好的方法去做这个。

from statistics import mean, median

class Player:
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age

class Team:
def __init__(self, players):
self.players = players
@property
def all_ages(self):
return {player.name: player.age for player in self.players}
@property
def total_age(self):
return sum([player.age for player in self.players])
@property
def average_age(self):
return mean([player.age for player in self.players])

@property
def median_age(self):
return median([player.age for player in self.players])

p1 = Player('John', 8)        
p2 = Player('Tim', 9)        
p3 = Player('Annie', 11)        
team = Team([p1, p2, p3])

正如我在注释中所说的,在集合类和它所包含的对象的类之间不涉及继承。这意味着总是需要一定数量的乏味的重复编码来设置东西——至少如果你像下面所示的那样使用类装饰器——但是一旦设置好了,添加更多的属性应该相对容易。

from statistics import mean, median

def make_stat_properties(team_cls, player_cls, name):
storage_name = '_' + name
@property
def prop(self):
return getattr(self, storage_name)
@prop.setter
def prop(self, value):
setattr(self, storage_name, value)
setattr(team_cls, name, prop)
# Statistical methods.
@property
def all_func(self):
return {getattr(player, 'name'): getattr(player, name) for player in self.players}
func_name = f'all_{name}' if name.endswith('s') else f'all_{name}s'
setattr(player_cls, func_name, all_func)
@property
def total_func(self):
return sum(getattr(player, name) for player in self.players)
setattr(player_cls, f'total_{name}', total_func)
@property
def average_func(self):
return mean(getattr(player, name) for player in self.players)
setattr(player_cls, f'average_{name}', average_func)
@property
def median_func(self):
return median(getattr(player, name) for player in self.players)
setattr(player_cls, f'median_{name}', median_func)
return prop

class Team:
def __init__(self, players):
self.players = players

def stats_decorate(*names):
def stats_prop_decorator(cls):
""" Add stats properties to class. """
for name in names:
setattr(cls, name, make_stat_properties(cls, Team, name))
def __init__(self, name, *args):
self.name = name
for name, arg in zip(names, args):
setattr(self, f'_{name}', arg)
setattr(cls, '__init__', __init__)
return cls
return stats_prop_decorator

@stats_decorate('age', 'goals')  # Add properties and related stat methods to class.
class Player:
pass

if __name__ == '__main__':
p1 = Player('John', 8, 1)
p2 = Player('Tim', 9, 2)
p3 = Player('Annie', 11, 3)
team = Team([p1, p2, p3])
print(f'{team.all_ages=}')
print(f'{team.total_age=}')
print(f'{team.average_age=:.2f}')
print(f'{team.median_age=}')
print()
print(f'{team.all_goals=}')
print(f'{team.total_goals=}')
print(f'{team.average_goals=:.2f}')
print(f'{team.median_goals=}')

最新更新