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

如何实现类组合?继承与组合选型及属性访问优化问询

问题描述

我有一个包含多个项的模型,所有项都具备一组共享属性:id、name、parent。此外,部分项仅用于容纳其他项,部分项用于连接其他项等。

在实现时,我考虑过使用继承,也考虑过使用组合(且看到很多建议优先选择组合)。于是我定义了一个仅用于提供共享属性访问与修改的基础类:

from attrs import define, Factory
@define
class BaseProperties:
  identifier: str
  name: str
  parent_id: Optional[str]

@define
class ConnectorItem:
  base_properties: BaseProperties
  connections: "List[ConnectorItems]" = Factory(list)

@define
class ContainerItem:
  base_properties: BaseProperties
  children: "List[Union[ConnectorItem, ContainerItem]]" = Factory(list)

但问题在于,当我想访问ContainerItem的name或id时,需要写成:

container_item.base_properties.identifier

或者在ContainerItem中定义property:

@property
def identifier(self):
  return self.base_properties.identifier

才能直接使用:

container_item.identifier

第一种方式要求使用者了解ContainerItem的内部实现,且项目扩张后会出现大量多层点语法;第二种方式则需要在每个包含BaseProperties的类中重复编写大量getter和setter,十分繁琐。

我是否应该改用继承?虽然ContainerItem和ConnectorItem共享基础属性,但它们永远不会在同一上下文使用,我仅需要代码复用,不需要多态。

考虑到这是简化版,实际中ConnectorItem可能由不同类组合生成不同的ConnectorItemTypes,请问更好的实现方式是什么?

解决方案

1. 直接用继承(最适合你的场景)

如果你只是需要代码复用而不需要多态,完全不用被「优先组合」的教条限制——这条建议主要针对需要多态、避免继承层级臃肿的场景,你的情况里继承就是最直接高效的方案:

from attrs import define, Factory
from typing import Optional, List, Union

@define
class BaseItem:
    identifier: str
    name: str
    parent_id: Optional[str]

@define
class ConnectorItem(BaseItem):
    connections: List["ConnectorItem"] = Factory(list)

@define
class ContainerItem(BaseItem):
    children: List[Union[ConnectorItem, ContainerItem]] = Factory(list)

这样你可以直接访问container_item.identifier、connector_item.name,不需要额外的代理代码,简洁明了。

2. 坚持组合?用__getattr__自动代理属性

如果因为某些原因必须用组合(比如未来要动态替换基础属性逻辑),可以通过实现__getattr__方法自动代理base_properties的属性,避免重复编写大量@property:

from attrs import define, Factory
from typing import Optional, List, Union

@define
class BaseProperties:
    identifier: str
    name: str
    parent_id: Optional[str]

@define
class ConnectorItem:
    base_properties: BaseProperties
    connections: List["ConnectorItem"] = Factory(list)

    def __getattr__(self, name):
        # 当访问的属性不在当前类时,自动从base_properties获取
        return getattr(self.base_properties, name)

@define
class ContainerItem:
    base_properties: BaseProperties
    children: List[Union[ConnectorItem, ContainerItem]] = Factory(list)

    def __getattr__(self, name):
        return getattr(self.base_properties, name)

现在你同样可以直接写container_item.name,Python会自动帮你从base_properties中取对应属性。如果类自身有同名属性,会优先使用自身的,不会产生冲突。

3. 针对多类型ConnectorItem:继承基础属性 + 组合功能组件

如果需要扩展不同类型的ConnectorItem,推荐用「继承共享属性 + 组合不同功能组件」的方式,兼顾代码复用和灵活性:

from attrs import define, Factory
from typing import Optional, List, Protocol

# 定义连接逻辑的协议,规范不同Connector的行为
class ConnectionLogic(Protocol):
    def add_connection(self, item: "BaseItem") -> None:
        ...

# 不同的连接逻辑实现
@define
class DirectConnectionLogic:
    connections: List["BaseItem"] = Factory(list)

    def add_connection(self, item: "BaseItem") -> None:
        self.connections.append(item)

@define
class RestrictedConnectionLogic:
    connections: List["BaseItem"] = Factory(list)
    max_connections: int = 2

    def add_connection(self, item: "BaseItem") -> None:
        if len(self.connections) < self.max_connections:
            self.connections.append(item)
        else:
            raise ValueError("超过最大连接数")

# 共享基础属性的父类
@define
class BaseItem:
    identifier: str
    name: str
    parent_id: Optional[str]

# 组合不同逻辑的ConnectorItem
@define
class ConnectorItem(BaseItem):
    connection_logic: ConnectionLogic

# 使用示例
direct_connector = ConnectorItem(
    identifier="conn1",
    name="直接连接",
    parent_id=None,
    connection_logic=DirectConnectionLogic()
)
restricted_connector = ConnectorItem(
    identifier="conn2",
    name="限制连接",
    parent_id=None,
    connection_logic=RestrictedConnectionLogic(max_connections=3)
)

这种方式既复用了基础属性,又能灵活扩展新的Connector类型——未来新增连接逻辑时,只需要实现ConnectionLogic协议即可,无需修改现有代码,符合开闭原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 18:44:58