Python中“__package__”属性的目的是什么?

时间:2014-01-20 11:47:17

标签: python python-3.x

我想知道的是 __package__究竟是什么意思?在官方文档中没有找到任何解释,即使是在SO。

如果你能提供一些例子,我会非常高兴。

2 个答案:

答案 0 :(得分:40)

请参阅PEP 366import system reference documentation

  

主要的建议更改是引入新的模块级属性__package__。如果存在,相对导入将基于此属性而不是模块__name__属性。

  
      
  • 应设置模块的__package__属性。它的值必须是一个字符串,但它可以是与__name__相同的值。如果该属性设置为None或缺少,则导入系统将使用更合适的值填充该属性。当模块是包时,其__package__值应设置为其__name__。当模块不是包时,__package__应设置为顶级模块的空字符串,或子模块的空字符串,设置为父包的名称。有关详细信息,请参阅PEP 366
  •   

因此,对于位于foo/bar/baz.py的模块,__name__设置为foo.bar.baz__package__设置为foo.bar,而foo/bar/__init__.py } foo.bar__name__属性都将__package__

答案 1 :(得分:16)

  

我想知道的是__package__究竟是什么意思

这是启用显式相对导入的机制。

__package__

有三种可能的值类别
  • 包名称(字符串)
  • 空字符串

包名称

也就是说,如果模块位于包中,则__package__设置为包名称以启用显式相对导入。 Specifically:

  

当模块是包时,其__package__值应设置为__name__。当模块不是包时,应为子模块设置__package__到父包的名称。

空字符串

如果模块位于root或top级别,即使用

导入当前模块
import current_module

或当顶级模块作为入口点运行时:

$ python -m current_module

然后__package__是一个空字符串。或者作为documentation says

  

当模块不是包时,__package__应设置为顶级模块的空字符串......

如果模块/脚本按文件名运行,__package__ is None

  

当主模块由其文件名指定时,__package__属性将设置为无。

证据

首先,让我们使用Python 3.6创建一个带有嘈杂调试的文件结构:

text = "print(f'{__name__}, __file__: {__file__}, __package__: {repr(__package__)}')"

from pathlib import Path
Path('foo.py').write_text(text)
Path('package').mkdir()
Path('package/__init__.py').write_text(text)
Path('package/__main__.py').write_text(text)
Path('package/bar.py').write_text(text)

# and include a submodule with a relative import:
Path('package/baz.py').write_text(text + '\nfrom . import bar')

现在我们看到作为模块执行的foo.py具有__package__的空字符串,而由文件名作为入口点执行的脚本具有None

$ python -m foo
__main__, __file__: ~\foo.py, __package__: ''
$ python foo.py
__main__, __file__: foo.py, __package__: None

当我们将包作为入口点的模块执行时,其__init__.py模块运行,然后运行__main__.py

$ python -m package
package, __file__: ~\package\__init__.py, __package__: 'package'
__main__, __file__: ~\package\__main__.py, __package__: 'package'

类似地,当我们执行子模块作为入口点的模块时,__init__.py模块运行,然后运行:

$ python -m package.bar
package, __file__: ~\package\__init__.py, __package__: 'package'
__main__, __file__: ~\package\bar.py, __package__: 'package'

最后,我们看到显式的相对导入,__package__的完整原因(这里发生在这里)是启用的:

$ python -m package.baz
package, __file__: ~\package\__init__.py, __package__: 'package'
__main__, __file__: ~\package\baz.py, __package__: 'package'
package.bar, __file__: ~\package\bar.py, __package__: 'package'

注意,在输出中,我已将~替换为父目录。