Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

Python2年前 (2022)发布 safedragon
124 0

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

2.11 极简模式执行 Python Shell

在终端输入 Python 就会进入 Python Shell 。
方便是挺方便,就是有点说不出的难受,谁能告诉我,为什么要多出这么大一段无关的内容。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

这有点像,你上爱某艺看视频吧,都要先看个 90 秒的广告。
如果你和我一样不喜欢这种 『牛皮癣』,那么可以加个 -q 参数,静默进入 Python Shell,就像下面这样子,开启了极简模式,舒服多了。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

2.12 在执行任意代码前自动念一段平安经

最近的“平安经”可谓是引起了不小的风波啊。
作为一个正儿八经的程序员,最害怕的就是自己的代码上线出现各种各样的 BUG。
为此明哥就研究了一下,如何在你执行任意 Python 代码前,让 Python 解释器自动念上一段平安经,保佑代码不出 BUG 。
没想到还真被我研究出来了
做好心理准备了嘛?
我要开始作妖了,噢不,是开始念经了。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

感谢佛祖保佑,Everything is ok,No bugs in the code.
你一定很想知道这是如何的吧?
如果你对 Linux 比较熟悉,就会知道,当你在使用 SSH 远程登陆 Linux 服务器的时候?会读取 .bash_profile 文件加载一些环境变量。
.bash_profile 你可以视其为一个 shell 脚本,可以在这里写一些 shell 代码达到你的定制化需求。
而在 Python 中,也有类似 .bash_profile 的文件,这个文件一般情况下是不存在的。
我们需要新建一个用户环境目录,这个目录比较长,不需要你死记硬背,使用 site 模块的方法就可以获取,然后使用 mkdir -p 命令创建它。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

在这个目录下,新建一个 usercustomize.py 文件,注意名字必须是这个,换成其他的可就识别不到啦。
这个 usercustomize.py 的内容如下(明哥注:佛祖只保佑几个 Python 的主要应用方向,毕竟咱是 Python 攻城狮嘛…)

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

这个文件我放在了我的 github 上,你可以点此前往获取。
一切都完成后,无论你是使用 python xxx.py 执行脚本

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

还是使用 python 进入 Python Shell ,都会先念一下平安经保平安。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

2.13 启动 Python Shell 前自动执行某脚本

前一节我们介绍了一种,只要运行解释器就会自动触发执行 Python 脚本的方法。

除此之外,可还有其他方法呢?
当然是有,只不过相对来说,会麻烦一点了。
先来看一下效果,在 ~/Library/Python/3.9/lib/python/site-packages 目录下并没有usercustomize.py 文件,但是在执行 python 进入 Python Shell 模式后,还是会打印平安经。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

这是如何做到的呢?
很简单,只要做两件事
第一件事,在任意你喜欢的目录下,新建 一个Python 脚本,名字也随意,比如我叫startup.py ,内容还是和上面一样

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

第二件事,设置一个环境变量 PYTHONSTARTUP,指向你的脚本路径

$ export PYTHONSTARTUP=/Users/MING/startup.py

这样就可以了。
但是这种方法只适用于 Python Shell ,只不适合 Python 执行脚本的方法。

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

如果要在脚本中实现这种效果,我目前想到最粗糙我笨拙的方法了 – 手动加载执行

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

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 的入口?

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

再看一下 json.tool 的源码文件,json 库下面却没有__main__.py 的文件。

不对,再回过头看,我们调用的不是 json 库,而是 json 库下的 tool 模块。
查看 tool 模块的源代码,有一个名为 main 的函数Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

但它这不是关键,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.module> 的简写形式。

3. 实践一下

先把当前路径设置追加到 PATH 的环境变量中

$ export PATH=${PATH}:`pwd`

先来验证一下第一种方法。
然后在当前目录下新建一个 demo 文件夹,并且在 demo 目录下新建一个 __main__.py 的文件,随便打印点东西

# __main__.py
print("hello, world")

然后直接执行如下命令,立马就能看到效果。

$ python3 -m demo
hello,world

执行过程如下:

Python黑魔法手册 2.0 文档第二章魔法命令行【11-13】

再来验证一下使用第二种方法。
在 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"
}

如此一对比,哪个更方便?你心里应该有数了。

© 版权声明

相关文章

暂无评论

暂无评论...