- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
标准库明确记录how to import source files directly (给定源文件的绝对文件路径),但如果源文件使用下面示例中描述的隐式同级导入,则此方法不起作用。
如果存在隐式同级导入,该示例如何适应工作?
我已经 checkout this和 this other 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__':
block 产生 ISI says: world
in Python 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'
如果我添加 import sys; sys.path.insert(0, os.path.dirname(isi_path))
到 app.py
, 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
而不是导入 requests library我使用 pip install requests
安装的。
解决方案必须实现为接受所需模块的绝对文件路径并返回 module object 的 python 函数。 .
理想情况下,解决方案不应引入副作用(例如,如果它确实修改了 sys.path
,它应该将 sys.path
返回到其原始状态)。如果解决方案确实引入了副作用,它应该解释为什么不引入副作用就无法实现解决方案。
PYTHONPATH
如果我有多个项目这样做,我不想每次在它们之间切换时都必须记住设置 PYTHONPATH
。用户应该能够pip install
我的项目并运行它而无需任何额外的设置。
-m
-m
flag是推荐的/pythonic 方法,但标准库也明确记录了 How to import source files directly .我想知道如何调整这种方法来应对隐式相对导入。显然,Python 的内部必须这样做,那么内部和“直接导入源文件”文档有何不同?
最佳答案
我能想到的最简单的解决方案是在执行导入的函数中临时修改 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
除非您同时在另一个线程中进行导入,否则这不会导致任何问题。否则,由于 sys.path
已恢复到以前的状态,因此应该不会有任何副作用。
编辑:
我意识到我的回答有些不令人满意,但是,深入研究代码表明,spec.loader.exec_module(module)
行基本上会导致 exec(spec.loader.get_code (module.__name__),module.__dict__)
被调用。这里的 spec.loader.get_code(module.__name__)
就是 lib.py 中包含的代码。
因此,要想更好地回答这个问题,就必须找到一种方法,通过简单地通过 exec 语句的第二个参数注入(inject)一个或多个全局变量来使 import
语句的行为有所不同。但是,“无论您如何使导入机制在该文件的文件夹中查找,它都必须在初始导入的持续时间之外徘徊,因为该文件中的函数可能会在您调用它们时执行进一步的导入”,如 @ 所述user2357112 在问题评论中。
不幸的是,改变 import
语句行为的唯一方法似乎是改变 sys.path
或包中的 __path__
。 module.__dict__
已经包含 __path__
所以这似乎不起作用留下 sys.path
(或试图找出为什么 exec 不将代码视为一个包,即使它具有 __path__
和 __package__
... - 但我不知道从哪里开始 - 也许它与没有__init__.py
文件)。
此外,这个问题似乎不是 importlib
所特有的,而是 sibling imports 的一般问题。 .
Edit2: 如果您不希望模块以 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
关于Python 3.5+ : How to dynamically import a module given the full file path (in the presence of implicit sibling imports)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41861427/
我一直在使用 angular-ui-router 并尝试从同一抽象的另一个 child 中转换到我的一个抽象状态的 child 。这张图更好地展示了这个想法: 所以“R”是模块,“蓝色 1”是我的抽象
很难解释我遇到的这个问题,因为有很多变数。 我的网站使用 javascipt 根据屏幕分辨率调整固定包装的大小,因此所有 div 都必须调整为 3 种不同的宽度,这就是使这个问题如此困难的原因.. H
我必须选择h1节点之间的所有内容。 Index some content some more content Index 2 some content other content Index 3 s
我目前正在使用已使用自定义 xml 转换器转换为 xml 的 VDA 消息类型。但是,源文档中的每个标题和行记录都处于同一级别,如下面的示例所示: 512
我需要查明 Table_3 是否包含属于某个父级的子级的所有 sibling 。例如,我询问 12 号 child (家长 1 = Charles)。 Table_2 告诉我他还有 2 个 sibli
所以我想选择一组元素中的最后一个兄弟元素,但是父容器中有多个元素。 Some text 所以在这种情况下,对于每一 block .select
我正在尝试为我的网站做一种 Accordion ,类似于 this website 上的那个。 , 但没有切换。 我正在使用的代码是 here . 我有基本的设置工作,但我似乎无法让 li 的高度正确
我很难尝试实现 XSL 转换。 我需要改变这个: 1 Homepage AB308E 5
我正在尝试创建一个爬虫来从供应商网站中提取一些属性数据,我可以根据我们的内部属性数据库对其进行审核,并且是 import.io 的新手。我看了一堆视频,虽然我的语法似乎是正确的,但我的手动 xpath
这是我的 xml 的摘录: content content 我定位在node[@id='1'] .我需要一个 Xpath 来匹配所有 元素直到下一个非空节点(这里是 node[@id='
我正在使用 Selenium-Python 来抓取此链接中的内容。 http://targetstudy.com/school/62292/universal-academy/ HTML代码是这样的,
这个问题在这里已经有了答案: Is there a CSS parent selector? (33 个答案) 关闭 4 年前。
代码笔:https://codepen.io/andrelange91/pen/VyjYBg 我做了以下声级示例,它应该在悬停时填充其他条。但目前是落后的...而且我一直没能找到一种方法(尽管我敢打赌
在下面的示例中,我试图获取所有 之前(包括当前的 ) class="current"具有番茄背景色。 所以在这个例子中:1、2、3、4 和 5 的背景颜色是番茄。 如果 我需要它来更新那class
在下面的例子中, h2 p p p h2 p p p h2 我只想在 h2.first 和 h2.second 之间选择 p。我试过 select si
对于下面的示例 HTML,返回具有 class='A' 和 class='B' 的“a”元素的 sibling 的 XPath 查询可以写为://a[@class='A']/following-sib
鉴于此 XML/HTML: Label1Value1 Label2Value2 Label3Value3aValue3b Label4Value4 我想找到所有 ,然后为每个找到
这是我在 StackExchange 的第一篇文章,如果我做错了什么,请多多包涵: 我有一个从产品数据库派生的 XML 文件,其中所有分组信息都丢失了,除了元素的顺序。所有产品都有一个首先出现的商品编
我希望以易于理解的方式描述我的问题。 我的 html 看起来像这样: AAAA BBBB 我想要 TextArea 的 Xpath(其中标签的值为 AAAA)用 Sel
我是一名优秀的程序员,十分优秀!