Ped*_*ori 29 python import python-3.x python-importlib
标准库清楚地记录了如何直接导入源文件(给定源文件的绝对文件路径),但如果源文件使用隐式同级导入,则此方法不起作用,如下例所示.
如果这个例子适用于隐式兄弟导入的存在?
我已经签出这个和这个其他的话题#1的问题,但他们并没有解决隐同级进口内用手导入的文件.
这是一个说明性的例子
目录结构:
root/
- directory/
- app.py
- folder/
- implicit_sibling_import.py
- lib.py
Run Code Online (Sandbox Code Playgroud)
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())
Run Code Online (Sandbox Code Playgroud)
lib.py:
def hello():
return 'world'
Run Code Online (Sandbox Code Playgroud)
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())
Run Code Online (Sandbox Code Playgroud)
python folder/implicit_sibling_import.py使用if __name__ == '__main__':块运行注释掉ISI says: worldPython 3.6中的产量.
但运行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'
Run Code Online (Sandbox Code Playgroud)
如果我想补充import sys; sys.path.insert(0, os.path.dirname(isi_path))到app.py,python app.py产量world如预期,但我想避免改写(munging)的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.
解决方案必须实现为python函数,它接受所需模块的绝对文件路径并返回模块对象.
理想情况下,解决方案不应引入副作用(例如,如果它确实修改sys.path,它应该返回sys.path其原始状态).如果解决方案确实引入了副作用,那么它应该解释为什么在不引入副作用的情况下无法实现解决方案.
PYTHONPATH如果我有多个项目这样做,我不想记得PYTHONPATH每次在它们之间切换时设置.用户应该只能够pip install我的项目并运行它而无需任何额外的设置.
-m该-m标志是推荐/ Python的方法,但标准库中也明确文件如何直接导入源文件.我想知道如何调整这种方法来应对隐含的相对进口.显然,Python的内部必须这样做,那么内部结构与"直接导入源文件"文档有何不同?
小智 14
我能想到的最简单的解决方案是sys.path在执行导入的函数中暂时修改:
from contextlib import contextmanager
@contextmanager
def add_to_path(p):
import sys
old_path = sys.path
sys.path = sys.path[:]
sys.path.insert(0, p)
try:
yield
finally:
sys.path = old_path
def path_import(absolute_path):
'''implementation taken from https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly'''
with add_to_path(os.path.dirname(absolute_path)):
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
Run Code Online (Sandbox Code Playgroud)
除非您同时在另一个线程中导入,否则这不会导致任何问题.否则,由于sys.path恢复到以前的状态,应该没有不必要的副作用.
编辑:
我意识到我的答案有些不尽如人意,但是,深入研究代码会发现,该行spec.loader.exec_module(module)基本上会导致exec(spec.loader.get_code(module.__name__),module.__dict__)被调用.这里spec.loader.get_code(module.__name__)只是lib.py中包含的代码.
因此import,只需通过exec语句的第二个参数注入一个或多个全局变量,就可以找到一种方法使语句的行为不同.但是,"无论你做什么使导入机器看起来都在该文件的文件夹中,它都必须在初始导入的持续时间之后停留,因为当你调用它们时,该文件中的函数可能会执行进一步的导入",如@所述. user2357112在问题评论中.
不幸的是,改变import语句行为的唯一方法似乎是改变sys.path或在包中__path__.module.__dict__已经包含__path__使似乎不工作,这叶子sys.path(或者试图找出为什么执行不会代码当作一个包,即使它有__path__和__package__... -但我不知道从哪里开始-也许它有与没有__init__.py文件有关的事情).
此外,这个问题似乎并不具体,importlib而是兄弟进口的一般问题.
编辑2:如果您不希望模块sys.modules以下结尾应该工作(请注意,sys.modules导入过程中添加的任何模块都将被删除):
from contextlib import contextmanager
@contextmanager
def add_to_path(p):
import sys
old_path = sys.path
old_modules = sys.modules
sys.modules = old_modules.copy()
sys.path = sys.path[:]
sys.path.insert(0, p)
try:
yield
finally:
sys.path = old_path
sys.modules = old_modules
Run Code Online (Sandbox Code Playgroud)
将PYTHONPATH应用程序所在的路径添加到环境变量中
增加模块文件的默认搜索路径.格式与shell的PATH相同:一个或多个目录路径名由os.pathsep分隔(例如Unix上的冒号或Windows上的分号).默认忽略不存在的目录.
在bash上它是这样的:
export PYTHONPATH="./folder/:${PYTHONPATH}"
Run Code Online (Sandbox Code Playgroud)
或直接运行:
PYTHONPATH="./folder/:${PYTHONPATH}" python directory/app.py
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6185 次 |
| 最近记录: |