我發(fā)現(xiàn)有不少朋友寫 Python 腳本非常隨意留量,要么不用函數(shù)窄赋,要么函數(shù)隨處定義,反正第一眼看不出要執(zhí)行的第一行代碼位于何處楼熄,這樣的腳本可讀性很差忆绰,而且容易隱藏 bug,解決這個問題很簡單可岂,當(dāng)我們寫 Python 腳本時错敢,一定要加上這個:
def main():
# do something
print("do something.")
if __name__ == "__main__":
main()
你可能要反對了:我怎么爽就怎么寫,憑什么聽你的缕粹,多寫個 if name...?
別急稚茅,讓我說三個原因。
第一平斩,它讓 Python 文件的作用更加明確
首先需要明白 name 的作用亚享,當(dāng)腳本直接被 Python 解釋器執(zhí)行時,其值就是 "main"绘面,當(dāng)其被其他 Python 程序 import 的時候欺税,其值就是對應(yīng)的 Python 腳本文件名,可以在 Python 解釋器驗證下揭璃,假定有個 some_script.py 其內(nèi)容如下:
print("some_script.py")
print(__name__)
在 Python 解釋器導(dǎo)入一下:
? vim some_script.py
? python
Python 3.8.5 (v3.8.5:580fbb018f, Jul 20 2020, 12:11:27)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import some_script
some_script.py
some_script
>>>
可以看到晚凿,name 的值就是 Python 腳本的文件名 some_script。
也就是說 if name == "main": 后面的代碼在 import 的時候是不會運(yùn)行的瘦馍。
明白了這一點歼秽,if name == "main": 就可以做為區(qū)分腳本和庫的一個標(biāo)志,當(dāng)我們看到 if name == "main": 時情组,就認(rèn)為這一個可以直接運(yùn)行的腳本哲银,當(dāng)沒有看到這行代碼時,就認(rèn)為這是一個庫呻惕,可以被其他程序引用荆责,Explicit is better than implicit.,不是嗎亚脆?
再舉個例子:
假如你寫了一個不帶if name == "main": 的腳本做院,叫 bad_script.py,內(nèi)容如下:
def useful_function(x):
return x * x
class UsefulClass:
def __init__(self, x):
self.x = x
#你自己測試了一吧,沒毛病
for i in range(7):
print(useful_function(i))
別人寫了個 useful.py键耕,引用了你的 useful_function:
from bad_script import useful_function
def main():
print(f'{useful_function(3)=}')
if __name__ == '__main__':
main()
一運(yùn)行寺滚,發(fā)現(xiàn)打印了不可預(yù)期的內(nèi)容,見下圖紅色部分:
查了半天原因屈雄,發(fā)現(xiàn)是你的腳本輸出的村视,你說別人會不會罵你?
假如你在自己腳本里定義了全局變量酒奶,別人如果在不合適的位置導(dǎo)入了 *蚁孔,就會把你這個全局變量也導(dǎo)入,導(dǎo)致變量覆蓋惋嚎,很容易會出現(xiàn) bug杠氢。
第二,它讓 Python 文件更加易讀另伍,對 IDE 友好
有了 if name == "main": 相當(dāng)于 Python 程序也有了一個入口函數(shù)鼻百,所有的變量都從這里開始定義和使用,我們可以清晰的知道程序的邏輯開始于何處(當(dāng)然還需要我們自覺的把程序的開始邏輯都放在這里)
其實摆尝,這也是 PyCharm 推薦的做法温艇,當(dāng)你新建一個項目的時候,它默認(rèn)創(chuàng)建的 main.py 就是長這樣的:
在if name == "main": 的那一行的最左邊也有一個綠色的運(yùn)行按鈕堕汞,點擊一下中贝,程序就從這一行開始運(yùn)行了。
為什么很多優(yōu)秀的編程語言臼朗,比如 C邻寿、Java、Golang视哑、C++ 都有一個 main 入口函數(shù)呢绣否?我想很重要的一個原因就是就是程序入口統(tǒng)一,容易閱讀挡毅。
第三蒜撮、多進(jìn)程場景下,必須用 if main
比如說你用多進(jìn)程搞并行計算跪呈,寫了這樣的代碼:
import multiprocessing as mp
def useful_function(x):
return x * x
print("processing in parallel")
with mp.Pool() as p:
results = p.map(useful_function, [1, 2, 3, 4])
print(results)
當(dāng)你運(yùn)行的時候段磨,會發(fā)現(xiàn)程序不停的在創(chuàng)建進(jìn)程,同時也在不停的報錯 RuntimeError耗绿,即使你 Ctrl C 也無法終止程序苹支。而加上了 if name == "main": 程序就會按照預(yù)期的進(jìn)行:
import multiprocessing as mp
def useful_function(x):
return x * x
if __name__ == '__main__':
print("processing in parallel")
with mp.Pool() as p:
results = p.map(useful_function, [1, 2, 3, 4])
print(results)
這是為什么呢?
其實我是這樣理解的误阻,Python 的多程序就是啟動了多個 Python 解釋器债蜜,每個 Python 解釋器都會導(dǎo)入你這個腳本晴埂,復(fù)制一份全局變量和函數(shù)給子進(jìn)程用,如果有了if name == "main":寻定,那它后面的代碼就不會被 import儒洛,也就不會被重復(fù)執(zhí)行。否則狼速,這個創(chuàng)建多進(jìn)程的代碼就會被 import琅锻,就會被執(zhí)行,從而無限遞歸的去創(chuàng)建子進(jìn)程向胡,Python3 會報 RuntimeError恼蓬,順序是先創(chuàng)建進(jìn)程,然后報錯的捷枯,因此就會出現(xiàn)不停的創(chuàng)建進(jìn)程,不停的報錯专执,Ctrl C 也無法終止的現(xiàn)象淮捆,只能 kill 掉整個終端。這里有個官方解釋
最后的話
if name == "main": 雖然不是強(qiáng)制的本股,但是基于上述三點原因攀痊,我強(qiáng)烈推薦你這么做,它是 Python 社區(qū)的約定拄显,對應(yīng)Python 之禪:明確優(yōu)于隱晦苟径。正如 _ 作為變量名的意思就是告訴讀代碼的人:這個變量不重要,后面也不會用到它躬审。當(dāng)你看到 Python 腳本有 if name == "main": 時棘街,就會意識到,這是一個可執(zhí)行的腳本承边,當(dāng)被其他程序?qū)霑r遭殉,這部分代碼不會被執(zhí)行,而多進(jìn)程的程序中博助,這是必須的险污。