深圳网页服务开发与网站建设,北京三大建筑设计院,网站建设 客户要退款,企业推广方式优选隐迅推在命令行中使用 Python 时#xff0c;它可以接收大约 20 个选项(option)#xff0c;语法格式如下#xff1a;python [-bBdEhiIOqsSuvVWx?] [-c command | -m module-name | script | - ] [args]本文想要聊聊比较特殊的“-m”选项#xff1a;关于它的典型用法、原理解析与发… 在命令行中使用 Python 时它可以接收大约 20 个选项(option)语法格式如下python [-bBdEhiIOqsSuvVWx?] [-c command | -m module-name | script | - ] [args]本文想要聊聊比较特殊的“-m”选项关于它的典型用法、原理解析与发展演变的过程。 首先让我们用“--help”来看看它的解释-m mod run library module as a script (terminates option list)mod是“module”的缩写即“-m”选项后面的内容是 module(模块)其作用是把模块当成脚本来运行。“terminates option list”意味着“-m”之后的其它选项不起作用在这点上它跟“-c”是一样的都是“终极选项”。官方把它们定义为“接口选项”(Interface options)需要区别于其它的普通选项或通用选项。-m 选项的五个典型用法Python 中有很多使用 -m 选项的场景相信大家可能会用到或者看见过我在这里想分享 5 个。在 Python3 中只需一行命令就能实现一个简单的 HTTP 服务python -m http.server 8000# 注:在 Python2 中是这样python -m SimpleHTTPServer 8000执行后在本机打开“http://localhost:8000”或者在局域网内的其它机器上打开“http://本机ip:8000”就能访问到执行目录下的内容例如下图就是我本机的内容与此类似我们只需要一行命令“python -m pydoc -p xxx”就能生成 HTML 格式的官方帮助文档可以在浏览器中访问。上面的命令执行了 pydoc 模块会在 9000 端口启动一个 http 服务在浏览器中打开我的结果如下它的第三个常见用法是执行 pdb 的调试命令“python -m pdb xxx.py”以调试模式来执行“xxx.py”脚本第四个同样挺有用的场景是用 timeit 在命令行中测试一小段代码的运行时间。以下的 3 段代码用不同的方式拼接 “0-1-2-……-99” 数字串。可以直观地看出它们的效率差异最后还有一种常常被人忽略的场景“python -m pip install xxx”。我们可能会习惯性地使用“pip install xxx”或者做了版本区分时用“pip3 install xxx”总之不在前面用“python -m”做指定。但这种写法可能会出问题。很巧合的是在本月初(2019.11.01)Python 的核心开发者、第一届指导委员会五人成员之一的 Brett Cannon 专门写了一篇博客《Why you should use python -m pip》提出应该使用“python -m pip”的方式并做了详细的解释。他的主要观点是在存在多个 Python 版本的环境中这种写法可以精确地控制三方库的安装位置。例如用“python3.8 -m pip”可以明确指定给 3.8 版本安装而不会混淆成其它的版本。(延伸阅读关于 Brett 的文章这有一篇简短的归纳《原来我一直安装 Python 库的姿势都不对呀》)-m 选项的两种原理解析看了前面的几种典型用法你是否开始好奇“-m”是怎么运作的它是怎么实现的 对于“python -m name”一句话解释Python 会检索sys.path 查找名字为“name”的模块或者包(含命名空间包)并将其内容当成“__main__”模块来执行。 1、对于普通模块以“.py”为后缀的文件就是一个模块在“-m”之后使用时只需要使用模块名不需要写出后缀但前提是该模块名是有效的且不能是用 C 语言写成的模块。在“-m”之后如果是一个无效的模块名则会报错“No module named xxx”。如果是一个带后缀的模块则首先会导入该模块然后可能报错Error while finding module specification for xxx.py (AttributeError: module xxx has no attribute __path__。对于一个普通模块有时候这两种写法表面看起来是等效的两种写法都会把定位到的模块脚本当成主程序入口来执行即在执行时该脚本的__name__都是”__main__“跟 import 导入方式是不同的。但它的前提是在执行目录中存在着“test.py”且只有唯一的“test”模块。对于本例如果换一个目录执行的话“python test.py”当然会报找不到文件的错误然而“python -m test”却不会报错因为解释器在遍历sys.path时可以找到同名的“test”模块并且执行由此差异我们其实可以总结出“-m”的用法已知一个模块的名字但不知道它的文件路径那么使用“-m”就意味着交给解释器自行查找若找到则当成脚本执行。 以前文的“python -m http.server 8000”为例我们也可以找到“server”模块的绝对路径然后执行尽管这样会变得很麻烦。那么“-m”方式与直接运行脚本相比在实现上有什么不同呢直接运行脚本时相当于给出了脚本的完整路径(不管是绝对路径还是相对路径)解释器根据文件系统的查找机制 定位到该脚本然后执行使用“-m”方式时解释器需要在不 import 的情况下在所有模块命名空间 中查找定位到脚本的路径然后执行。为了实现这个过程解释器会借助两个模块pkgutil 和 runpy前者用来获取所有的模块列表后者根据模块名来定位并执行脚本2、对于包内模块如果“-m”之后要执行的是一个包那么解释器经过前面提到的查找过程先定位到该包然后会去执行它的“__main__”子模块也就是说在包目录下需要实现一个“__main__.py”文件。换句话说假设有个包的名称是“pname”那么“python -m pname”其实就等效于“python -m pname.__main__”。 仍以前文创建 HTTP 服务为例“http”是 Python 内置的一个包它没有“__main__.py”文件所以使用“-m”方式执行时就会报错No module named http.__main__; http is a package and cannot be directly executed。作为对比我们可以看看前文提到的 pip它也是一个包为什么“python -m pip”的方式可以使用呢当然是因为它有“__main__.py”文件“python -m pip”实际上执行的就是这个“__main__.py”文件它主要作为一个调用入口调用了核心的pip._internal.main。http 包因为没有一个统一的入口模块所以采用了“python -m 包.模块”的方式而 pip 包因为有统一的入口模块所以加了一个“__main__.py”文件最后只需要写“python -m 包”简明直观。-m 选项的十年演变过程最早引入 -m 选项的是 Python 2.4 版本(2004年)当时功能还挺受限只能作用于普通的内置模块(如 pdb 和 profile)。随后知名开发者 Nick Coghlan 提出的《PEP 338 -- Executing modules as scripts》把它的功能提升了一个台阶。这个 PEP 在 2004 年提出最终实现在 2006 年的 2.5 版本。(插个题外话Nick Coghlan 是核心开发者中的核心之一也是第一届指导委员会的五人成员之一。记得当初看材料他是在 2005 年被选为核心开发者的这时间与 PEP-338 的时间紧密贴合)这个 PEP 的几个核心点是结合了 PEP-302 的新探针机制(new import hooks)提升了解释器查找包内模块的能力结合了其它的导入机制(例如zipimport和冻结模块(frozen modules))拓展了解释器查找模块的范围与精度开发了新的runpy.run_module(modulename)来实现本功能而不用修改 CPython 解释器如此可方便移植到其它解释器至此-m 选项使得 Python 可以在所有的命名空间内定位到命令行中给定的模块。2009 年在 Python 3.1 版本中只需给定包的名称就能定位和运行它的“__main__”子模块。2014 年-m 扩展到支持命名空间包。至此经过十年的发展演变-m 选项变得功能齐全羽翼丰满。最后我们来个 ending 吧-m 选项可能看似不起眼但它绝对是最特别的选项之一它使得在命令行中使用内置模块、标准包与三方库时变得更轻松便利。有机会就多用一下吧体会它带来的愉悦体验。参考材料https://docs.python.org/3.7/using/cmdline.html#cmdoption-mhttps://snarky.ca/why-you-should-use-python-m-piphttps://www.python.org/dev/peps/pep-0338https://blog.csdn.net/jian3x/article/details/89556592作者豌豆花下猫 来源Python猫