5 python import namespaces module
列出目录analyse结构如下:
tree analyse
analyse
??? __init__.py
??? vix.py
Run Code Online (Sandbox Code Playgroud)
该__init__.py是空白的,vix.py包含一个函数draw_vix。
import analyse
analyse.vix.draw_vix()
Run Code Online (Sandbox Code Playgroud)
draw_vix被引用 analyse.vix.,我想用vix而不是引用它,analyse.vix也就是说,vix在命名空间之后添加import analyse。
编辑__init__.py:
import analyse.vix as vix
from analyse import vix
Run Code Online (Sandbox Code Playgroud)
检查一下:
import analyse
vix.draw_vix()
Run Code Online (Sandbox Code Playgroud)
一条错误信息:
NameError: name 'vix' is not defined
Run Code Online (Sandbox Code Playgroud)
@ hl037_,如果我添加from .vix import vix的__init__.py,
import analyse
vix.draw_vix()
Run Code Online (Sandbox Code Playgroud)
它带来了其他问题:ImportError: cannot import name 'vix'。我想要的是写点东西__init__.py,或者写点东西vix.py,让它vix.draw_vix()生效。
如何解决?
你就是不能喜欢这个。
import analyse
Run Code Online (Sandbox Code Playgroud)
只会analyse在您的全局命名空间中创建。充其量,您可以这样做:
from analyse import vix
Run Code Online (Sandbox Code Playgroud)
将 vix 放入全局变量中。
(或使用星号表示法)
奖励:如果您不想安装分析(即使使用 venv,建议将您的应用程序/模块实际安装在 venv 中),要导入vixinside __init__.py,您也可以在内部进行相对导入:
from . import vix
Run Code Online (Sandbox Code Playgroud)
https://docs.python.org/3/reference/import.html
[编辑] 一些例子:
文件系统 :
analyse
?? __init__.py
?? vix.py
?? viz.py # another submodule to demonstrate some other points
?? vit.py # yet another submodule to demonstrate some other points
Run Code Online (Sandbox Code Playgroud)
analyse/__init__.py 包含:
import .vit as vit
# from . import vit # works too
vix = 42
viy = 43
Run Code Online (Sandbox Code Playgroud)
analyse/vix.py 包含:
def draw_vix():
print('Hello from drax_vix')
Run Code Online (Sandbox Code Playgroud)
analyse/viz.py 包含:
def draw_viz():
print('Hello from drax_viz')
Run Code Online (Sandbox Code Playgroud)
analyse/vit.py 包含:
def draw_vit():
print('Hello from drax_vit')
Run Code Online (Sandbox Code Playgroud)
如果您的包已安装(例如,您创建了 setup.py 并 did pip install analyse),或 PYTHONPATH 环境变量中列出的目录的子目录,则在包外的任何脚本(或 python 解释器)中,会发生以下情况:
1)使用别名完全导入:
import analyse.vix as vix
vix.draw_vix()
# works, and print print('Hello from drax_vix')
analyse
# Error : analyse not defined since you import only symbols from analyse.vix, not the modules themselves
analyse.vix
# Same as previous one
Run Code Online (Sandbox Code Playgroud)
2) 全进口
import analyse.vix
analyse.vix.draw_vix()
#Works because python guarantees analyse.vix to be a valid expression
analyse
# analyse module
vix.draw_vix()
# Error : vix undefined. import analyse.vix guarantee analyse.vix to be a valid module, but does not add submodule to global namespace
analyse.vix
# is a module, analyse.vix module has been imported too and set as an attribute of analyse.
analyse.viz
# Error : analyse has no attr viz since viz is a non-imported submodule.
analyse.vit.draw_vit
# Works because analyse is indirectly imported with import analyse.vix, and vit is imported as vit inside analyse/__init__.py and thus an attribute of the module.
Run Code Online (Sandbox Code Playgroud)
3) 仅导入分析
import analyse
analyse.vix
# 42 because analyse.vix module is not imported, thus you saw the variable defined vix inside analyse
vix.draw_vix()
# Error : vix undefined, obviously, you never defined it nor imported it
vit.draw_vit()
# Works because vit is imported in __init__.py and set as an attribute of analyse.
Run Code Online (Sandbox Code Playgroud)
4) 从分析导入 vix, viy, viz, vit
from analyse import vix, viz, vit
analyse.vix
# Error : analyse not define since you imported only symbols
vix
# 42 Because you didn't import analyse.vix and there is a variable vix defined in analyse/__init__.py
viy
# 43
viz
# module viz : viz is not declared as an attribute of analyse, but it falls back to the existing analyse.viz module
vit
# module vit : The one imported inside __init__.py (that is the same as analyse.vit)
Run Code Online (Sandbox Code Playgroud)
5)从分析进口viy
from analyse import viy
# Error : analyse/viy.py does not exist
Run Code Online (Sandbox Code Playgroud)
导入分析后在命名空间中添加 vix
默认情况下,顶级范围在模块中不可见。globals模块中的函数返回模块级范围,而不是导入模块的范围(请参阅文档)。因此,需要一些技巧来将其置于vix外部范围内。
绝招一。
importPython中的语句实际上是一个函数调用,并且其中的代码__init__.py在某个堆栈帧中执行。诀窍是找到与导入模块相对应的堆栈帧并污染它的范围。虽然这是可能的,但这是一种非常糟糕的做法。它与 Python 的范式相矛盾,在 Python 中,简单的事情就必须简单。如果您需要vix在顶级范围内,请使用import analyse.vix as vix. 范围污染可能会导致名称冲突和非常微妙的错误。
绝招二。
from 的代码__init__.py仅在第一次加载包期间执行一次。在所有其他import analyse语句中,python 解释器使用缓存在sys.modules. 我发现的唯一方法是拦截对__import__内置函数的调用并添加范围污染挂钩。
我强烈建议避免这种“魔法”。不过,如果你想搬起石头砸自己的脚,那就来吧。
将以下代码放入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
Run Code Online (Sandbox Code Playgroud)
我是这样测试的:
secondary.py仅加载目标模块。import analyse
# demonstrate that first import created `vix` at this scope
vix.draw_vix("called from secondary as `vix.draw_vix`")
Run Code Online (Sandbox Code Playgroud)
test.py同时加载secondary.py和analyseprint("***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`")
Run Code Online (Sandbox Code Playgroud)
输出:
***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`
Run Code Online (Sandbox Code Playgroud)
变更日志
增加了重载__import__功能。最初的答案只是将vix对象插入到导入范围的全局变量中,但这还不够:__init__.py在第一次加载包期间,代码仅执行一次。当包被导入到另一个文件中时,它不会看到任何vix模块,因为注入代码没有执行。
| 归档时间: |
|
| 查看次数: |
229 次 |
| 最近记录: |