【问题标题】:How to create a collection class which automatically adds new aggregation methods based on inherited class?如何创建一个基于继承类自动添加新聚合方法的集合类?
【发布时间】:2021-12-03 21:59:47
【问题描述】:

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

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

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])

【问题讨论】:

  • 您的示例代码中不涉及继承——我不希望球员和他们所属的球队之间有任何继承。集合类通常不会从它们包含的对象继承任何东西(因为这样做会严重限制它们的用处)。听起来你在混淆“is-a”和“has-a”类关系。

标签: python class inheritance properties


【解决方案1】:

正如我在评论中所说,您的集合类与其包含的对象的类之间不涉及继承。这意味着设置事物总是需要一定数量的繁琐重复编码 - 至少如果你按照如下所示使用类装饰器进行操作 - 但是一旦设置好,添加更多属性应该相对容易。

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=}')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-30
    • 2017-06-13
    • 1970-01-01
    • 2016-01-13
    • 2010-12-08
    相关资源
    最近更新 更多