python 导入包 - 子包不应出现在符号表中

9 投票
1 回答
2365 浏览
提问于 2025-04-17 08:32

我想知道为什么在导入一个Python包时,包含子模块的目录(子包)会作为一个符号出现。比如说,如果我有这样一个包:

PyModTest/                        Top-level package
          __init__.py             Initialize the package
          Source/                 Subpackage holding source files
                 __init__.py
                 WildMod.py        Submodule containing a function: 'WildFunc'

这个包的顶层 __init__.py 文件看起来是这样的:

#!/usr/bin/env python

from Source.WildMod import WildFunc

为了完整起见,底层的 __init__.py 文件是这样的:

#!/usr/bin/env python

__all__ = ["WildMod"]

好吧,现在我打开解释器,导入这个模块,然后查看符号:

>>> import PyModTest
>>> dir(PyModTest)
['Source', 'WildFunc', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__']

你看,'Source' 模块出现了,尽管我并没有特别导入它!

我只想看到一个符号(除了私有的那些),就是我的 'WildFunc'。有没有办法隐藏 'Source' 包呢?

1 个回答

8

这里有两点需要注意:

  • 在Python中,模块实际上是对象,模块名之间的点(.)表示访问一个属性。
  • 你正在做的是相对导入,这意味着Source实际上是PyModTest.Source(感谢TokenMacGuy指出这一点)。

所以:为了导入PyModTest.Source.WildMod.WildFunc,Python需要:

  1. 导入PyModTest(这一步你已经做过了)。
  2. 检查是否有一个叫Source的属性,如果没有,就从PyModTest/Source/__init__.py导入来创建这个属性。
  3. 检查Source是否有一个叫WildMod的属性,如果没有,就从PyModTest/Source/WildMod.py导入来创建这个属性。
  4. 检查WildMod是否有一个叫WildFunc的属性(它是有的)。

一些相关的细节可以在PEP 302Python语言参考中找到。

在机制的更深层次,带点的名称导入会被分解成它的组成部分。对于"import spam.ham",首先会执行"import spam",只有在这一步成功后,才会将"ham"作为"spam"的子模块导入。

如果你不想有一个叫Source的变量,这很简单:在导入函数后只需del Source。但要记住,这样会阻止后续运行的代码访问PyModTest.Source.<任何东西>(除了WildFunc,因为你已经保存了对它的引用)。我建议你只是忽略对Source的引用,而不是删除它,因为它并没有造成什么问题。

撰写回答