列出目录的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.pyanalyse

  • 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/

    10-10 05:43