问题
标准库清楚地记录了如何直接导入源文件(给定源文件的绝对路径),但是如果该源文件使用如下所述的隐式兄弟导入,则此方法将无法正常工作。
如何才能使该示例适应隐式兄弟导入的情况?
我已经查看了此处和此其他关于这个主题的Stackoverflow问题,但它们没有解决手动导入文件中存在的隐式兄弟导入。
设置/示例
以下是说明性示例
目录结构:
root/
- directory/
- app.py
- folder/
- implicit_sibling_import.py
- lib.py
app.py
:
import os
import importlib.util
# construct absolute paths
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 # this is the implicit sibling import. grabs root/folder/lib.py
def hello_wrapper():
return "ISI says: " + lib.hello()
#if __name__ == '__main__':
# print(hello_wrapper())
运行 python folder/implicit_sibling_import.py
,并注释掉 if __name__ == '__main__':
块,在 Python 3.6 中会输出 ISI says: world
。
但是运行 python directory/app.py
则会输出:
Traceback (most recent call last):
File "directory/app.py", line 10, in <module>
spec.loader.exec_module(module)
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
File "/Users/pedro/test/folder/implicit_sibling_import.py", line 1, in <module>
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方法,但标准库也明确记录了如何直接导入源文件。我想知道如何调整该方法以处理隐式相对导入。显然,Python的内部必须这样做,那么内部与“直接导入源文件”文档的区别在哪里?
sys.path
可能是最好的选择。无论你做什么让导入机制查找该文件夹,它都必须在初始导入的持续时间之外存在,因为当你调用来自该文件的函数时,它们可能执行进一步的导入。 - user2357112sys.path
是可以接受的。在这种情况下,只需要“解释为什么篡改sys.path
是最佳选择”即可。 - Pedro Cattori