Python:如何使用相对导入导入包根目录

3

我有一个python包的顶层 __init__.py 中定义了大量名称。 我想使用 相对 导入来导入此命名空间,因为在运行时我不一定知道包名称(它甚至可能被用作其他包的子包)。例如,考虑以下包:

my_package/
    __init__.py
        a = 1
        b = 2
        ...
    my_module.py
        from . import ??? as my_package
        print(my_package.a)

我该如何修复my_module中的导入语句,以使print语句正常工作?我也想避免使用from . import a, b, ...; 我只想将整个包命名空间作为一个变量导入。
我尝试过from . import __init__ as my_package,但在这种情况下,my_package被导入为一个方法包装器,而不是一个模块。我还尝试过my_module = __import__(__package__),但这仅适用于my_package不是子包的情况。
看起来应该有一个简单的答案...

1
为什么需要将那段代码放在 __init__.py 中,而不是将其作为单独的模块? - BrenBarn
仅为方便起见 - 包中的许多符号被导入到顶层,以使它们易于使用。我想我也可以将同一组导入放到子模块中,但这似乎是一个愚蠢的修复方法(但问题可能同样愚蠢)。 - Luke
__init__.py文件是一个罕见的地方,如果您希望使子模块中的所有公共名称在包中也直接可访问,则可以适用 from submodule import *。正如@BrenBarn所说,只需将其放入单独的模块中,然后您就可以在 my_module 中使用 from . import submodule - jfs
3个回答

4
您可以使用__package__来获取包名,并使用importlib.import_module来导入模块;这适用于子包。
import importlib
pkg = importlib.import_module(__package__)
print(pkg.a)

@BrenBarn,感谢您的评论。我更新了答案,使用import.import_module - falsetru
谢谢,这个很好用(尽管BrenBarn和shx2提出了一个有说服力的论点,即要避免首先出现这种情况)。 - Luke
@Luke,我同意他们。 - falsetru

1

__init__.py用于将你想要从包的顶层暴露为可导入的名称。在这种情况下,你正在使用它来内部导入名称,即在你的包内部使用,因此出现了问题。

因此,你应该将所有要在包内部使用的名称/定义放在另一个文件中,例如defs.py,并从那里导入:

from .defs import foo, bar, baz

请确保仅在__init__.py中保留您想要向外界顶层公开的名称。

编辑:为了澄清,在__init__.py中,您不需要复制名称/定义,只需以完全相同的方式导入它们:

from .defs import foo, bar

或者

from .defs import *  # not recommended, of course

然后,从包的外部,可以像这样从包的顶层导入名称:
from my_package import foo, bar

或者

from path.to.my_package import foo, bar

init.py 中的名称是公开可见的,但某些内部模块受益于访问相同的合并命名空间(以避免非常冗长的导入语句块)。我肯定可以在内部模块中复制合并的命名空间,但似乎应该能够导入顶级命名空间。 - Luke
@Luke:你可以导入顶层命名空间,但你需要知道它的名称(即包名)。 - BrenBarn
我在测试中无法实现这一点,其中“顶层”实际上是某个较大包的子包。在导入时,父包命名空间中似乎缺少子包名称。 - Luke
2
@Luke:那么它就不是顶层了!:-) 我不太确定你描述的情况,尽管在一个包中的模块并不会在你只导入包时自动加载。因此,如果你执行 import pkg 然后 pkg.module 将失败,除非你实际上执行 import pkg.module 或类似操作。 - BrenBarn

0
据我所知,没有一种方法可以使用相对导入将包的__init__.py导入为模块。
如果您在包中有各种通用对象,并且您的目标是(如您在评论中所说)“使它们易于使用”,只需将它们放在自己的模块中。 from . import miscPackageStuff并不比from . import __init__(如果您可以这样做)更方便。
如果您还希望这些通用对象可以在不显式导入子模块的情况下从外部访问,请在__init__.py中执行from .miscPackageStuff import foo, bar, baz

方便性是为外部用户而设计的;init.py中的名称旨在公开可见,但某些内部模块受益于访问相同的大型命名空间(以避免过长的导入语句块)。 - Luke
1
@Luke:然后只需在__init__.py中从子模块导入即可。 - BrenBarn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接