|
【要點搶先看】
上一小節(jié)我們引入了函數(shù)的概念,這一節(jié)我們開始接觸函數(shù)里的一個非常重要的話題:變量的作用域。 當(dāng)你在一個程序中使用變量名時,python創(chuàng)建、改變或查找變量名都是在所謂的命名空間中進(jìn)行的,也就是我們要說的變量的作用域。在代碼中給一個變量賦值的地方?jīng)Q定了這個變量將存在于哪一個命名空間,也就是他的可見范圍。 def之中的變量名和def之外的變量名并不沖突,一個在def之外被賦值(例如,在另外一個def之中或者在模塊文件的頂層)的變量X與在這個def之中賦值的變量X是完全不同的變量。 所以我們看出,變量的作用域完全是由變量在程序文件中源代碼的位置而決定,而不是由函數(shù)調(diào)用決定。 【妹子說】好啦,說了這么多概念,還是用例子說話吧! x = 99這里就可以看出,在這個模塊文件中:語句X=99,我們創(chuàng)建了一個名為X的全局變量(在這個函數(shù)所在的模塊文件中可見),但是X=88這個賦值語句創(chuàng)建了一個本地變量X(只在def語句內(nèi)是可見的)。 盡管這兩個變量都是X,但是他們作用域可以把它們區(qū)別開來。實際上,函數(shù)的作用域有助于防止程序之中變量名的沖突,并且有助于函數(shù)成為更加獨立的程序單元。 【妹子說】哦,我大概對作用域有那么點概念了。 那下面我們接著展開具體介紹函數(shù)的四個作用域:LEGB,即L本地作用域,E內(nèi)嵌作用域,G全局作用域和B內(nèi)置作用域。 在一個函數(shù)中定義的是本地作用域,而模塊(也就是一個xxx.py文件)中定義的是全局作用域。而內(nèi)置作用域,我們使用時是直接使用變量名而不需要導(dǎo)入任何模塊,比如一些內(nèi)置的函數(shù)名:print等等 這里再強調(diào)一下python中所謂的全局作用域: 全局作用域的作用范圍僅限于單個文件,別被全局二字所迷惑,這里的全局指的是一個文件的頂層的變量名僅對于這個文件內(nèi)部的代碼而言是全局的,在python中聽到全局,你就應(yīng)該想到模塊二字。 變量名由模塊文件隔開,并且必須精確的導(dǎo)入一個模塊文件才能夠使用這個文件中使用的變量名。 再說說本地作用域:每次對函數(shù)的調(diào)用都創(chuàng)建一個新的本地作用域,賦值的變量名除非聲明為全局變量或非本地變量,否則均為本地變量。在默認(rèn)的情況下,所有函數(shù)定義的內(nèi)部變量名都位于本地作用域(與函數(shù)調(diào)用相關(guān)的)內(nèi)。 再來看一個例子來演示一下這兩種作用域: x = 99這個例子中出現(xiàn)了全局作用域內(nèi)變量名:x和func 因為x是在模塊文件頂層注冊的,所以他是全局變量;他能夠在函數(shù)內(nèi)部進(jìn)行引用,僅僅是引用變量不需要進(jìn)行全局變量聲明。 也有本地作用域變量名,y,z z和參數(shù)y都是本地變量,只在函數(shù)運行時存在,因為他們都是在函數(shù)定義內(nèi)部進(jìn)行賦值的,前面我們說過函數(shù)的參數(shù)也是通過賦值進(jìn)行傳遞的。 這種變量名隔離機(jī)制存在的意義在于本地變量是作為臨時的變量名,只有在函數(shù)運行的時候才需要它們,例如,y和z都只存在于函數(shù)內(nèi)部,這些變量名不會與模塊命名空間內(nèi)的變量名(同理,與其他函數(shù)內(nèi)的變量名)產(chǎn)生沖突。 【妹子說】那么如果一段程序中,不同作用域內(nèi)的幾個變量名稱相同怎么辦? 好問題!這正是python的LEGB變量名搜索機(jī)制要解決的問題。 當(dāng)在python中使用某個變量名時,python按照L-E-G-B的順序依次搜索四個作用域,L本地作用域,E即上一層def或者lambda的本地作用域,之后是全局作用域G,最后是內(nèi)置作用域B,并且在第一處能找到作用名的地方停下來,如果變量名在這一次搜索中沒有找到,python會報錯。 因此按照LEGB法則中規(guī)定的變量搜索順序,在本地作用域中的變量名是會在本地作用域中覆蓋在全局作用域和內(nèi)置作用域中有相同變量名的變量,全局變量名會覆蓋內(nèi)置的同名變量名。 【妹子說】話不多說,上例子 x = 88在這一段程序中,本地變量名x覆蓋了全局變量名x,此時本地和全局的兩個變量雖然都叫x,但他們是完全不同的變量。 def func():這個例子中,本地作用域中的變量名open就覆蓋了內(nèi)置作用域中的變量名open,因此再使用open去打開文件,此時的操作就無法使用,因為文件打開的open函數(shù)變量被open=1這個本地數(shù)值變量覆蓋了。 強調(diào)一點:這里我們提到的只是在本地作用域去引用或者覆蓋全局變量和內(nèi)置變量。 但是,請注意!如果試圖去修改,即在函數(shù)內(nèi)部試圖改變函數(shù)外部聲明的值,那就得用global和nonlocal關(guān)鍵字了。 global關(guān)鍵字 之前我們說過python中的變量不用聲明,直接賦值使用,但是這個global關(guān)鍵字看上去就像一個聲明,但是他不是一個類型的聲明,而是一個變量命名空間的聲明,它告訴python函數(shù)打算生成一個或多個全局變量。應(yīng)用他,就可以在函數(shù)內(nèi)部對全局變量進(jìn)行引用和修改 x = 88在這個例子中,我們對X加了一個global聲明,以便在def之內(nèi)引用并修改位于全局的變量x,而不是產(chǎn)生一個新的本地變量x并將其覆蓋 我們再看一個綜合的例子,串聯(lián)起剛剛我們提到的幾個知識點 x,y,z = 1,2,3這個例子中,x,y,z都是全局變量,y和z只是引用值,而對于x,我們想改變他的值,因此用了global進(jìn)行引用聲明。 【妹子說】恩,作用域的內(nèi)容也是不少呀,LEGB,本地作用域、全局作用域、內(nèi)置作用域,包括他們的引用索引順序,但是好像掉了一條吧。 對,還有嵌套作用域,這個是一個更加pythonic的內(nèi)容,我們下一節(jié)單獨花一節(jié)的篇幅進(jìn)行介紹。 |
|
|