PYTHONPATH文档https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH说:“可以从Python程序中将搜索路径作为变量sys.path进行操作。”
I.E。另一个模块可以自由编辑sys.path并将其附加到列表的任何位置,甚至可以将其清空。
我的理解是,为了保持一致的搜索顺序,使用了PYTHONPATH,不是吗?
让我们假设“ y”模块在脚本A.py中更改sys.path 汇入x 进口y 导入z
我希望在顶层模块中具有一致的搜索顺序路径,我认为这可能会受到另一个子模块/导入对其进行更改的影响。 PYTHONPATH是获取此信息的方法还是我还不知道的其他提示/技巧?
答案 0 :(得分:0)
如果您的模块y更改了sys.path
,则即使您执行importlib.reload(sys)
所以想象模块'y'执行
from sys import path
path.clear()
在您的A.py脚本中:
import sys, importlib
import x, y
importlib.reload(sys)
print(sys.path) # is []
import z
找不到模块z。
要解决此问题,您可以将脚本sys.path
变量恢复为解释器开头指定的值。
从文档中:
一个字符串列表,它指定模块的搜索路径。从环境变量PYTHONPATH初始化,再加上依赖于安装的默认值。
还有...
在程序启动时初始化,此列表的第一项path [0]是包含用于调用Python解释器的脚本的目录
让我们假设解释器未在交互模式下运行或未从stdin读取(它正在执行文件脚本),并且其位于当前工作目录中
我们的A.py可能类似于:
import importlib
import x, y
# We can still load (sys, os, ...)
from sys import path
from os import getcwd
import site
print(sys.path) # []
path.append(getcwd()) # Add directory where script is executed
path.append(os.environ.get('PYTHONPATH')) # Add PYTHONPATH
site.main() # Add site packages
import z # Now this dont fail
注意:即使删除所有sys.path
项,importlib
也能够找到软件包os
,site
,sys
,...
这是因为importlib
使用sys.modules
访问此类软件包:
如果模块在sys.modules中,则返回sys.modules [name]。 loader
根据sys.modules文档:
这是一个字典,将模块名称映射到已加载的模块。
sys.path
是否已更改。
如果为true,则将其设置为原始值
from copy import copy
import warnings
import sys
sys.path = list(sys.path)
_original_path = copy(sys.path)
_base_import = __import__
def _import(*args, **kwargs):
try:
module = _base_import(*args, **kwargs)
return module
finally:
if type(sys.path) != list or sys.path != _original_path:
warnings.warn('System path was modified', Warning)
# Restore path
sys.path = copy(_original_path)
__builtins__.__import__ = _import
现在执行以下代码:
import sys
before = copy(sys.path)
import y # 'y' tries to change sys.path
after = copy(sys.path)
print(before == after) # True
它还将在标准输出上显示警告消息
EDIT#2(另一种解决方案):
这仅适用于python> = 3.7,因为它依赖于PEP 562
在这里,我基本上替换了模块“ sys”,以便避免外部模块更改实际的sys.path
首先使用下一个代码(proxy.py)创建脚本:
import importlib
from sys import path, modules
from copy import copy
path = copy(path)
modules = copy(modules)
def __getattr__(name):
if name in globals():
return getattr(globals(), name)
return getattr(importlib.import_module('sys'), name)
def __dir__():
return dir(importlib.import_module('sys'))
现在,在您的A.py上,输入下一个代码:
import proxy
import sys
sys.modules['sys'] = proxy
import y # y imports 'sys' but import sys returns the 'proxy' module
# 'y' thinks he changes sys.path but it only modifies proxy.path
print(proxy.path) # []
print(sys.path) # Unchanged
y模块上的代码
import sys
sys.path.clear() # a.k: proxy.path.clear()
# You can still access to all properties from the sys module
print(dir(sys)) # ['ps1', 'ps2', 'platform', ...]