<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 中 -m 的典型用法、原理解析與發(fā)展演變

          共 4024字,需瀏覽 9分鐘

           ·

          2019-11-11 23:21


          d40d5b3115bcd05230f49a050c0ae842.webp

          在命令行中使用 Python 時(shí),它可以接收大約 20 個(gè)選項(xiàng)(option),語法格式如下:

          python?[-bBdEhiIOqsSuvVWx?]?[-c?command?|?-m?module-name?|?script?|?-?]?[args]

          本文想要聊聊比較特殊的“-m”選項(xiàng):關(guān)于它的典型用法、原理解析與發(fā)展演變的過程。

          首先,讓我們用“--help”來看看它的解釋:

          11e9877a4be3feee68f9a0f4d98d89fb.webp

          -m ?mod run library module as a script (terminates option list)

          "mod"是“module”的縮寫,即“-m”選項(xiàng)后面的內(nèi)容是 module(模塊),其作用是把模塊當(dāng)成腳本來運(yùn)行。

          “terminates option list”意味著“-m”之后的其它選項(xiàng)不起作用,在這點(diǎn)上它跟“-c”是一樣的,都是“終極選項(xiàng)”。官方把它們定義為“接口選項(xiàng)”(Interface options),需要區(qū)別于其它的普通選項(xiàng)或通用選項(xiàng)。

          -m 選項(xiàng)的五個(gè)典型用法

          Python 中有很多使用 -m 選項(xiàng)的場景,相信大家可能會(huì)用到或者看見過,我在這里想分享 5 個(gè)。

          在 Python3 中,只需一行命令就能實(shí)現(xiàn)一個(gè)簡單的 HTTP 服務(wù):

          python?-m?http.server?8000

          #?注:在?Python2?中是這樣
          python?-m?SimpleHTTPServer?8000
          a0e446a947baa447b6532b3be353baef.webp

          執(zhí)行后,在本機(jī)打開“http://localhost:8000”,或者在局域網(wǎng)內(nèi)的其它機(jī)器上打開“http://本機(jī)ip:8000”,就能訪問到執(zhí)行目錄下的內(nèi)容,例如下圖就是我本機(jī)的內(nèi)容:

          1cc60365c16204e09bec412ed0bf69a8.webp

          與此類似,我們只需要一行命令“python -m pydoc -p xxx”,就能生成 HTML 格式的官方幫助文檔,可以在瀏覽器中訪問。

          b524fb5e057cfe15f0d72d63dc9d4d42.webp

          上面的命令執(zhí)行了 pydoc 模塊,會(huì)在 9000 端口啟動(dòng)一個(gè) http 服務(wù),在瀏覽器中打開,我的結(jié)果如下:

          0980d673d403b8aad6b93658a1ce76bf.webp

          它的第三個(gè)常見用法是執(zhí)行 pdb 的調(diào)試命令“python -m pdb xxx.py”,以調(diào)試模式來執(zhí)行“xxx.py”腳本:

          0d6c1f2ec29ee61682355f4689ca60de.webp

          第四個(gè)同樣挺有用的場景是用 timeit 在命令行中測試一小段代碼的運(yùn)行時(shí)間。以下的 3 段代碼,用不同的方式拼接 “0-1-2-……-99” 數(shù)字串。可以直觀地看出它們的效率差異:

          31933e13984c34057656d9ad1ed20193.webp

          最后,還有一種常常被人忽略的場景:“python -m pip install xxx”。我們可能會(huì)習(xí)慣性地使用“pip install xxx”,或者做了版本區(qū)分時(shí)用“pip3 install xxx”,總之不在前面用“python -m”做指定。但這種寫法可能會(huì)出問題。

          很巧合的是,在本月初(2019.11.01),Python 的核心開發(fā)者、第一屆指導(dǎo)委員會(huì)五人成員之一的 Brett Cannon 專門寫了一篇博客《Why you should use "python -m pip"》,提出應(yīng)該使用“python -m pip”的方式,并做了詳細(xì)的解釋。

          他的主要觀點(diǎn)是:在存在多個(gè) Python 版本的環(huán)境中,這種寫法可以精確地控制三方庫的安裝位置。例如用“python3.8 -m pip”,可以明確指定給 3.8 版本安裝,而不會(huì)混淆成其它的版本。

          (延伸閱讀:關(guān)于 Brett 的文章,這有一篇簡短的歸納原來我一直安裝 Python 庫的姿勢都不對(duì)呀!》)

          -m 選項(xiàng)的兩種原理解析

          看了前面的幾種典型用法,你是否開始好奇:“-m”是怎么運(yùn)作的?它是怎么實(shí)現(xiàn)的?

          對(duì)于“python -m name”,一句話解釋:Python 會(huì)檢索sys.path ,查找名字為“name”的模塊或者包(含命名空間包),并將其內(nèi)容當(dāng)成“__main__”模塊來執(zhí)行。

          1、對(duì)于普通模塊

          以“.py”為后綴的文件就是一個(gè)模塊,在“-m”之后使用時(shí),只需要使用模塊名,不需要寫出后綴,但前提是該模塊名是有效的,且不能是用 C 語言寫成的模塊。

          在“-m”之后,如果是一個(gè)無效的模塊名,則會(huì)報(bào)錯(cuò)“No module named xxx”。

          如果是一個(gè)帶后綴的模塊,則首先會(huì)導(dǎo)入該模塊,然后可能報(bào)錯(cuò):Error while finding module specification for 'xxx.py' (AttributeError: module 'xxx' has no attribute '__path__'。

          bc6a1d2ea00816952c2266e86f2e362f.webp

          對(duì)于一個(gè)普通模塊,有時(shí)候這兩種寫法表面看起來是等效的:

          ee731ef98cefc1caa55f61fc8d8a0d91.webp

          兩種寫法都會(huì)把定位到的模塊腳本當(dāng)成主程序入口來執(zhí)行,即在執(zhí)行時(shí),該腳本的__name__都是”__main__“,跟 import 導(dǎo)入方式是不同的。

          但它的前提是:在執(zhí)行目錄中存在著“test.py”,且只有唯一的“test”模塊。對(duì)于本例,如果換一個(gè)目錄執(zhí)行的話,“python test.py”當(dāng)然會(huì)報(bào)找不到文件的錯(cuò)誤,然而,“python -m test”卻不會(huì)報(bào)錯(cuò),因?yàn)榻忉屍髟诒闅vsys.path時(shí)可以找到同名的“test”模塊,并且執(zhí)行:

          95c4a5e0fe7d7eaa4020dead79366447.webp

          由此差異,我們其實(shí)可以總結(jié)出“-m”的用法:已知一個(gè)模塊的名字,但不知道它的文件路徑,那么使用“-m”就意味著交給解釋器自行查找,若找到,則當(dāng)成腳本執(zhí)行。

          以前文的“python -m http.server 8000”為例,我們也可以找到“server”模塊的絕對(duì)路徑,然后執(zhí)行,盡管這樣會(huì)變得很麻煩。

          18a2011bdb84ca71544bc2059ab075f9.webp

          那么,“-m”方式與直接運(yùn)行腳本相比,在實(shí)現(xiàn)上有什么不同呢?

          • 直接運(yùn)行腳本時(shí),相當(dāng)于給出了腳本的完整路徑(不管是絕對(duì)路徑還是相對(duì)路徑),解釋器根據(jù)文件系統(tǒng)的查找機(jī)制, 定位到該腳本,然后執(zhí)行
          • 使用“-m”方式時(shí),解釋器需要在不 import 的情況下,在所有模塊命名空間 中查找,定位到腳本的路徑,然后執(zhí)行。為了實(shí)現(xiàn)這個(gè)過程,解釋器會(huì)借助兩個(gè)模塊:pkgutilrunpy,前者用來獲取所有的模塊列表,后者根據(jù)模塊名來定位并執(zhí)行腳本

          2、對(duì)于包內(nèi)模塊

          如果“-m”之后要執(zhí)行的是一個(gè)包,那么解釋器經(jīng)過前面提到的查找過程,先定位到該包,然后會(huì)去執(zhí)行它的“__main__”子模塊,也就是說,在包目錄下需要實(shí)現(xiàn)一個(gè)“__main__.py”文件。

          換句話說,假設(shè)有個(gè)包的名稱是“pname”,那么,“python -m pname”,其實(shí)就等效于“python -m pname.__main__”。

          仍以前文創(chuàng)建 HTTP 服務(wù)為例,“http”是 Python 內(nèi)置的一個(gè)包,它沒有“__main__.py”文件,所以使用“-m”方式執(zhí)行時(shí),就會(huì)報(bào)錯(cuò):No module named http.__main__; 'http' is a package and cannot be directly executed。

          199db969f73f4aab6f2128e42a646522.webp

          作為對(duì)比,我們可以看看前文提到的 pip,它也是一個(gè)包,為什么“python -m pip”的方式可以使用呢?當(dāng)然是因?yàn)樗小癬_main__.py”文件:

          0c01ed59ef355651cc43e71d901da768.webp

          “python -m pip”實(shí)際上執(zhí)行的就是這個(gè)“__main__.py”文件,它主要作為一個(gè)調(diào)用入口,調(diào)用了核心的"pip._internal.main"。

          http 包因?yàn)闆]有一個(gè)統(tǒng)一的入口模塊,所以采用了“python -m 包.模塊”的方式,而 pip 包因?yàn)橛薪y(tǒng)一的入口模塊,所以加了一個(gè)“__main__.py”文件,最后只需要寫“python -m 包”,簡明直觀。

          -m 選項(xiàng)的十年演變過程

          最早引入 -m 選項(xiàng)的是 Python 2.4 版本(2004年),當(dāng)時(shí)功能還挺受限,只能作用于普通的內(nèi)置模塊(如 pdb 和 profile)。

          隨后,知名開發(fā)者 Nick Coghlan 提出的《PEP 338 -- Executing modules as scripts》把它的功能提升了一個(gè)臺(tái)階。這個(gè) PEP 在 2004 年提出,最終實(shí)現(xiàn)在 2006 年的 2.5 版本。

          (插個(gè)題外話:Nick Coghlan 是核心開發(fā)者中的核心之一,也是第一屆指導(dǎo)委員會(huì)的五人成員之一。記得當(dāng)初看材料,他是在 2005 年被選為核心開發(fā)者的,這時(shí)間與 PEP-338 的時(shí)間緊密貼合)

          d29d9662c5e55b1b2cb03607b5c21a0c.webp

          這個(gè) PEP 的幾個(gè)核心點(diǎn)是:

          • 結(jié)合了 PEP-302 的新探針機(jī)制(new import hooks),提升了解釋器查找包內(nèi)模塊的能力

          • 結(jié)合了其它的導(dǎo)入機(jī)制(例如zipimport和凍結(jié)模塊(frozen modules)),拓展了解釋器查找模塊的范圍與精度

          • 開發(fā)了新的runpy.run_module(modulename)來實(shí)現(xiàn)本功能,而不用修改 CPython 解釋器,如此可方便移植到其它解釋器

          至此,-m 選項(xiàng)使得 Python 可以在所有的命名空間內(nèi)定位到命令行中給定的模塊。

          2009 年,在 Python 3.1 版本中,只需給定包的名稱,就能定位和運(yùn)行它的“__main__”子模塊。2014 年,-m 擴(kuò)展到支持命名空間包。

          至此,經(jīng)過十年的發(fā)展演變,-m 選項(xiàng)變得功能齊全,羽翼豐滿。

          最后,我們來個(gè) ending 吧:-m 選項(xiàng)可能看似不起眼,但它絕對(duì)是最特別的選項(xiàng)之一,它使得在命令行中,使用內(nèi)置模塊、標(biāo)準(zhǔn)包與三方庫時(shí)變得更輕松便利。有機(jī)會(huì)就多用一下吧,體會(huì)它帶來的愉悅體驗(yàn)。

          參考材料

          https://docs.python.org/3.7/using/cmdline.html#cmdoption-m

          https://snarky.ca/why-you-should-use-python-m-pip

          https://www.python.org/dev/peps/pep-0338

          https://blog.csdn.net/jian3x/article/details/89556592
          覺得不錯(cuò),點(diǎn)個(gè)在看唄!
          瀏覽 125
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  小黄片视频 | 欧美黄色网址 | 91精品综合久久久久久五月丁香 | 欧美一级在线视频 | 中文字幕欧美在线 |