跳转到主要内容

优先使用组合而非继承

项目描述

Compclasses

Code style: black

类似于dataclasses,但用于组合。

正如四人帮(可能)所说

优先使用对象组合而非类继承。

然而,当我们在Python中使用组合时,我们无法直接访问组合类的函数,我们或者从头开始重新定义这些函数,或者使用链式调用访问它们。

这个代码库旨在解决这个问题,通过委托类所需的方法到其属性中,使其变得容易实现。


文档 | 源代码


目录表

Alpha通知

此代码库是实验性的,适用于我的用例。很可能存在尚未涵盖的情况,所有内容都会中断。如果您发现这种情况,请随时在问题页面上打开一个问题。

安装

compclasses 作为Python包发布在pypi上,可以使用pip安装,最好使用虚拟环境。

在终端中,可以使用以下命令安装:

python -m pip install compclasses

入门

假设我们有以下3个类,分别是 FooBarBaz

  • FooBar 之间相互独立;
  • Baz 通过两个类属性 (_foo_bar) 进行初始化,这两个属性是其他两个类的实例。
class Foo:
    """Foo class"""

    def __init__(self, value: int):
        """foo init"""
        self._value = value

    def get_value(self):
        """get value attribute"""
        return self._value

    def hello(self, name: str) -> str:
        """Method with argument"""
        return f"Hello {name}, this is Foo!"


class Bar:
    """Bar class"""
    b: int = 1

    def __len__(self) -> int:
        """Custom len method"""
        return 42

class Baz:
    """Baz class"""

    def __init__(self, foo: Foo, bar: Bar):
        self._foo = foo
        self._bar = bar

现在让我们实例化它们,看看我们如何访问“内部”属性/方法

foo = Foo(123)
bar = Bar()

baz = Baz(foo, bar)

baz._foo.get_value()  # -> 123
baz._foo.hello("GitHub")  # -> "Hello GitHub, this is Foo!"
baz._bar.__len__()  # -> 42

len(baz)  # -> TypeError: object of type 'Baz' has no len()

compclass(装饰器)

使用 compclass 装饰器,我们可以在定义时将我们想要的方法从其属性转发到 Baz

from compclasses import compclass

delegates = {
    "_foo": ( "get_value", "hello"),
    "_bar": ("__len__", )
}

@compclass(delegates=delegates)
class Baz:
    """Baz class"""

    def __init__(self, foo: Foo, bar: Bar):
        self._foo = foo
        self._bar = bar

baz = Baz(foo, bar)
baz.get_value()  # -> 123
baz.hello("GitHub")  # -> "Hello GitHub, this is Foo!"
len(baz)  # -> 42

现在我们可以直接访问这些方法了。

注意,在 delegates 字典中,我们有以下内容:

  • 键对应于 Baz 类中的属性名称;
  • 每个值应该是一个字符串的可迭代集合,对应于与键属性关联的类实例中存在的属性/方法。

compclass 装饰器将每个属性和方法作为 属性属性 添加,可以以 self.<attr_name> 的形式调用,而不是 self.<delegatee_cls>.<attr_name>

CompclassMeta(元类)

另一种等效的、但不同的方法是,在定义类时使用 CompclassMeta 元类。

from compclasses import CompclassMeta

delegates = {
    "_foo": ( "get_value", "hello"),
    "_bar": ("__len__", )
}

class Baz(metaclass=CompclassMeta, delegates=delegates):
    """Baz class"""

    def __init__(self, foo: Foo, bar: Bar):
        self._foo = foo
        self._bar = bar

baz = Baz(foo, bar)
baz.get_value()  # -> 123
baz.hello("GitHub")  # -> "Hello GitHub, this is Foo!"
len(baz)  # -> 42

如您所见,其语法几乎与 compclass 装饰器一一对应,并且结果行为完全相同!

高级使用

而不是在 delegates 字典中使用可迭代对象,我们建议使用 代理人 实例作为值。

这将提供更多灵活性和功能,当我们决定转发类属性和方法时。

请参阅专门的 文档页面,以更好地理解并查看更多关于如何使用 代理人、其优缺点示例。

为什么选择组合(TL;DR)

总的来说,组合是一种更灵活、更透明的代码重用和类设计方式。它允许构建满足特定需求的类,并促进代码重用和模块化。

更详细的解释见 文档页面

反馈

欢迎在存储库的 问题页面 提供反馈、改进/增强或问题。

贡献

请参阅文档站点中的 贡献指南

灵感

本项目受到 forwardable 库的启发,这是一个“通过代理进行简单对象组合的实用程序”。

然而,我在寻找更多灵活性和更多功能。特别是

  • 在类定义和方法转发之间有明确的分离;
  • 有一个验证步骤,以确保从组件中更改内容不会破坏类;
  • 可以通过单一指令转发给定组件的所有方法/属性;
  • 可以为每个组件添加前缀和/或后缀;

请参阅 超越基础 页面以查看示例用法。

许可证

本项目遵循 MIT 许可证

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源代码发行版

compclasses-0.4.2.tar.gz (58.5 kB 查看哈希值)

上传时间 源代码

构建发行版

compclasses-0.4.2-py3-none-any.whl (11.6 kB 查看哈希值)

上传时间 Python 3

由以下支持