我有一个类(Team
),它是另一个类(Player
)的集合。在最小的示例中,我只有一个property
对应Player
,但是我的真实世界代码有几十个更复杂的属性,我经常添加更多的属性。
目前每次我添加一个新的property
到Player
,我需要手动添加四个新的属性到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=}')