仅使模块中的特定符号可导入,f.i。用`__all__`

Make only specific symbols in a module importable, f.i. with `__all__`

tl;博士:

我喜欢干净的 APIs,因此我不喜欢在模块 API 中出现“内部”导入,例如 import numpy as np 在我的模块中。 =46=]

在我的包中(可使用 setup.py 安装)我有几个子模块,每个子模块导入一组模块,f.i。 numpysys.
等内置函数 假设在我的包 mytools 中,我有一个子模块 mytools.mysubtools,我在其中定义了一个函数 foo,它需要 numpy(使用 import numpy as np 导入)。 foo 添加到 __all__ = ['foo'] .
现在我用 import mytools.mysubtools as mst 导入这个子模块。使用代码完成访问 mst 时(例如使用 Spyder IDE、PyCharm、Kite...)等,它总是显示 all符号 mst 中导入,在本例中为 foonp.
我可以避免像 np 这样的导入出现在导入的模块中,这样只有 foo 出现吗?

详细说明

假设我的包结构如下所示:

/mytools/
 |-- __init__.py  # "mytools-init"
 |-- my_subtools.py
 |-- some_other_subtools.py

__init__.py 文件如下所示:

from . import my_subtools as mysubtools
__all__ = ['mysubtools']

my_subtools.py

import numpy as np

__all__ = ['foo']

def foo():
    return np.sqrt(4**3)

现在我用

导入my_subtools
import mytools.mysubtools as mst

使用导入模块时,代码补全等总是显示npfoo、f.i。输入 mst. 时。对于较大的模块,有许多导入 and/or function/class 定义,这使得使用代码补全变得非常困难,因为对 API 的简单检查显示 all 导入的模块和所有定义。

使用 __all__ 可以避免为 wild/star 导入所有符号,例如 from mytools.mysubtools import *__all__ 也能以某种方式用于破坏显式导入吗?

或者有什么推荐的方法可以避免导入模块的所有符号吗?
对于函数,我使用 name mangling、f.i。 def _baz(): return 4**4.
但这也对进口、f.i是可取的吗? import numpy as _np?有什么缺点吗?

我在找什么:
显式解决方案,因为:显式优于隐式。
例如 __all__,但用于显式导入。或者像 __except__ = ['np'] 这样的东西,它从导入中排除符号。

__all__ = [...] 是“可导出”名称的一个很好的提示,但这只是一个提示。 (尽管 PyCharm 等一些 IDE 确实倾向于获得线索。)

我肯定不会import numpy as _np;你只会让你的生活更艰难。

你不能阻止别人从你的模块中导入东西,如果他们在那里......如果你真的真的想要,你当然可以后缀所有你的模块有像 del np, sys, os, quux, bar 这样的咒语;那样的话那些名字就不会被导入了,但是...我真的不认为这是值得的。

感谢@chepner 的评论,我一直在研究 python 标准库中的链接实现。

结论是,标准库中经常使用导入模块的名称修饰。在我看来,这是一个非常明显的迹象,这被认为是 pythonic and/or 没有严重的缺点。
可以找到几个 name-mangling 导入的例子,f.i.:

哪些模块是 name-mangled 似乎因文件而异。似乎越是“system-relevant”的模块,越不容易被name mangled。例如导入

import re as _re
import math as _math

经常被名称损坏,而 import os as _os 则不那么常见。这只是我的印象,所以不一定是真的。