Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:更改导入文件类型的优先级(.py before.so)_Python_Python 3.x_Import_Python Import_Python Importlib - Fatal编程技术网

Python:更改导入文件类型的优先级(.py before.so)

Python:更改导入文件类型的优先级(.py before.so),python,python-3.x,import,python-import,python-importlib,Python,Python 3.x,Import,Python Import,Python Importlib,如果我从同时包含A.py和A.so的目录中导入,将导入.so文件。我想更改导入文件类型的顺序,以便.py优先于.so,尽管只是暂时的,即在代码行I和j之间。这当然可以通过一些魔法来实现 目前,我通过将.py复制到一个单独的目录中,将该目录预先添加到sys.path中,然后进行导入来解决这个问题,这太糟糕了 为什么需要? .so文件是.py文件的cython编译版本。我正在cython上进行一些自定义代码转换,为此我需要导入.py源代码,即使存在“等效的”。因此 测试设置 下面是一个简单的测试设置

如果我从同时包含
A.py
A.so
的目录中导入
,将导入
.so
文件。我想更改导入文件类型的顺序,以便
.py
优先于
.so
,尽管只是暂时的,即在代码行
I
j
之间。这当然可以通过一些魔法来实现

目前,我通过将
.py
复制到一个单独的目录中,将该目录预先添加到
sys.path
中,然后进行导入来解决这个问题,这太糟糕了

为什么需要?
.so
文件是
.py
文件的cython编译版本。我正在cython上进行一些自定义代码转换,为此我需要导入
.py
源代码,即使存在“等效的”
。因此

测试设置 下面是一个简单的测试设置

#A.py
进口B
#B.py
进口C
打印('hello from B')
#C.py
通过
运行
pythona.py
成功地从
B.py
打印出消息。现在添加
B.so
(因为
.so
文件的内容是不相关的,让
B.so
真正成为文本文件就可以了):

现在
python A.py
失败了。虽然这是现代的处理方式,但到目前为止,我只知道如何使用不推荐使用的模块直接导入特定文件。将
A.py
更新为

#A.py
进口小商品
B=导入加载\源('B','B.py')
使它再次工作。但是,引入
C.so
会再次破坏它,因为对
.py
而不是
.so
的查找不会在导入机制中全局注册:

# C.so
this is a fake binary

注意,在本例中,我只允许编辑
A.py
。我需要Python3.8的解决方案,但我怀疑3.x的任何解决方案也适用于3.8。

我现在有了一个可行的解决方案。它有点粗糙,但我认为它很结实

结果是,
sys.path\u importer\u cache
存储各种查找程序,这些查找程序依次存储
list
加载程序,这些加载程序由
import
按顺序提取。这些加载程序存储为2元组,第一个元素正好是给定加载程序处理的文件扩展名

我只需遍历加载程序的所有
列表
,并将那些带有
.so
扩展名的加载程序推到
列表
的后面,以实现尽可能低的优先级(我可以完全删除它们,但无法导入任何
.so
文件)。我跟踪对sys.path\u importer\u cache的更改,并在完成特殊导入后撤消这些更改。所有这些都被整齐地包装在一个上下文管理器中:

导入集合、上下文库、系统
@contextlib.contextmanager
def禁用_加载器(外部):
ext='.'+ext.lstrip('.'))
#将ext extension的所有装载机推到后面
编辑=集合.defaultdict(列表)
path\u importer\u cache=list(sys.path\u importer\u cache.values())
对于i,枚举中的查找器(路径\导入器\缓存):
loaders=getattr(查找器,'.\u loaders',无)
如果装载机为无:
持续
对于j,枚举中的加载器(加载器):
如果j+len(编辑[i])==len(装载机):
打破
如果加载程序[0]!=提取:
持续
#找到ext扩展的加载程序。
#向后推。
装载机.附加(装载机.pop(j))
编辑[i]。追加(j)
尝试:
#将控件返回给调用方
产量
最后:
#撤消对路径导入器缓存的更改
对于i,在edits.items()中编辑:
加载程序=路径\u导入程序\u缓存[i]。\u加载程序
对于反向(编辑)中的j:
insert(j,loaders.pop())
#演示导入失败
尝试:
导入
例外情况除外,如e:
打印(e)
#演示解决方案
使用disable_loader('.so'):
导入
#用声明证明(通缉)外部故障
进口A2
请注意,要使
import A2
正常失败,您需要复制测试设置,以便您还具有
A2.py
B2.py
C2.py
B2.so
,它们以与原始测试文件相同的方式相互导入


在进行更改之前,只需进行一次完整的备份
copy.deepcopy(sys.path\u importer\u cache)
,然后将此备份粘贴到
sys
,就可以摆脱涉及
编辑的有些复杂的簿记工作。在上面的有限测试中,它确实起作用,但由于导入机器的各个部分可能包含对不同嵌套对象的引用,我认为只使用变异更安全。

“尽管只是暂时的,即在代码行I和j之间”:不;这难道不会让整个事情变得毫无意义,因为一旦模块被导入,它就不会被重新导入吗?@CristiFati不,这就是我需要的。我在函数中加载模块,做一些事情,然后从函数返回。
# C.so
this is a fake binary