在Python中启用用户代码扩展的流行技术是什么?

Ree*_*ece 10 python oop python-3.x

我正在寻找允许用户覆盖应用程序中的模块或使用新模块扩展应用程序的技术.

想象一下名为pydraw的应用程序.它目前提供了一个Circle类,它继承了Shape.包树可能如下所示:

/usr/lib/python/
??? pydraw
    ??? __init__.py
    ??? shape.py
    ??? shapes
        ??? circle.py
        ??? __init__.py
Run Code Online (Sandbox Code Playgroud)

现在假设我想启用动态发现和加载实现新形状的用户模块,或者甚至是Shape类本身.对于用户的树来说,与应用程序树具有相同的结构似乎是最直接的,例如:

/home/someuser/python/
??? pydraw
    ??? __init__.py
    ??? shape.py       <-- new superclass
    ??? shapes
        ??? __init__.py
        ??? square.py   <-- new user class
Run Code Online (Sandbox Code Playgroud)

换句话说,我想用来自用户树的同名文件覆盖和屏蔽应用程序树,或者至少从Python的角度来看这个明显的结构.

然后,通过配置sys.path或PYTHONPATH,pydraw.shapes.square可能是可发现的.但是,Python的模块路径搜索找不到像square.py这样的模块.我认为这是因为__method__已经在另一个路径中包含父模块.

你会如何用Python完成这项任务?

小智 1

如果要从不同位置动态加载 python 代码,可以使用pkgutil 模块扩展搜索 __path__ 属性:

将这些行放入每个pydraw/__init__.pyand中pydraw/shapes/__init__.py

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Run Code Online (Sandbox Code Playgroud)

您将能够编写导入语句,就好像您有一个独特的包一样:

>>> import pydraw.shapes
>>> pydraw.shapes.__path__
['/usr/lib/python/pydraw/shapes', '/home/someuser/python/pydraw/shapes']
>>> from pydraw.shapes import circle, square
>>>
Run Code Online (Sandbox Code Playgroud)

您可能会考虑自动注册您的插件。您仍然可以通过设置模块变量(它将充当一种单例模式)来使用基本的 python 代码。

在每个文件中添加最后一行pydraw/shapes/__init__.py

 from pkgutil import extend_path
 __path__ = extend_path(__path__, __name__)

 # your shape registry
 __shapes__ = []
Run Code Online (Sandbox Code Playgroud)

circle.py您现在可以在其相关模块的顶部(或此处)注册形状square.py

 from pydraw.shapes import __shapes__
 __shapes__.append(__name__)
Run Code Online (Sandbox Code Playgroud)

最后一次检查:

 >>> from pydraw.shapes import circle,square
 >>> from pydraw.shapes import circle,square,__shapes__
 >>> __shapes__
 ['pydraw.shapes.circle', 'pydraw.shapes.square']
Run Code Online (Sandbox Code Playgroud)