Python 3.5+:如何在隐式兄弟引用存在的情况下,根据完整文件路径动态导入模块?

23 浏览
0 Comments

Python 3.5+:如何在隐式兄弟引用存在的情况下,根据完整文件路径动态导入模块?

问题

标准库明确记录了如何直接导入源文件(给定源文件的绝对路径),但是如果该源文件使用了下面示例中描述的隐式同级导入,则该方法无效。

如何适应隐式同级导入的情况下修改示例以使其工作?

我已经查看了这个这个其他关于此主题的Stackoverflow问题,但它们没有解决被手动导入的文件中的隐式同级导入的问题。

设置/示例

这是一个说明性的示例

目录结构:

root/
  - directory/
    - app.py
  - folder/
    - implicit_sibling_import.py
    - lib.py


app.py:

import os
import importlib.util
# 构建绝对路径
root = os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
isi_path = os.path.join(root, 'folder', 'implicit_sibling_import.py')
def path_import(absolute_path):
   '''implementation taken from https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly'''
   spec = importlib.util.spec_from_file_location(absolute_path, absolute_path)
   module = importlib.util.module_from_spec(spec)
   spec.loader.exec_module(module)
   return module
isi = path_import(isi_path)
print(isi.hello_wrapper())


lib.py:

def hello():
    return 'world'


implicit_sibling_import.py:

import lib # 这是隐式同级导入,获取root/folder/lib.py
def hello_wrapper():
    return "ISI says: " + lib.hello()
#if __name__ == '__main__':
#    print(hello_wrapper())


使用注释掉的if __name__ == '__main__':块运行python folder/implicit_sibling_import.py会得到Python 3.6中的ISI says: world

但是运行python directory/app.py会得到:

Traceback (most recent call last):
  File "directory/app.py", line 10, in 
    spec.loader.exec_module(module)
  File "", line 678, in exec_module
  File "", line 205, in _call_with_frames_removed
  File "/Users/pedro/test/folder/implicit_sibling_import.py", line 1, in 
    import lib
ModuleNotFoundError: No module named 'lib'

解决方法

如果我在app.py中添加import sys; sys.path.insert(0, os.path.dirname(isi_path)),则python app.py会像预期的那样输出world,但如果可能的话,我想避免修改sys.path

答案要求

我希望python app.py能够打印ISI says: world,并且我希望通过修改path_import函数来实现这一点。

我不确定修改sys.path的影响。例如,如果有directory/requests.py并且我将directory的路径添加到sys.path中,我不希望import requests开始导入directory/requests.py而不是导入使用pip install requests安装的requests库

解决方案必须作为一个python函数实现,该函数接受所需模块的绝对文件路径并返回模块对象

理想情况下,解决方案不应引入副作用(例如,如果它确实修改了sys.path,则应将sys.path返回到其原始状态)。如果解决方案确实引入副作用,它应解释为什么不能在不引入副作用的情况下实现解决方案。


PYTHONPATH

如果我有多个执行此操作的项目,我不想每次切换项目时都要记住设置PYTHONPATH。用户只需pip install我的项目并运行它,无需任何额外设置。

-m

-m标志是推荐的/Pythonic方法,但标准库还清楚地记录了如何直接导入源文件。我想知道如何适应隐式相对导入的这种方法与“直接导入源文件”文档中的方法有何不同?

0