一个在运行时定义可扩展类的库。
项目描述
Extendable
关于
Extendable是一个模块,旨在提供一种定义可扩展Python类的方法。它旨在为开发者提供一种方便且灵活的方式来扩展其Python应用程序的功能。通过利用"extendable"库,开发者可以轻松创建模块化和可定制的组件,这些组件可以在不修改核心代码库的情况下添加或修改。该库利用Python的面向对象编程特性,并为扩展和自定义应用程序提供了一个简单直观的接口。它旨在提高代码的可重用性、可维护性和整体开发效率。它通过继承和组合模式实现扩展。它受到了Odoo实现模型的方式的启发。可以使用其他模式使代码可插拔,而这个库并不取代它们。
快速开始
让我们定义第一个Python类。
from extendable import ExtendableMeta
class Person(metaclass=ExtendableMeta):
def __init__(self, name: str):
self.name = name
def __repr__(self) -> str:
return self.name
使用定义该类的模块的用户需要通过一个姓字段扩展人员的定义。
from extendable import ExtendableMeta
class PersonExt(Person, extends=Person):
def __init__(self, name: str):
super().__init__(name)
self._firstname = None
@property
def firstname(self) -> str:
return self._firstname
@firstname.setter
def firstname(self, value:str) -> None:
self._firstname = value
def __repr__(self) -> str:
res = super().__repr__()
return f"{res}, {self.firstname or ''}"
此时,我们已定义了PersonExt
扩展了Person
的初始定义。为了完成这个过程,我们需要指导运行时,通过构建最终类定义并将其放入当前执行上下文中,完成所有类声明的构建。
from extendable import context, registry
_registry = registry.ExtendableClassesRegistry()
context.extendable_registry.set(_registry)
_registry.init_registry()
一旦完成,Person
和 PersonExt
类就可以在你的代码中互相替换使用,因为它们都代表相同的类...
p = Person("Mignon")
p.firstname = "Laurent"
print (p)
#> Mignon, Laurent
:warning: 这种扩展预定义行为的方式必须谨慎使用,并遵循Liskov 替代原则。它不会替代其他可以用来使代码可插拔的设计模式。
工作原理
幕后,"可扩展" 库利用几个关键概念和机制来启用其功能。总的来说,"可扩展" 库通过元类、注册初始化和动态加载,为扩展 Python 类提供了一种灵活和模块化的方法。通过利用这些机制,开发者可以轻松增强应用程序的功能,而无需对核心代码库进行大量修改。
元类
元类执行两个操作。
- 它收集声明的类的定义,并收集其属性、方法和其他特征的信息。这些定义按模块存储在全局注册表中。这个注册表是模块名到类定义列表的映射。
- 然后,这些信息被用来构建一个类对象,该对象充当稍后根据所有声明的要扩展初始类的类的聚合定义初始化注册表时创建的实际具体类的代理或蓝图...
注册初始化
注册初始化是构建最终类定义的过程。要使蓝图类正常工作,您需要初始化注册表。这是通过调用注册表对象的 init_registry
方法来完成的。该方法将通过聚合要扩展初始类的所有类的定义,通过类层次结构构建最终的类定义。类在层次结构中的顺序很重要。默认情况下,这是 Python 解释器加载类的顺序。对于高级用法,您可以更改此顺序,甚至可以更改用于构建最终类定义的定义列表。这是通过调用带有要加载的模块列表的 init_registry
方法来完成的。
[^1]: 当您将模块指定为要加载的模块列表时,模块名末尾允许使用通配符字符 *
以加载模块的所有子模块。否则,只加载模块本身。
from extendable import registry
_registry = registry.ExtendableClassesRegistry()
_registry.init_registry(["module1", "module2.*"])
一旦注册表被初始化,就必须使其在当前执行上下文中可用,以便蓝图类可以使用它。为此,您必须将注册表设置为 extendable_registry
上下文变量。这是通过调用 extendable_registry
上下文变量的 set
方法来完成的。
from extendable import context, registry
_registry = registry.ExtendableClassesRegistry()
context.extendable_registry.set(_registry)
_registry.init_registry()
动态加载
所有这些都是通过 Python 的动态加载能力实现的。Python 中的动态加载概念指的是在初始编译或执行阶段之外,在运行时加载和执行代码的能力。它允许开发者在满足某些条件或用户输入的情况下动态导入和使用模块、类、函数或变量。动态加载还可以在类实例化时应用。这是 "可扩展" 库在调用蓝图类的构造函数时,实例化最终类定义所使用的机制。这是通过在元类中实现 __call__
方法来完成的,该方法返回最终的类定义而不是蓝图类本身。同样的方法也适用于通过在元类中实现 __subclasscheck__
方法来假装蓝图类是最终的类定义。
开发
要运行测试,请使用 tox
。您将在 htmlcov/index.html
中获得测试覆盖率报告。安装 tox 的简单方法是 pipx install tox
。
此项目使用 pre-commit 来强制执行代码检查(其中包括 black 用于代码格式化、isort 用于排序导入和 mypy 用于类型检查)。
为确保在每次提交时本地运行代码检查器,请安装 pre-commit(推荐使用 pipx install pre-commit
),并在可扩展仓库的本地克隆中运行 pre-commit install
。
要发布
- 运行
bumpversion patch|minor|major
--list
- 检查上一个命令返回的
new_version
值 - 运行
towncrier build
。 - 检查并提交更新的 HISTORY.rst。
git tag {new_version} ; git push --tags
.
贡献
欢迎所有类型的贡献。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。