首页 > 解决方案 > 从 __init__ 中的 Python 子模块导入方法,但不是子模块本身

问题描述

我有一个具有以下结构的 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绝对会使文件变得混乱。)

标签: pythonpython-importpython-module

解决方案


The existence of the mymod.tools attribute is crucial to maintaining proper function of the import system. One of the normal invariants of Python imports is that if a module x.y is registered in sys.modules, then the x module has a y attribute referring to the x.y module. Otherwise, things like

import x.y
x.y.y_function()

break, and depending on the Python version, even

from x import y

can break. Even if you don't think you're doing any of the things that would break, other tools and modules rely on these invariants, and trying to remove the attribute causes a slew of compatibility problems that are nowhere near worth it.


Trying to make tools not show up in your mymod module's namespace is kind of like trying to not make "private" (leading-underscore) attributes show up in your objects' namespaces. It's not how Python is designed to work, and trying to force it to work that way causes more problems than it solves.

The leading-underscore convention isn't just for instance variables. You could mark your tools module with a leading underscore, renaming it to _tools. This would prevent it from getting picked up by from mymod import * imports (unless you explicitly put it in an __all__ list), and it'd change how IDEs and linters treat attempts to access it directly.


推荐阅读