创建聚合其他对象的类的最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实例的集合对象,有几个问题:
- 使用列表、字典、元组还是自定义类更符合Pythonic风格?
- 面向对象框架下,自定义集合类的典型实现方式是什么?比如是否应该让
FriendGroup继承AbstractPerson? - 希望能便捷地使用集合内的实例,比如批量调用方法、遍历等。
我不关心实例的名称,只要求所有实例都继承自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
相关产品推荐
相关产品推荐

