列出目录的analyse
结构如下:
tree analyse
analyse
├── __init__.py
└── vix.py
__init__.py
为空白,vix.py
包含一个函数draw_vix
。import analyse
analyse.vix.draw_vix()
draw_vix
是用 analyse.vix.
引用的,我想用vix
而不是analyse.vix
引用的,也就是说,要在vix
之后的命名空间中添加import analyse
。编辑
__init__.py
:import analyse.vix as vix
from analyse import vix
检查一下:import analyse
vix.draw_vix()
错误消息:NameError: name 'vix' is not defined
@ hl037_,如果我在from .vix import vix
中添加__init__.py
,import analyse
vix.draw_vix()
它带来了其他问题:ImportError:无法导入名称“vix”。我想要的是在
__init__.py
中写一些东西,或者在vix.py
中写一些东西,以使vix.draw_vix()
生效。如何解决?
最佳答案
默认情况下,顶级作用域在模块中不可见。模块中的globals
函数返回模块级范围,而不是导入模块的范围(请参阅docs)。因此,需要一些技巧来将vix
放入外部范围。
绝招一。
Python中的import
语句实际上是一个函数调用,并且__init__.py
中的代码在某些堆栈帧中执行。诀窍是找到与导入模块相对应的堆栈框架,污染的范围。尽管这是可能的,但这是一种BAD,BAD的做法。它与Python的范式矛盾,在Python的范式中,简单的事情必须简单。如果您在顶层作用域中需要vix
,请使用import analyse.vix as vix
。示波器污染可能导致名称冲突和非常细小的错误。
绝招二。
在包的第一次加载期间,__init__.py
中的代码仅执行一次。在所有其他import analyse
语句中,python解释器使用sys.modules
中缓存的模块对象。我发现的唯一方法是拦截对内置__import__
的调用并添加范围污染钩子(Hook)。
我强烈建议避免使用此“魔术” 。不过,如果您想在脚上射击,那就在这里。
将以下代码放在analyse/__init__.py
中:
from . import vix # create module "vix" in the current scope
import inspect as _inspect
# Inject `vix` at the first import
_frame = _inspect.currentframe().f_back
while _frame is not None:
# skip Pyhton module loader
if "importlib._bootstrap" in _frame.f_code.co_filename:
_frame = _frame.f_back
continue
#this frame corresponds to the scope, where `import analyse` is specified
_frame.f_globals["vix"] = vix # bind the name 'vix' with the module 'vix' from the current scope
print("DEBUG: injected vix to ", _frame)
break
# remove _frame from the scope
del[_frame]
# overload __import__ function to inject `vix` on every succeeding import of `analyse`
import builtins as _builtins
# save builtin that `import` calls
_builtin_import = _builtins.__import__
def _never_overload_import(name, globals=None, locals=None, fromlist=(), level=0):
"This function injects `vix` in every scope that imports `analyse` module"
global _builtin_import
global vix
result = _builtin_import(name, globals, locals, fromlist, level)
if name == "analyse" and globals is not None and not "vix" in globals:
globals["vix"] = vix
print("DEBUG: injected vix to ", _inspect.currentframe().f_back)
return result
_builtins.__import__ = _never_overload_import
这是我测试的方式:
secondary.py
仅加载目标模块。 import analyse
# demonstrate that first import created `vix` at this scope
vix.draw_vix("called from secondary as `vix.draw_vix`")
test.py
同时加载secondary.py
和analyse
print("***Loading secondary***")
import secondary
print("***Secondary loaded***")
print("***Loading analyse***")
import analyse
print("***Analyse loaded***")
analyse.vix.draw_vix("called as `analyse.vix.draw_vix`")
vix.draw_vix("called as `vix.draw_vix`")
输出:
***Loading secondary***
DEBUG: injected vix to <frame at 0x00909AE8, file 'secondary.py', line 1, code <module>>
draw vix, called from secondary as `vix.draw_vix`
***Secondary loaded***
***Loading analyse***
DEBUG: injected vix to <frame at 0x00911C30, file 'test.py', line 6, code <module>>
***Analyse loaded***
draw vix, called as `analyse.vix.draw_vix`
draw vix, called as `vix.draw_vix`
更改
添加了重载
__import__
函数。最初的答案只是将vix
对象插入到导入范围的全局变量中,但这还不够:__init__.py
中的代码在包的第一次加载期间仅执行一次。当将软件包导入另一个文件时,由于未执行注入(inject)代码,因此将看不到vix
模块。关于python - 如何更直接地引用功能?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59928591/