当外部模块已经导入到其他导入模块中时,是否必须重新导入它们?

Do you have to reimport external modules when they have been already imported in an other imported module?

假设您有两个模块 mod1.pymod2.py

mod1.py 中你导入一个外部(到你正在写的包)模块,比如 numpy 并且在 mod2.py 中你导入 mod1:

里面 mod1.py:

import numpy as np

里面 mod2.py:

import mod1

现在,如果 mod2 中的一行代码调用 np,则会引发错误:

NameError: name 'np' is not defined

如何避免在包的每个模块(= 文件)中导入外部模块?

import 可以做两件事:

  1. 创建模块
  2. 在绑定到模块的当前作用域中定义一个新变量

一个模块foo每个进程只会创建一次,无论import foo执行了多少次。但是,import foo 总是 将模块绑定到名称 foo,即使 foo 已经绑定(到该模块或其他一些值)在当前范围内。

import numpy as np 将名称 np 绑定到 mod1.

全局范围内的 numpy 模块

import mod1只绑定名字mod1;它不会将 mod1 的全局变量带入 mod2 的全局范围。为此,你需要像

这样的东西
from mod1 import np

或者在 mod2.

中使用 mod1.np

您似乎在寻找一种将 np 放入进程范围的命名空间的方法,可从任何模块访问。 Python 只有一个这样的命名空间,即内置命名空间,但您不能向其添加名称。您只有单独的模块全局命名空间。

上面,我提到了一个模块只创建一次。这是因为 import 将首先检查所请求的模块是否已存在于 sys.modules 中。使用

导入一次后,您可以从任何地方访问np
sys.modules['numpy']  # The "real" name, not the import-defined alias

但是,不能保证 numpy 已经定义了 ,您仍然需要导入 sys 才能访问 sys.modules,所以这不会比简单地使用

给你带来任何好处
import numpy as np

随心所欲 np.

这里有两件事需要考虑:导入 模块,以及命名这些导入.

从您的角度来看,这些问题是相同的:如果您想要名称空间中的某些内容,则将其导入:

# mod1.py
import numpy as np

没有其他方法可以将某些内容放入模块的命名空间。 (好吧,没有其他方法不涉及弄乱 sys.modules 来做完全相同的事情)。

但是 python 在这里做了什么?首先,它在 sys.modules 中查找了 numpy。如果不存在,它会加载 numpy,并将加载的模块插入 sys.modules["numpy"]。然后它绑定一个本地名称 np 以指向 sys.modules["numpy"]。因此,如果您导入 mod1,您可以访问它的导入:

#mod2.py
import mod1
mod1.np

(另外:严格执行命名空间是一个很大的好处,并且使 python 更不容易受到随机名称冲突错误的影响。它简化了您作为编码器,因为您不必担心导入模块中的名称会覆盖其他内容,因此您不必求助于命名函数 $get 或其他 'very unlikely' 之类的东西) .

性能

这是否意味着如果多次导入,python将多次加载同一个模块?不会。Python 只会导入一个模块一次:之后它只会绑定一个本地名称以指向 sys.modules 中的模块。所以唯一的性能损失是:

#mod2
import numpy as np
import mod1
np

就是绑定一个名字,这很琐碎(如果你太在意,你就不想使用python)。没有理由这样做:

# mod2.py
from mod1 import np
np

除非更容易不去想你的函数来自哪里(你会经常看到'为了方便,你可以从库y导入x,例如导入[=26的组件=] 直接来自 FastApi).

声明式

像这样管理命名空间有一个巨大的优势:当我阅读您的代码时,我 确切地 知道 function_xyz() 的来源。您要么在作用域中定义它,要么将它导入到该作用域中。将其与例如当我不得不问 JS 或 R 时——xyz 到底从哪里来?然后去查一下。出于同样的原因,如果你写

,大多数 linters 都会对你生气
from numpy import *

并坚持你应该明确地导入你需要的东西。