You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建聚合其他对象的类的最Pythonic且面向对象的实现方式

AbstractPerson集合实现的Pythonic风格问题

背景与现有代码

我定义了抽象基类AbstractPerson,Bob、Alice等子类继承该类并实现interact方法,代码如下:

class AbstractPerson:
    def __init__(self, age=1):
        """抽象人物基类"""
        self.age = age

    def interact(self):
        raise NotImplementedError("未实现交互方法")

class Bob(AbstractPerson):
    def __init__(self, age=25):
        """Bob的实现"""
        super().__init__(age=age)

    def interact(self):
        return "Bob讲了个笑话。"
    
class Alice(AbstractPerson):
    def __init__(self, age=23):
        """Alice的实现"""
        super().__init__(age=age)

    def interact(self):
        return "Alice笑了起来。"

疑问点

现在需要创建一个能容纳任意数量AbstractPerson实例的集合对象,有几个问题:

  1. 使用列表、字典、元组还是自定义类更符合Pythonic风格?
  2. 面向对象框架下,自定义集合类的典型实现方式是什么?比如是否应该让FriendGroup继承AbstractPerson?
  3. 希望能便捷地使用集合内的实例,比如批量调用方法、遍历等。

我不关心实例的名称,只要求所有实例都继承自AbstractPerson并具备相同的方法与属性。

参考实现与期望用法

我之前尝试的参考实现如下(存在问题):

class FriendGroup:
    def __init__(self, **persons_dict):
        self.__dict__.update(persons_dict)

    def interact():
        """朋友们交互"""
        interaction = ""
        for key, value in self.__dict__.items():
            interaction += self.key.interact()
        return interaction

期望的使用方式是:

bob = Bob(age=40)
second_bob = Bob(age=25)
alice = Alice(age=35)
friends = FriendGroup(bob, alice, second_bob)

解答

1. 容器类型选择

  • 元组:不可变,适合不需要修改成员的场景,但无法动态添加/删除实例,灵活性不足。
  • 字典:适合需要按名称索引实例的场景,但你明确表示不关心实例名称,所以没必要额外维护键值对。
  • 列表:最基础的可变容器,能满足存储需求,但如果需要集合具备统一行为(比如批量调用interact),直接用列表需要每次手动遍历调用,代码复用性差。
  • 自定义类:最符合Pythonic风格,因为可以将容器的行为(比如批量交互、成员管理)封装起来,提供清晰的接口,让代码更易读、易维护。

2. 自定义集合类的典型实现

绝对不要让FriendGroup继承AbstractPerson——FriendGroup是容器,不是"人物",继承会违反里氏替换原则(比如FriendGroup不需要也不应该实现单个人物的interact逻辑,而是批量交互)。

典型实现方式是:

  • 内部维护一个存储AbstractPerson实例的容器(比如列表);
  • 实现初始化方法,支持传入任意数量的AbstractPerson实例;
  • 封装需要的批量操作方法(比如interact);
  • 可选实现迭代器协议,让集合可以像列表一样被遍历,方便便捷使用内部实例。

修正后的示例代码:

class FriendGroup:
    def __init__(self, *persons):
        # 校验所有传入实例都继承自AbstractPerson
        for person in persons:
            if not isinstance(person, AbstractPerson):
                raise TypeError(f"所有成员必须是AbstractPerson的实例,传入了{type(person)}")
        self.members = list(persons)

    def interact(self):
        """批量调用所有成员的interact方法,返回拼接后的交互内容"""
        return "\n".join(person.interact() for person in self.members)
    
    def add_member(self, person):
        """添加新成员"""
        if not isinstance(person, AbstractPerson):
            raise TypeError("成员必须是AbstractPerson的实例")
        self.members.append(person)
    
    def remove_member(self, person):
        """移除成员"""
        self.members.remove(person)
    
    # 实现迭代器协议,支持直接遍历FriendGroup实例
    def __iter__(self):
        return iter(self.members)
    
    # 支持通过索引访问成员
    def __getitem__(self, index):
        return self.members[index]

3. 便捷使用示例

使用修正后的FriendGroup,可以轻松完成各种操作:

bob = Bob(age=40)
second_bob = Bob(age=25)
alice = Alice(age=35)

# 创建朋友群
friends = FriendGroup(bob, alice, second_bob)

# 批量交互
print(friends.interact())
# 输出:
# Bob讲了个笑话。
# Alice笑了起来。
# Bob讲了个笑话。

# 遍历所有成员
for member in friends:
    print(f"年龄:{member.age}")

# 通过索引访问成员
print(friends[0].interact())  # 输出:Bob讲了个笑话。

# 添加新成员
new_alice = Alice(age=28)
friends.add_member(new_alice)

# 移除成员
friends.remove_member(second_bob)

内容的提问来源于stack exchange,提问作者Euler_Salter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:03:27