【问题标题】:How can I import a module that is not in sys.path?如何导入不在 sys.path 中的模块?
【发布时间】:2021-11-29 17:32:47
【问题描述】:

我需要为用户提供一个不在sys.path 中的模块。我想避免将模块的父目录污染sys.path 到路径,因为我不知道用户系统上可能还有哪些其他模块。有没有与下例不同的解决方案,避免将父目录添加到路径中?

# My library code
import sys
sys.path.insert(0, 'path/to/parent/dir')
import my_module
del sys.path[0]
sys.modules['my_module'] = my_module

# User code
import my_module

StackOverflow 上有类似的问题,但据我所知没有提供解决方案:

【问题讨论】:

  • 这能回答你的问题吗? What exactly should be set in PYTHONPATH?
  • 谁是“用户”?这个模块是否打算供其他程序员在他们自己的程序中使用?如果是这样,那么您应该将其打包,以便他们可以使用 pip 安装它。然后导入将自动可供他们使用。
  • @Code-Apprentice 谢谢,但这是不正确的。 sys.path 从环境变量 PYTHONPATH 初始化并用于导入,用于解析 Python 导入。我上面给出的示例可以正常工作,但如果我要添加到sys.path 的目录中有意外模块,则会失败。所以你提供的链接没有回答我的问题。
  • 我从来不需要直接修改sys.path。你想解决什么问题,你觉得你需要这样做?您尝试导入的模块不在项目目录中吗?
  • @Code-Apprentice 总之,我感谢您的帮助,但我对用例非常清楚,并考虑了那些更常见的选项。我是一位经验丰富的 Python 程序员,只需要回答上面发布的具体问题 :)

标签: python python-3.x python-importlib


【解决方案1】:

我创建了imptools 包以使该解决方案更容易使用:

import imptools  # pip3 install imptools

my_module = imptools.import_path(
    '../path/to/my_module',  # Path to a module directory or single file.
    notfound='error',        # Raise 'error' or 'ignore' if not found.
    reload=False,            # Whether to import if already available.
)

import my_module  # Import statement also works.

解决方案

# My library code
import sys
import importlib.util

name = 'my_module'
path = 'path/to/parent/dir'
for finder in sys.meta_path:
  spec = finder.find_spec(name, [path])
  if spec is not None:
    break
else:
  raise ModuleNotFoundError(f'No module named {name!r}', name=name)
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)

# User code
import my_module
print(dir(my_module))

【讨论】:

  • 您表示sys.path 污染是一个问题;除非您需要 sys.modules 提供的缓存(在这种情况下,我很确定您还需要在 importlib.util.module_from_spec 之前明确检查自己),否则我会强烈考虑返回模块对象,并让用户代码只调用来自您的库的包装器并将结果分配给my_module。对我来说真的闻起来像Explicit is better than implicit. 场景。 (我碰巧有一个自己的项目,它使用了类似的“插件”系统。)
  • 谢谢!是的,这就是我在imptools 包中所做的。我正在检查模块是否存在于sys.modules 中,并且只有在通过reload=True 时才再次导入它。我还返回了模块对象,所以由用户决定是使用返回值还是再次导入模块。
  • @KarlKnechtel 顺便说一句,如果您对处理此类进口有任何其他建议,我很想听听。也许还有更多有用的用例可以添加到包中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-30
  • 2017-06-11
  • 1970-01-01
  • 2015-10-31
  • 1970-01-01
  • 1970-01-01
  • 2020-10-19
相关资源
最近更新 更多