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