python语言原理_Python学习:“-m”选项典型用法和原理解析

Python `-m` 选项允许将模块作为脚本运行,它从sys.path中查找模块并在必要时执行`__main__.py`。常见用途包括启动HTTP服务器、生成文档、调试和性能测试。该选项始于Python 2.4,通过PEP 338增强,支持执行包内的`__main__.py`。它的演变使得在命令行中使用内置模块和第三方库更加便捷。
摘要由CSDN通过智能技术生成

在命令行中使用Python时,它可以接收大约20个选项(opon),语法格式如下:python[-bBdEhiIOqsSuvVWx?][-ccommand|-mmodule-name|script|-][args]

本文想要聊聊比较特殊的“-m”选项:关于它的典型用法、原理解析与发展演变的过程。

首先,让我们用“--help”来看看它的解释:

o4YBAF9t99eACn2rAAGIxvWJw5M621.jpg

-mmodrunlibrarymoduleasascript(terminatesopTIonlist)

"mod"是“module”的缩写,即“-m”选项后面的内容是module(模块),其作用是把模块当成脚本来运行。

“terminatesopTIonlist”意味着“-m”之后的其它选项不起作用,在这点上它跟“-c”是一样的,都是“终极选项”。官方把它们定义为“接口选项”(InterfaceopTIons),需要区别于其它的普通选项或通用选项。

-m选项的五个典型用法

Python中有很多使用-m选项的场景,相信大家可能会用到或者看见过,我在这里想分享5个。

在Python3中,只需一行命令就能实现一个简单的HTTP服务:python-mhttp.server8000#注:在Python2中是这样python-mSimpleHTTPServer8000

pIYBAF9t99iADZ1eAAAwYY1-ws4580.jpg

执行后,在本机打开“http://localhost:8000”,或者在局域网内的其它机器上打开“http://本机ip:8000”,就能访问到执行目录下的内容,例如下图就是我本机的内容:

pIYBAF9t99mAJ6BFAABv2xFWe_0125.jpg

与此类似,我们只需要一行命令“python-mpydoc-pxxx”,就能生成HTML格式的官方帮助文档,可以在浏览器中访问。

o4YBAF9t99qAQRUeAAA8Q-TPEho910.jpg

上面的命令执行了pydoc模块,会在9000端口启动一个http服务,在浏览器中打开,我的结果如下:

pIYBAF9t99yARYwpAAEYBaOOqD0997.jpg

它的第三个常见用法是执行pdb的调试命令“python-mpdbxxx.py”,以调试模式来执行“xxx.py”脚本:

pIYBAF9t992AWkavAABOby3LjC0755.jpg

第四个同样挺有用的场景是用timeit在命令行中测试一小段代码的运行时间。以下的3段代码,用不同的方式拼接“0-1-2-……-99”数字串。可以直观地看出它们的效率差异:

o4YBAF9t99-AdPReAACEMsp-rLI604.jpg

最后,还有一种常常被人忽略的场景:“python-mpipinstallxxx”。我们可能会习惯性地使用“pipinstallxxx”,或者做了版本区分时用“pip3installxxx”,总之不在前面用“python-m”做指定。但这种写法可能会出问题。

很巧合的是,在本月初(2019.11.01),Python的核心开发者、第一届指导委员会五人成员之一的BrettCannon专门写了一篇博客《Whyyoushoulduse"python-mpip"》,提出应该使用“python-mpip”的方式,并做了详细的解释。

他的主要观点是:在存在多个Python版本的环境中,这种写法可以精确地控制三方库的安装位置。例如用“python3.8-mpip”,可以明确指定给3.8版本安装,而不会混淆成其它的版本。

-m选项的两种原理解析

看了前面的几种典型用法,你是否开始好奇:“-m”是怎么运作的?它是怎么实现的?

对于“python-mname”,一句话解释:Python会检索sys.path,查找名字为“name”的模块或者包(含命名空间包),并将其内容当成“__main__”模块来执行。

1、对于普通模块

以“.py”为后缀的文件就是一个模块,在“-m”之后使用时,只需要使用模块名,不需要写出后缀,但前提是该模块名是有效的,且不能是用C语言写成的模块。

在“-m”之后,如果是一个无效的模块名,则会报错“Nomodulenamedxxx”。

