如何使 Python 模块的 __init__.py 将其 help() 委托给同级文件?

How can I make a Python module's __init__.py delegate its help() to a sibling file?

目前我们有一个 Python 库,类似于:

Jupiter/
  __init__.py
  Ganymede.py
  Callisto.py

Ganymede.py 依次包含函数 Foo()Bar(),等等。我们现有的脚本通过

使用这些功能
from Jupiter import Ganymede
# later in the program...
Ganymede.Foo()

我想重新组织一下,使目录看起来更像

Jupiter/
  __init__.py
  Ganymede/
    __init__.py
    functions.py
  Callisto/
    __init__.py
    functions.py

无需破坏或修改任何使用 Jupiter 的现有脚本。

如果 Ganymede/__init__.py 使用 "The import system" documentation 中描述的导入语法:

from .functions import *

然后 Foo()Bar() 最终出现在 Ganymede 的命名空间中,但是 Ganymede 的 help() 没有提到它们。

>>> from Jupiter import Ganymede
>>> dir(Ganymede)
['Bar', 'Foo', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'functions']
>>> help(Ganymede)
Help on package Jupiter.Ganymede in Jupiter:

NAME
    Jupiter.Ganymede

PACKAGE CONTENTS
    functions

FILE
    x:\yadda\yadda\ganymede\__init__.py

我真的非常喜欢 help(Ganymede) 自动列出所有可用函数和 类。

我想我可以从 Ganymede/__init__.py execfile("functions.py") 但我觉得必须有一些更简洁的方法来做到这一点。还是没有?

我需要在 Python 2.7.15 和 3.5.3 中都可用的东西。

__init__.py 中创建一个 __all__


如果您在 functions.py 中已经有一个 __all__,您可以直接导入它:

from .functions import *
from .functions import __all__

如果您要将多个模块合并在一起,则必须更加冗长

from .functions import *
from .classes import *
from . import functions
from . import classes
__all__ = functions.__all__ + classes.__all__

或者,当然,您可以始终明确:

from .functions import *
__all__ = ['spam', 'eggs']

或者,如果您想动态构建它:

from .functions import *
from . import functions
__all__ = [name for name in dir(functions) if not name.startswith('_')]

… 或(相当 hacky——但如果你有一个 __init__.py 从大量子模块收集名称而不做任何其他事情,有时会有用)…

from .functions import *
__all__ = [name for name in globals() if not name.startswith('_')]

… 或者你可以变得非常聪明并按照这种方式做事,例如,multiprocessing 做:1

from . import functions
__all__ = [name for name in functions if not name.startswith('_')]
globals().update({name: getattr(functions, name) for name in __all__})

请记住,__all__ 也会影响某人执行 from Ganymede import *(实际上是 that's the main purpose of __all__)时发生的情况,以及 inspect 和其他工具报告的内容作为您的包的 public 成员。

我不确定在没有 __all__ 的情况下 help 的行为在任何地方都有记录(交互式 help 作品一般只被少量记录......),而且它没有与 import 认为的 public.

完全相同

1.好吧,multiprocessing 实际上比 clever/hacky 多很多;它不是从子模块中提取属性,而是从该子模块的动态单例属性中提取属性,该子模块在主进程和子进程上设置不同,并相应地更改包的一些顶级函数…