Python入门之——Package内的__main__.py和__init__.py

概念

假设一个最简单的Package如下:

├──pkg
│   ├── __init__.py
│   ├── __main__.py
  1. 如果你希望 python 将一个文件夹作为 Package 对待,那么这个文件夹中必须包含一个名为 __init__.py 的文件,即使它是空的。
  2. 如果你需要 python 将一个文件夹作为 Package 执行,那么这个文件夹中必须包含一个名为 __main__.py 的文件。

在实际中,可以将pkg作为一个文件夹执行:python pkg
也可以将pkg作为一个Package执行:python -m pkg

那么,这两者有什么区别呢?

(1) 案例1
在__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,对比一下它们的输出结果:

[root@localhost test]# python pkg
__main__
('__main__.__name__', '__main__')
('__main__.__package__', '')
('sys.path', ['pkg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', 
 '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])

[root@localhost test_08_10]# python -m pkg
__init__
('__init__.__name__', 'pkg')
('__init__.__package__', 'pkg')
__main__
('__main__.__name__', '__main__')
('__main__.__package__', 'pkg')
('sys.path', ['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', 
'/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', 
'/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])

可以看出:
(a) 当作文件夹执行的时候,__init__.py 不会被执行。对于 __main__.py 来说,变量 __package__ 是一个空字符串。
当作模块执行的时候,会先执行 __init__.py ,再执行 __main__.py 。对于 __main__.py 来说,变量 __package__ 是 Package 的名字。另外, __init__.py 和 __main__.py 中的 __name__变量的值是不同的。
(b) 对于一个 Package 来说,既然 __init__.py 必须存在,并且当作为模块执行的时候,它会先执行,我们就应该把入口函数 main() 定义在 __init__.py 中。当我们使用模块方式 -m 执行的时候, __init__.py 定义了 main() 函数,然后在 __main__.py 中调用它,就能实现我们统一入口的目的。

(2) 案例2
对 __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,调用正常。对比一下它们的输出结果:

[root@localhost test]# python pkg
__main__
('__main__.__name__', '__main__')
('__main__.__package__', '')
('sys.path', ['pkg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])
Traceback (most recent call last):
  File "/usr/lib64/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib64/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/home/lws/python/test_08_10/pkg/__main__.py", line 10, in <module>
    import pkg
ImportError: No module named pkg

[root@localhost test_08_10]# python -m pkg
__init__
('__init__.__name__', 'pkg')
('__init__.__package__', 'pkg')
('sys.path', ['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])
__main__
('__main__.__name__', '__main__')
('__main__.__package__', 'pkg')
('sys.path', ['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])
__init__.main()
[root@localhost test_08_10]# 

原因在于,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 ,结果正常:

[root@localhost test]# python pkg
__main__
('__main__.__name__', '__main__')
('__main__.__package__', '')
('sys.path', ['/home/lws/python/test_08_10/pkg/..', 'pkg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])
__init__
('__init__.__name__', 'pkg')
('__init__.__package__', 'pkg')
('sys.path', ['/home/lws/python/test_08_10/pkg/..', 'pkg', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2',
 '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload',
 '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages'])
__init__.main()
[root@localhost test_08_10]#

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

  1. 为什么不直接在 sys.path 前面加上一个空字符串来解决问题呢?
    答:因为,如果不是在当前路径下调用,空字符串就没效果了,比如执行 python /path/to/pkg。
  2. 为什么不用 if package == ‘’ 来判断 package 的值呢?
    答:这并不是偷懒。因为可能会出现这种调用(不推荐): python pkg/main.py 。而这种情况下, package 的值就是 None 而不是 ‘’ 了。
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值