您的位置:首页 > 大数据 > 人工智能

Package内的__main__.py和__init__.py

2016-04-18 18:52 603 查看
假设一个最简单的Package如下:

├──pkg
│   ├── __init__.py
│   ├── __main__.py


如果你希望 python 将一个文件夹作为 Package 对待,那么这个文件夹中必须包含一个名为 __init__.py 的文件,即使它是空的。 参见:
Packages

如果你需要 python 将一个文件夹作为 Package 执行,那么这个文件夹中必须包含一个名为 __main__.py 的文件。参见: __main__ — Top-level script environment

在实际中,可以将pkg作为一个文件夹执行:

python pkg


也可以将pkg作为一个Package执行:

python -m pkg


那么,这两者有什么区别呢?为此,我们做一个简单的实验。

在__init__.py写入如下内容:

#!/usr/bin/env python
import sys

print('__init__')
print('__init__.__name__', __name__)
print('__init__.__package__', __package__)


在__main__.py写入如下内容:

#!/usr/bin/env python
import sys

print('__main__')
print('__main__.__name__', __name__)
print('__main__.__package__', __package__)

print('sys.path', sys.path)


执行 python pkg 和 python -m pkg,对比一下它们的输出结果:



可以看出:

当作文件夹执行的时候,__init__.py 不会被执行。对于 __main__.py 来说,变量 __package__ 是一个空字符串。

当作模块执行的时候,会先执行 __init__.py ,再执行 __main__.py 。对于 __main__.py 来说,变量 __package__ 是 Package 的名字。另外, __init__.py 和 __main__.py 中的 __name__变量的值是不同的。

对于一个 Package 来说,既然 __init__.py 必须存在,并且当作为模块执行的时候,它会先执行,我们就应该把入口函数 main() 定义在 __init__.py 中。

当我们使用模块方式 -m 执行的时候, __init__.py 定义了 main() 函数,然后在 __main__.py 中调用它,就能实现我们统一入口的目的。

对 __init__.py 做如下修改:

#!/usr/bin/env python
import sys

print('__init__')
print('__init__.__name__', __name__)
print('__init__.__package__', __package__)

print('sys.path', sys.path)

def main():
print('__init__.main()')


对 __main__.py 做如下修改:

#!/usr/bin/env python
import sys

print('__main__')
print('__main__.__name__', __name__)
print('__main__.__package__', __package__)

print('sys.path', sys.path)

import pkg
pkg.main()


执行 python pkg ,调用失败;执行 python -m pkg,调用正常。对比一下它们的输出结果:



原因在于,sys.path 的第一个搜索路径,一个是 pkg ,一个是空字符串。

对于 python -m pkg 的调用方式来说,由于 __init__.py 被事先载入,此时 python 解释器已经知道了这是一个 package ,因此当前路径(空字符串)被包含在 sys.path 中。然后再调用 __main__.py ,这时 import pkg 这个包就没有问题了。

而对于 python hhlb 的调用方式来说,由于 __init__.py 没有被载入,python 解释器并不知道自己正在一个 Package 下面工作。默认的,python 解释器将 __main__.py 的当前路径 pkg 加入 sys.path 中,然后在这个路径下面寻找 pkg 这个模块。显然, pkg 文件夹下面并没有 pkg 这个模块,因此出错。

要理解这点,就要明白 __init__.py 是 python 解释器将当前文件夹作为 Package 处理的必要条件。如果没有读取到 __init__.py ,python 就不会认为当前的文件夹是一个 Package,而只是把它当作普通文件夹来处理。

既然找到了问题原因,那么只需要把当前路径加入到 sys.path 中,就能解决这个问题。

修改后的 __main__.py 如下:

#!/usr/bin/env python
import os, sys

print('__main__')
print('__main__.__name__', __name__)
print('__main__.__package__', __package__)

if not __package__:
path = os.path.join(os.path.dirname(__file__), os.pardir)
sys.path.insert(0, path)

print('sys.path', sys.path)

import pkg
pkg.main()


执行 python pkg ,结果正常:



看到这里,有人可能会提出两个问题:

1. 为什么不直接在 sys.path 前面加上一个空字符串来解决问题呢? 

答:因为,如果不是在当前路径下调用,空字符串就没效果了,比如执行 python /path/to/pkg。

2. 为什么不用 if __package__ == '' 来判断 __package__ 的值呢?

答:这并不是偷懒。因为可能会出现这种调用(不推荐): python pkg/__main__.py 。而这种情况下, __package__ 的值就是 None 而不是 '' 了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: