将子模块中的方法导入到根命名空间的Python模块

6 投票
2 回答
3056 浏览
提问于 2025-04-17 18:43

我想创建一个像NumPy那样的Python模块。这个模块的功能不仅仅是一些子模块,而是有一个根模块,里面包含了很多我可以直接调用的方法,同时还有一些子模块。问题是,这些根方法必须在某个地方定义。我在考虑一个目录结构:

module/
  __init__.py
  core.py
  stuff1.py
  submodule/
    __init__.py
    stuff2.py
    stuff3.py

我希望“core”文件夹里的所有内容都能被导入到“module”这个命名空间里,就像它是一个module.py文件一样,而core.py的内容就像在这个module.py里一样。问题是,module是一个文件夹,而不是一个文件,那我该怎么定义这些应该在模块根部的方法呢?

我试着在init.py里写“from core import *”,但那并没有成功。(编辑:其实是成功了。)

我是不是应该把core的方法放在一个“module.py”文件里,同时还有一个module文件夹?我不知道这样是否可行,但看起来有点别扭。

2 个回答

5

我觉得你想要的是能够这样做:

# some_other_script.py

import module
# Do things using routines defined in module.core

当你让Python执行 import module 时,简单来说,就是运行了 module/__init__.py 文件,并创建了一个 module 对象,这个对象会被导入到你的命名空间中。这个对象(再次强调,非常简单的说法)包含了在运行 __init__.py 时发生的事情,比如定义的名称等等。你可以通过 module.something 来访问这些内容。

现在,如果你的设置是这样的:

# module/__init__.py

from module.core import Clazz
c = Clazz()
print c # Note: demo only! Module-level side-effects are usually a bad idea!

当你导入 module 时,你会看到类似这样的打印信息:

<module.core.Clazz object at 0x00BBAA90>

很好。但是如果你接着尝试访问 c,你会得到一个 NameError 错误:

# some_other_script.py
import module # prints "<module.core.Clazz object at 0x00BBAA90>"
print c # NameError (c is not defined)

这是因为你并没有导入 c;你只导入了 module。如果你的入口脚本看起来是这样的:

# some_other_script.py
import module # prints "<module.core.Clazz object at 0x00BBAA90>"
print module.c  # Access c *within module*

一切都会正常运行。这样做 会和 from core import * 或者 from module import * 一样正常,但我(还有PEP8)建议不要这样做,因为当你开始随意导入时,脚本的内容就不太清楚了。为了清晰起见:

# module/core.py

def core_func():
    return 1


# module/__init__.py

from core import *
def mod_func():
    return 2

上面的代码基本上是没问题的,不过你可以把 core 改成“私有”的(重命名为 _core),这样就表示没有必要从包外去触碰它了。

# some_other_script.py

from module import *

print core_func() # Prints 1
print mod_func() # Prints 2
3

你可以查看一下关于 __all__ 列表的信息。这个列表可以让你定义哪些名字是可以被外部使用的。

如果你把它标记好,就可以设置一个函数来决定从你的子模块中引入哪些内容:

@property
all(self):
    #Whatever introspective code you may want for your modules
    __all__ += submodule.__all__

如果你想在模块空间里引入所有的内容,这里有一种方法:

$ ipython
In [1]: from foomod import *

In [2]: printbar()
Out[2]: 'Imported from a foreign land'

In [3]: ^D
Do you really want to exit ([y]/n)?
$ ls foomod/
__init__.py __init__.pyc    core.py         core.pyc        submodule
$ grep . foomod/*.py 
foomod/__init__.py:from foomod.core import *
foomod/core.py:def printbar():
foomod/core.py:     return "Imported from a foreign land"

... 如果我们把 __init__.py 文件留空的话:

$ echo > foomod/__init__.py
$ ipython

In [1]: from foomod import *

In [2]: printbar()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-ba5b6693441e> in <module>()
----> 1 printbar()

NameError: name 'printbar' is not defined

撰写回答