新聞中心
__main__ —- 最高層級代碼環(huán)境
在 python 中,特殊名稱 __main__ 被用于兩個重要的構造:

創(chuàng)新互聯(lián)公司擁有十余年成都網(wǎng)站建設工作經(jīng)驗,為各大企業(yè)提供網(wǎng)站設計、網(wǎng)站建設服務,對于網(wǎng)頁設計、PC網(wǎng)站建設(電腦版網(wǎng)站建設)、重慶App定制開發(fā)、wap網(wǎng)站建設(手機版網(wǎng)站建設)、程序開發(fā)、網(wǎng)站優(yōu)化(SEO優(yōu)化)、微網(wǎng)站、申請域名等,憑借多年來在互聯(lián)網(wǎng)的打拼,我們在互聯(lián)網(wǎng)網(wǎng)站建設行業(yè)積累了很多網(wǎng)站制作、網(wǎng)站設計、網(wǎng)絡營銷經(jīng)驗,集策劃、開發(fā)、設計、營銷、管理等網(wǎng)站化運作于一體,具備承接各種規(guī)模類型的網(wǎng)站建設項目的能力。
-
程序的最高層級環(huán)境的名稱,可以使用
__name__ == '__main__'表達式來檢查它;以及 -
Python 包中的
__main__.py文件。
這兩種機制都有 Python 模塊有關;用戶如何與它們交互以及它們之間如何交互。 下文將進行詳細說明。 如果你還不了解 Python 模塊,請查看教程 模塊 一節(jié)的介紹。
__name__ == '__main__'
當一個 Python 模塊或包被導入時,__name__ 會被設為模塊的名稱。 通常,這將是 Python 文件本身的名稱去掉 .py 后綴:
>>> import configparser>>> configparser.__name__'configparser'
如果文件是包的組成部分,則 __name__ 還將包括父包的路徑:
>>> from concurrent.futures import process>>> process.__name__'concurrent.futures.process'
不過,如果模塊是在最高層級代碼環(huán)境中執(zhí)行的,則它的 __name__ 會被設為字符串 '__main__'。
什么是“最高層級代碼環(huán)境”?
__main__ 是最高層級代碼運行所在環(huán)境的名稱。 “最高層級代碼”即用戶指定最先啟動運行的 Python 模塊。 它被稱為“最高層級”是因為它將導入程序所需的所有其他模塊。 有時“最高層級代碼”也被稱為應用的 入口點。
最高層級代碼環(huán)境可以是:
-
一個交互提示符的作用域:
>>> __name__'__main__'
-
作為文件參數(shù)傳給 Python 解釋器的 Python 模塊:
$ Python3 helloworld.pyHello, world!
-
作為 -m 參數(shù)傳給 Python 解釋器的 Python 模塊或包:
$ python3 -m tarfileusage: tarfile.py [-h] [-v] (...)
-
Python 解釋器從標準輸入中讀取的 Python 代碼:
$ echo "import this" | python3The Zen of Python, by Tim PetersBeautiful is better than ugly.Explicit is better than implicit....
-
作為 -c 參數(shù)傳遞給 Python 解釋器的 Python 代碼:
$ python3 -c "import this"The Zen of Python, by Tim PetersBeautiful is better than ugly.Explicit is better than implicit....
在以上每個情形中,頂級模塊的 __name__ 被設置為 '__main__' 。
因此,一個模塊可以通過檢查自己的 __name__ ,來發(fā)現(xiàn)它是否在頂層環(huán)境中運行。這是允許在模塊沒有從導入語句中初始化的情況下,有條件地執(zhí)行代碼的一個常見的語句:
if __name__ == '__main__':# Execute when the module is not initialized from an import statement....
參見
關于在所有情況下 __name__ 是如何設置的細節(jié),請看教程部分 模塊 。
常見用法
有些模塊包含了僅供腳本使用的代碼,比如解析命令行參數(shù)或從標準輸入獲取數(shù)據(jù)。 如果這樣的模塊被從不同的模塊中導入,例如為了單元測試,腳本代碼也會無意中執(zhí)行。
這就是 if __name__ == '__main__' 代碼塊的用武之地。除非模塊在頂層環(huán)境中被執(zhí)行,否則該塊內(nèi)的代碼不會運行。
將盡可能少的語句放在下面的 if __name___ == '__main__' 塊中可以提高代碼的清晰度和正確性。最常見的,一個名為 main 的函數(shù)封裝了程序的主要行為:
# echo.pyimport shleximport sysdef echo(phrase: str) -> None:"""A dummy wrapper around print."""# for demonstration purposes, you can imagine that there is some# valuable and reusable logic inside this functionprint(phrase)def main() -> int:"""Echo the input arguments to standard output"""phrase = shlex.join(sys.argv)echo(phrase)return 0if __name__ == '__main__':sys.exit(main()) # next section explains the use of sys.exit
請注意,如果模塊沒有將代碼封裝在 main 函數(shù)內(nèi),而是直接放在 if __name__ == '__main__' 塊內(nèi),那么這個 phrase 變量對整個模塊來說就是全局變量。 這很容易出錯,因為模塊內(nèi)的其他函數(shù)可能會無意中使用全局變量而不是局部名稱。 一個 main 函數(shù)解決了這個問題。
使用 main 函數(shù)有一個額外的好處,就是 echo 函數(shù)本身是孤立的,可以在其他地方導入。當 echo.py 被導入時,echo 和 main 函數(shù)將被定義,但它們都不會被調(diào)用,因為 __name__ != '__main__' 。
打包考量
main 函數(shù)經(jīng)常被用來創(chuàng)建命令行工具,把它們指定為控制臺腳本的入口點。 當這樣做時,pip 將函數(shù)調(diào)用插入到模板腳本中,其中 main 的返回值被傳遞到 sys.exit() 。例如:
sys.exit(main())
由于 main 調(diào)用被包裹在 sys.exit() 中,期望你的函數(shù)將返回一些可被 sys.exit() 作為輸入而接受的值;通常為一個整數(shù)或 None (如果你的函數(shù)沒有返回語句,則隱含返回)。
通過主動遵循這一慣例,我們的模塊在直接運行時(即 python3 echo.py )會有相同的行為,如果我們以后把它打包成可用 pip 安裝的包中的控制臺腳本入口,它也會有相同的行為。
特別的是,要小心從你的 main 函數(shù)中返回字符串。 sys.exit() 將把一個字符串參數(shù)解釋為失敗信息,所以你的程序?qū)⒂幸粋€ 1 的退出代碼,表示失敗。并且這個字符串將被寫入 sys.stderr 。 前面的 echo.py 例子舉例說明了使用 sys.exit(main()) 的約定。
參見
Python 打包用戶指南 包含了一系列關于如何用現(xiàn)代工具分發(fā)和安裝 Python 包的教程和參考資料。
Python 包中的 __main__.py
如果你不熟悉Python包,請參閱本教程的 包 一節(jié)。最常見的是, __main__.py 文件被用來為一個包提供命令行接口。假設有下面這個虛構的包,”bandclass”:
bandclass├── __init__.py├── __main__.py└── student.py
當使用 -m 標志從命令行直接調(diào)用軟件包本身時,將執(zhí)行 __main__.py 。比如說。
$ python3 -m bandclass
這個命令將導致 __main__.py 的運行。你如何利用這一機制將取決于你所編寫的軟件包的性質(zhì),但在這個假設的案例中,允許教師搜索學生可能是有意義的:
# bandclass/__main__.pyimport sysfrom .student import search_studentsstudent_name = sys.argv[2] if len(sys.argv) >= 2 else ''print(f'Found student: {search_students(student_name)}')
注意, from .student import search_students 是一個相對導入的例子。 這種導入方式可以在引用一個包內(nèi)的模塊時使用。 更多細節(jié),請參見教程 模塊 中的 子包參考 一節(jié)。
常見用法
__main__.py 的內(nèi)容通常不是用 if __name__ == '__main__' 區(qū)塊圍起來的。 相反,這些文件保持簡短,功能從其他模塊執(zhí)行。 那些其他模塊可以很容易地進行單元測試,并且可以適當?shù)刂貜褪褂谩?/p>
如果使用,一個 if __name__ == '__main__' 區(qū)塊仍然會像預期的那樣對包內(nèi)的 __main__.py 文件起作用,因為如果導入,它的 __name__ 屬性將包括包的路徑:
>>> import asyncio.__main__>>> asyncio.__main__.__name__'asyncio.__main__'
但這對 .zip 文件的根目錄中的 __main__.py 文件不起作用。 因此,為了保持一致性,像下面提到的 venv 這樣的最小 __main__.py 是首選。
參見
參見 venv 以了解標準庫中最小 __main__.py 的軟件包示例。它不包含一個 if __name__ == '__main__' 塊。你可以用 python3 -m venv [directory] 調(diào)用它。
參見 runpy 以了解更多關于 -m 標志對解釋器可執(zhí)行包的細節(jié)。
參見 zipapp 了解如何運行打包成 .zip 文件的應用程序。在這種情況下,Python 會在歸檔文件的根目錄下尋找一個 __main__.py 文件。
import __main__
不管 Python 程序是用哪個模塊啟動的,在同一程序中運行的其他模塊可以通過導入 __main__ 模塊來導入頂級環(huán)境的范圍 ( namespace )。這并不是導入一個 __main__.py 文件,而是導入使用特殊名稱 '__main__' 的哪個模塊。
下面是一個使用 __main__ 命名空間的模塊的例子:
# namely.pyimport __main__def did_user_define_their_name():return 'my_name' in dir(__main__)def print_user_name():if not did_user_define_their_name():raise ValueError('Define the variable `my_name`!')if '__file__' in dir(__main__):print(__main__.my_name, "found in file", __main__.__file__)else:print(__main__.my_name)
該模塊的用法示例如下:
# start.pyimport sysfrom namely import print_user_name# my_name = "Dinsdale"def main():try:print_user_name()except ValueError as ve:return str(ve)if __name__ == "__main__":sys.exit(main())
現(xiàn)在,如果我們啟動我們的程序,結果會是這樣的:
$ python3 start.pyDefine the variable `my_name`!
該程序的退出代碼為 1 ,表明有錯誤。取消對 my_name = "Dinsdale" 這一行的注釋,就可以修復程序,現(xiàn)在它的退出狀態(tài)代碼為 0 ,表示成功。
$ python3 start.pyDinsdale found in file /path/to/start.py
請注意,導入 __main__ 不會導致無意中運行旨在用于腳本的頂層代碼的問題,這些代碼被放在模塊 start 的 if __name__ == "__main__" 塊中。為什么這樣做?
Python 解釋器啟動時會在 sys.modules 中插入一個空的 __main__ 模塊,并通過運行頂層代碼來填充它。 在我們的例子中這就是 start 模塊,它逐行運行并導入 namely。 反過來,namely 會導入 __main__ (這其實是 start)。 這就是一個導入循環(huán)! 幸運的是,由于部分填充的 __main__ 模塊存在于 sys.modules 中,Python 會將其傳遞給 namely。 請參閱導入系統(tǒng)參考資料中 有關 __main__ 的特別考量 來了解其中的詳情。
Python REPL 是另一個 “頂層環(huán)境 “的例子,所以在 REPL 中定義的任何東西都成為 __main__ 范圍的一部分:
>>> import namely>>> namely.did_user_define_their_name()False>>> namely.print_user_name()Traceback (most recent call last):...ValueError: Define the variable `my_name`!>>> my_name = 'Jabberwocky'>>> namely.did_user_define_their_name()True>>> namely.print_user_name()Jabberwocky
注意,在這種情況下, __main__ 范圍不包含 __file__ 屬性,因為它是交互式的。
__main__ 范圍用于 pdb 和 rlcompleter 的實現(xiàn)。
新聞名稱:創(chuàng)新互聯(lián)Python教程:__main__—-最高層級代碼環(huán)境
標題鏈接:http://m.fisionsoft.com.cn/article/dppepej.html


咨詢
建站咨詢
