从 __init__ 中的 Python 子模块导入方法,但不是子模块本身

Import method from Python submodule in __init__, but not submodule itself

我有一个具有以下结构的 Python 模块:

mymod/
    __init__.py
    tools.py
# __init__.py
from .tools import foo
# tools.py
def foo():
    return 42

现在,当 import mymod 时,我看到它有以下成员:

mymod.foo()
mymod.tools.foo()

不过我不想要后者;它只会污染命名空间。

有趣的是,如果 tools.py 被调用 foo.py 你会得到你想要的:

mymod.foo()

(显然,这仅在每个文件只有一个函数的情况下有效。)

如何避免导入 tools?请注意,将 foo() 放入 __init__.py 不是一个选项。 (实际上,有 很多 函数,例如 foo 绝对会使文件混乱。)

尝试将其放入您的 __init__.py 文件中:

from .tools import foo
del tools

mymod.tools 属性的存在对于维持导入系统的正常功能至关重要。 Python 导入的正常不变量之一是,如果模块 x.ysys.modules 中注册,则 x 模块有一个 y 属性引用x.y 模块。否则,

import x.y
x.y.y_function()

break,根据 Python 版本,甚至

from x import y

可以破解。即使您不认为自己正在做任何会破坏的事情,其他工具和模块也依赖于这些不变量,并且尝试删除该属性会导致大量兼容性问题,而这些问题根本不值得。


试图让 tools 不出现在你的 mymod 模块的命名空间中有点像试图不让 "private" (leading-underscore) 属性出现在你的模块中对象的命名空间。这不是 Python 设计的工作方式,并且试图强迫它以这种方式工作会导致比它解决的问题更多的问题。

leading-underscore 约定不仅仅适用于实例变量。您可以使用前导下划线标记您的 tools 模块,并将其重命名为 _tools。这将防止它被 from mymod import * 导入获取(除非你明确地将它放在 __all__ 列表中),并且它会改变 IDE 和 linters 处理直接访问它的尝试的方式。

您没有导入 tools 模块,它只是在您导入包时可用:

import mymod

您将有权访问 __init__ 文件中定义的所有内容以及此包的所有模块:

import mymod

# Reference a module
mymod.tools

# Reference a member of a module
mymod.tools.foo

# And any other modules from this package
mymod.tools.subtools.func

当你在 __init__ 中导入 foo 时,你只是让 foo 在那里可用,就像你在那里定义了它一样,当然你在 [=15] 中定义了它=] 这是一种组织包的方式,所以现在既然你在 __init__ 中导入了它,你就可以:

import mymod

mymod.foo()

或者你可以单独导入foo

from mymod import foo

foo()

但是您可以导入 foo 而无需在 __init__ 中使用它,您可以执行以下与上面的示例完全相同的操作:

from mymod.tools import foo

foo()

您可以使用这两种方法,它们都是正确的,在所有这些示例中您不是 "cluttering the file" 正如您所看到的那样,使用 mymod.tools.foo 访问 foo 是命名空间的,因此您可以在其他模块中定义了多个 foo