python-3.x 管理__init__.py中的可选依赖项

yc0p9oo0  于 2023-01-14  发布在  Python
关注(0)|答案(1)|浏览(114)

我正在开发一个python包K(所以它有一个__init__.py)。
这样的包包含不同的子包,每一个关于我的工作的不同部分,让我们称之为这些M之一(所以它也有自己的__init__.py).
现在M有2个模块A和B,每个模块包含一个或多个函数,有多少函数并不重要,但有一个区别:A中的所有函数依赖于可选的依赖性 * opt_dep_A *,并且类似地,B * opt_dep_B * 也是如此。
当安装带有pip的K时,可以安装两个可选的依赖项,例如pip install 'K[opt_dep_A]'
现在我希望让用户体验"模块化",这意味着如果用户对B功能感兴趣,他/她应该能够使用它们(前提是安装了opt_dep_B),而不必担心A。
问题是这样的:A和B属于同一个子包(因为它们的功能涉及同一个高级"组")。
从K或M的__init__.py导入/显示时,如何处理此冲突?
在这两种情况下,例如从M/__init__.py执行此操作时

from .A import func_A
from .B import func_B

如果没有安装 * opt_dep_A *,但是用户执行了from K import func_Bfrom M import func_B,那么任何捕获/未捕获的ImportError或从A模块发出的警告都会被触发,即使我从来不想从A导入东西。
不过,我还是希望A和B属于同一层:如何在保持模块化的同时,仍然保持相同的封装结构?有可能吗?
我尝试了try/except子句importlib.util.find_spec,但问题实际上是我正在从同一级别导入一个部分。

n8ghc7c1

n8ghc7c11#

这里有一个基本的方法来处理这个问题。在K/M/A.py中,你可以有这样的代码:

import whatever # not optional dependency

def func_A():
    import opt_dep_A
    ...

def some_other_func():
    # Doesn't require opt_dep_A
    ...

def func_A_frobnicate():
    # requires opt_dep_A as well, but dont worry, imports are cached
    import opt_dep_A
    ...

本质上,在函数(或类或其他)被 * 实际使用 * 之前,您不会真正导入可选依赖项。这样,您将func_A移到哪个名称空间都无关紧要。
这就是pandas处理此问题的方式,请参见:
https://github.com/pandas-dev/pandas/blob/8dab54d6573f7186ff0c3b6364d5e4dd635ff3e7/pandas/io/pytables.py#L559
注意,它们将导入封装在一个函数中,该函数提供一些错误处理和有用的错误消息,以及一些其他的簿记逻辑,但基本思想是相同的。
注意,处理ImportError * 的方法当然会 * 起作用,然后每当您尝试使用具有依赖关系的函数时,您将获得随机的NameErrors。因此,我真的不认为这样做会节省您很多工作,因为如果您将顶层导入 Package 在try... except ImportError中,那么您必须处理该名称将不存在于模块中的问题

相关问题