見鬼!導入Python模塊執行了全部代碼,你不知道__name__變量是什麼意思嗎?

可能不少同窗在閱讀Python源代碼時會發現常常會出現if __name__ == '__main__':這樣的代碼,那麼這樣的代碼起到什麼做用呢?本文將爲你深刻解析__name__變量的含義和應用場景。
當Python解析器讀源代碼文件時,會作以下兩件事情:
  • 設置特殊變量,如__name__;css

  • 執行源代碼文件中的全部代碼;python


如今咱們將焦點放到__name__變量上來,看看在Python程序中爲何要使用__name__變量。
讓咱們先使用一段代碼示例來探索導入和腳本的工做方式。假設這些代碼位於名爲foo.py的文件中。
print("before import")import math
print("before functionA")def functionA(): print("Function A")
print("before functionB")def functionB(): print("Function B {}".format(math.sqrt(100)))
print("before __name__ guard")if __name__ == '__main__': functionA() functionB()print("after __name__ guard")
當Python解釋器讀取源文件時,它首先定義一些特殊變量。在這個案例中,咱們關心的是__name__變量。若是將Python腳本文件做爲主程序運行,也就是經過下面的命令運行foo.py。
python foo.py
Python解釋器會使用下面的代碼爲__name__變量賦值,也就是說,__name__變量的值是"__main__"。
__name__ = "__main__"
另外一方面,假設其餘模塊是主程序,而且它將導入foo.py。這意味着在主程序中會有以下的語句:
import foo
Python解釋器將搜索foo.py文件(以及搜索其餘一些變體),在執行該模塊以前,它將從import語句中將名稱「 foo」分配給__name__變量,也便是使用下面的代碼爲__name__變量賦值。
__name__ = "foo"
設置__name__變量後,Python解釋器經過一次執行一條語句的方式執行模塊中的全部代碼。您可能想要在代碼示例側面打開另外一個窗口,以便您能夠按照如下說明進行操做。
如今執行前面的代碼,會輸出以下內容:
before importbefore functionAbefore functionBbefore __name__ guardFunction AFunction B 10.0after __name__ guard
若是當前的Python腳本按着主程序的方式執行(使用python命令),那麼__name__變量的值就是"__main__"。這樣就會調用functionA和functionB函數,從而輸出 "Function A" 和 "Function B 10.0"
若是foo.py腳本不是做爲主程序運行,而是被另外一個程序導入,則__name__變量的值是「 foo」,而不是「 __main__」,在這種狀況下,將不會調用functionA和functionB函數不過在這種狀況下,仍然會輸出 "after __name__ guard" ,由於這條語句不屬於if語句。
如今總結一下:
根據foo.py的運行方式,可能會有兩種輸出結果。
(1)做爲主程序運行,會輸出以下結果
before importbefore functionAbefore functionBbefore __name__ guardFunction AFunction B 10.0after __name__ guard
(2)做爲模塊被導入,會輸出以下結果:
before importbefore functionAbefore functionBbefore __name__ guardafter __name__ guard
可能有不少同窗會有這樣的疑問,爲何Python要提供這個功能呢?像C#、Java這樣的編程語言並無這樣的功能啊!其實這要從Python腳本的運行機理談起。若是Python腳本做爲主程序執行,這個執行方式與Java相似。不過當導入一個Python模塊就不同了。對於Java語言,導入一個包,也只是導入而已,除非顯式調用包中的API,不然單單導入,是不會執行Java代碼的。但Python就不同了,若是使用import語句導入一個模塊,實際上是先執行被導入模塊中的全部代碼,而後纔會執行當前模塊的代碼。例如,下面的代碼,會先執行foo.py腳本中的代碼,而後纔會執行print('current module')
import fooprint('current module')
執行這段代碼,會輸出以下的內容:
before importbefore functionAbefore functionBbefore __name__ guardafter __name__ guardcurrent module
若是一個Python腳本,同時便可以做爲主程序執行,也能夠做爲模塊被導入,這就要求在模塊被導入時不執行做爲主程序執行時的代碼,因此若是是在主程序中執行的代碼,若是使用__main__變量進行判斷。
關於__name__的一些疑問
有的同窗問,在腳本文件中能夠有多個__name__校驗代碼塊嗎?其實一般只有一個__name__校驗代碼塊嗎,但Python解析器並不會阻止你編寫多個__name__校驗代碼塊嗎。
下面再給你們2段代碼,看看輸出結果會是什麼:
# foo2.pydef functionA(): print("a1") import foo2 print("a2") functionB() print("a3")
def functionB(): print("b")
print("t1")if __name__ == "__main__": print("m1") functionA() print("m2")print("t2")
在這段代碼中導入了模塊自己(foo2.py),執行代碼,會輸出以下結果:
t1m1a1t1t2a2ba3m2t2
你們能夠分析一下這段代碼的執行過程。下面將if __name__ == "__main__":去掉,看看會發生什麼。
# foo3.pydef functionA(): print("a1") import foo3 print("a2") functionB() print("a3")
def functionB(): print("b")
print("t1")print("m1")functionA()print("m2")print("t2")
執行這段代碼,會引發死循環嗎?實際上是不會的,由於Python解析器有緩存,若是一個模塊在當前模塊中已經被導入了,當第二次導入時,將不會再次執行被導入模塊的代碼,而是直接使用緩存中的內容,因此import foo3只會致使foo3.py文件中的代碼做爲導入模塊的方式被執行一次。因此執行這段代碼,輸出結果以下:
t1m1a1t1m1a1a2ba3m2t2a2ba3m2t2

你們能夠本身分析一下程序的執行過程。

推薦閱讀   點擊標題可跳轉

一、連Python產生器(Generator)的原理都解釋不了,還敢說Python用了5年?nginx

二、牛掰了!鴻蒙與Android完美融合,將鴻蒙設備當Android設備用web

三、【鴻蒙學院】鴻蒙App開發直播學員提問與回答編程

四、【鴻蒙學院】鴻蒙IDE:下載、安裝DevEco Studioswift

五、 Python高效編程之88條軍規(2):你真的會格式化字符串嗎?緩存

六、像極客同樣提取Android的Root權限微信


關注「極客起源」公衆號,加星標,不錯過精彩技術乾貨
app



本文分享自微信公衆號 - 極客起源(geekculture)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。編程語言

相關文章
相關標籤/搜索