优先使用组合而非继承
项目描述
Compclasses
类似于dataclasses,但用于组合。
正如四人帮(可能)所说
优先使用对象组合而非类继承。
然而,当我们在Python中使用组合时,我们无法直接访问组合类的函数,我们或者从头开始重新定义这些函数,或者使用链式调用访问它们。
这个代码库旨在解决这个问题,通过委托类所需的方法到其属性中,使其变得容易实现。
目录表
Alpha通知
此代码库是实验性的,适用于我的用例。很可能存在尚未涵盖的情况,所有内容都会中断。如果您发现这种情况,请随时在问题页面上打开一个问题。
安装
compclasses 作为Python包发布在pypi上,可以使用pip安装,最好使用虚拟环境。
在终端中,可以使用以下命令安装:
python -m pip install compclasses
入门
假设我们有以下3个类,分别是 Foo
、Bar
和 Baz
Foo
和Bar
之间相互独立;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 许可证。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。