如何更直接地引用函数?

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()生效。
如何解决?

hl0*_*37_ 5

你就是不能喜欢这个。

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)


Pak*_*ula 4

导入分析后在命名空间中添加 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)

我是这样测试的:

  1. 文件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)
  1. 文件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`")
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模块,因为注入代码没有执行。