2.14 把模块当做脚本来执行 7 种方法及原理¶
1. 用法举例¶
前面的文章里其实分享过不少类似的用法。比如:
1、 快速搭建一个 HTTP 服务
# python2
$ python -m SimpleHTTPServer 8888
# python3
$ python3 -m http.server 8888
2、快速构建 HTML 帮助文档
$ python -m pydoc -p 5200
3、快速进入 pdb 调试模式
$ python -m pdb demo.py
4、最优雅且正确的包安装方法
$ python3 -m pip install requests
5、快速美化 JSON 字符串
$ echo '{"name": "MING"}' | python -m json.tool
6、快速打印包的搜索路径
$ python -m site
7、用于快速计算程序执行时长
$ python3 -m timeit '"-".join(map(str, range(100)))'
2. 原理剖析¶
上面的诸多命令,都有一个特点,在命令中都含有 -m
参数选项,而参数的值,SimpleHTTPServer, http.server, pydoc,pdb,pip,
json.tool,site ,timeit这些都是模块或者包。
通常来说模块或者包,都是用做工具包由其他模块导入使用,而很少直接使用命令来执行(脚本除外)。
Python 给我们提供了一种方法,可以让我们将模块里的部分功能抽取出来,直接用于命令行式的调用。效果就是前面你所看到的。
那这是如何实现的呢?
最好的学习方式,莫过于模仿,直接以 pip 和 json 模块为学习对象,看看目录结构和代码都有什么特点。
先看一下 pip
的源码目录,发现在其下有一个 __main__.py
的文件,难道这是 -m
的入口?
再看一下 json.tool
的源码文件,json 库下面却没有 __main__.py
的文件。
这就很奇怪了。
不对,再回过头看,我们调用的不是 json 库,而是 json 库下的 tool 模块。
查看 tool 模块的源代码,有一个名为 main 的函数
但它这不是关键,main 函数是在模块中直接被调用的。
只有当 __name__
为 __main___
时,main 函数才会被调用
if __name__ == '__main__':
main()
当模块被导入时,__name__
的值为模块名,
而当模块被直接执行,__name__
的值就变成了 __main__
。
这下思路清晰了。
想要使用 -m
的方式执行模块,有两种方式:
第一种:以
-m <package>
的方式执行,只要在 package 下写一个__main__.py
的文件即可。第二种:以
-m <package.module>
的方式执行,只要在 module 的代码中,定义一个 main 函数,然后在最外层写入下面这段固定的代码
if __name__ == '__main__':
main()
上面我将 -m
的使用情况分为两种,但是实际上,只有一种,对于第一种,你完全可以将
-m <package>
理解为 -m <package.__main__>
的简写形式。
3. 实践一下¶
先把当前路径设置追加到 PATH 的环境变量中
$ export PATH=${PATH}:`pwd`
先来验证一下第一种方法。
然后在当前目录下新建一个demo
文件夹,并且在 demo 目录下新建一个
__main__.py
的文件,随便打印点东西
# __main__.py
print("hello, world")
然后直接执行如下命令,立马就能看到效果。
$ python3 -m demo
hello,world
执行过程如下:
再来验证一下使用第二种方法。
在 demo 目录下再新建一个 foobar.py 文件
# foobar.py
def main():
print("hello, world")
if __name__ == "__main__":
main()
最后执行一下如下命令,输出与预期相符
$ python3 -m demo.foobar
hello, foobar
4. -m 存在的意义¶
-m
实现的效果,无异于直接执行一个 Python 模块/脚本。
那么问题就来了,那我直接执行不就行啦,何必多此一举再加个 -m
呢?
这个问题很有意思,值得一提。
当我们使用一个模块的时候,往往只需要记住模块名,然后使用 import 去导入它就行了。
之所以能这么便利,这得益于 Python 完善的导入机制,你完全不需要知道这个模块文件存在哪个目录下,它的绝对路径是什么?因为 Python 的包导入机制会帮你做这些事情。
换句话说,如果你不使用 -m
的方式,当你要使用
python -m json.tool
,你就得这样子写
$ echo '{"name": "MING"}' | python /usr/lib64/python2.7/json/tool.py
{
"name": "MING"
}
如此一对比,哪个更方便?你心里应该有数了。