如果是一个带后缀的模块,则首先会导入该模块,然后可能报错:Errorwhilefindingmodulespecificationfor'xxx.py'(AttributeError:module'xxx'hasnoattribute'__path__'。

pIYBAF9t9-CAPrtoAACAENrDUbA549.jpg

对于一个普通模块,有时候这两种写法表面看起来是等效的:

o4YBAF9t9-GAAs1pAAA4zBY_e2U664.jpg

两种写法都会把定位到的模块脚本当成主程序入口来执行,即在执行时,该脚本的__name__都是”__main__“,跟import导入方式是不同的。

但它的前提是:在执行目录中存在着“test.py”,且只有唯一的“test”模块。对于本例,如果换一个目录执行的话,“pythontest.py”当然会报找不到文件的错误,然而,“python-mtest”却不会报错,因为解释器在遍历sys.path时可以找到同名的“test”模块,并且执行:

pIYBAF9t9-OASVuLAAFBsKZJ4PM493.jpg

由此差异,我们其实可以总结出“-m”的用法:已知一个模块的名字,但不知道它的文件路径,那么使用“-m”就意味着交给解释器自行查找,若找到,则当成脚本执行。

以前文的“python-mhttp.server8000”为例,我们也可以找到“server”模块的绝对路径,然后执行,尽管这样会变得很麻烦。

o4YBAF9t9-SAEuqdAAA3in8QDIY109.jpg

那么,“-m”方式与直接运行脚本相比,在实现上有什么不同呢?直接运行脚本时,相当于给出了脚本的完整路径(不管是绝对路径还是相对路径),解释器根据文件系统的查找机制,定位到该脚本,然后执行

使用“-m”方式时,解释器需要在不import的情况下,在所有模块命名空间中查找,定位到脚本的路径,然后执行。为了实现这个过程,解释器会借助两个模块:pkgutil和runpy,前者用来获取所有的模块列表,后者根据模块名来定位并执行脚本

2、对于包内模块

如果“-m”之后要执行的是一个包,那么解释器经过前面提到的查找过程,先定位到该包,然后会去执行它的“__main__”子模块,也就是说,在包目录下需要实现一个“__main__.py”文件。

换句话说,假设有个包的名称是“pname”,那么,“python-mpname”,其实就等效于“python-mpname.__main__”。

仍以前文创建HTTP服务为例,“http”是Python内置的一个包,它没有“__main__.py”文件,所以使用“-m”方式执行时,就会报错:Nomodulenamedhttp.__main__;'http'isapackageandcannotbedirectlyexecuted。

pIYBAF9t9-WAVnOFAABWyHJP8vg664.jpg

作为对比,我们可以看看前文提到的pip,它也是一个包,为什么“python-mpip”的方式可以使用呢?当然是因为它有“__main__.py”文件:

pIYBAF9t9-eAN1siAADb6dSowp4391.jpg

“python-mpip”实际上执行的就是这个“__main__.py”文件,它主要作为一个调用入口,调用了核心的"pip._internal.main"。

http包因为没有一个统一的入口模块,所以采用了“python-m包.模块”的方式,而pip包因为有统一的入口模块,所以加了一个“__main__.py”文件,最后只需要写“python-m包”,简明直观。

-m选项的十年演变过程

最早引入-m选项的是Python2.4版本(2004年),当时功能还挺受限,只能作用于普通的内置模块(如pdb和profile)。

随后,知名开发者NickCoghlan提出的《PEP338--Executingmodulesasscripts》把它的功能提升了一个台阶。这个PEP在2004年提出,最终实现在2006年的2.5版本。

(插个题外话:NickCoghlan是核心开发者中的核心之一,也是第一届指导委员会的五人成员之一。记得当初看材料,他是在2005年被选为核心开发者的,这时间与PEP-338的时间紧密贴合)

o4YBAF9t9-iAFvqJAACT1D_TXAo207.jpg

这个PEP的几个核心点是:结合了PEP-302的新探针机制(newimporthooks),提升了解释器查找包内模块的能力

结合了其它的导入机制(例如zipimport和冻结模块(frozenmodules)),拓展了解释器查找模块的范围与精度

开发了新的runpy.run_module(modulename)来实现本功能,而不用修改CPython解释器,如此可方便移植到其它解释器

至此,-m选项使得Python可以在所有的命名空间内定位到命令行中给定的模块。

2009年,在Python3.1版本中,只需给定包的名称,就能定位和运行它的“__main__”子模块。2014年,-m扩展到支持命名空间包。

至此,经过十年的发展演变,-m选项变得功能齐全,羽翼丰满。

最后,我们来个ending吧:-m选项可能看似不起眼,但它绝对是最特别的选项之一,它使得在命令行中,使用内置模块、标准包与三方库时变得更轻松便利。有机会就多用一下吧,体会它带来的愉悦体验。

编辑:hfy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值