我正在维护一个python包,在其中我进行了一些重组.现在,我想支持仍然执行from my_package.old_subpackage.foo import Foo而不是新的from my_package.new_subpackage.foo import Foo的客户端,而不需要显式地重新引入许多执行转发的文件.(old_subpackage仍然存在,但不再包含foo.py.)

我了解到有"加载器"和"查找器",我的印象是我应该为自己的目的实现loader,但到目前为止,我只实现了finder:

RENAMED_PACKAGES = {
    'my_package.old_subpackage.foo': 'my_package.new_subpackage.foo',
}

# TODO: ideally, we would not just implement a "finder", but also a "loader"
# (using the importlib.util.module_for_loader decorator); this would enable us
# to get module contents that also pass identity checks
class RenamedFinder:

    @classmethod
    def find_spec(cls, fullname, path, target=None):
        renamed = RENAMED_PACKAGES.get(fullname)
        if renamed is not None:
            sys.stderr.write(
                f'WARNING: {fullname} was renamed to {renamed}; please adapt import accordingly!\n')
            return importlib.util.find_spec(renamed)
        return None

sys.meta_path.append(RenamedFinder())

然而,https://docs.python.org/3.5/library/importlib.html#importlib.util.module_for_loader和相关功能似乎被弃用.我知道这并不是我想要实现的一件很像Python 的事情,但我很高兴知道这是可以实现的.

推荐答案

导入包的__init__.py时,可以将任何对象放入sys.modules中,输入的值将由import条语句返回:

from . import new_package
from .new_package import module1, module2
import sys

sys.modules["my_lib.old_package"] = new_package
sys.modules["my_lib.old_package.module1"] = module1
sys.modules["my_lib.old_package.module2"] = module2

如果有人现在使用import my_lib.old_packageimport my_lib.old_package.module1,他们将获得my_lib.new_package.module1的参考.由于进口机器已经在sys.modules字典中找到了 keys ,它甚至从未开始寻找旧文件.

如果希望避免立即导入所有子模块,可以通过在sys.modules中放置一个带有__getattr__的模块来模拟一点延迟加载:

from types import ModuleType
import importlib
import sys

class LazyModule(ModuleType):
 def __init__(self, name, mod_name):
  super().__init__(name)
  self.__mod_name = name

 def __getattr__(self, attr):
  if "_lazy_module" not in self.__dict__:
    self._lazy_module = importlib.import(self.__mod_name, package="my_lib")
  return self._lazy_module.__getattr__(attr)

sys.modules["my_lib.old_package"] = LazyModule("my_lib.old_package", "my_lib.new_package")

Python相关问答推荐

@Property方法上的inspect.getmembers出现意外行为,引发异常

时间序列分解

将整组数组拆分为最小值与最大值之和的子数组

如何在箱形图中添加绘制线的传奇?

max_of_three使用First_select、second_select、

为什么默认情况下所有Python类都是可调用的?

Telethon加入私有频道

海上重叠直方图

为什么NumPy的向量化计算在将向量存储为类属性时较慢?'

当点击tkinter菜单而不是菜单选项时,如何执行命令?

在代码执行后关闭ChromeDriver窗口

基于另一列的GROUP-BY聚合将列添加到Polars LazyFrame

为什么t sns.barplot图例不显示所有值?'

当HTTP 201响应包含 Big Data 的POST请求时,应该是什么?  

多个矩阵的张量积

时长超过24小时如何从Excel导入时长数据

Scipy.linprog的可行性有问题吗?(A_ub@x0<;=b_ub).all()为True-但是-linprog(np.zeros_like(X0),A_ub=A_ub,b_ub=b_ub)不可行

try 在单个WITH_COLUMNS_SEQ操作中链接表达式时,使用Polars数据帧时出现ComputeError

多个布尔条件的`jax.lax.cond`等效项

`Convert_time_zone`函数用于根据为极点中的每一行指定的时区检索值