Python3學習筆記 | 十九、Python的函數-作用域

  • 2019 年 10 月 6 日
  • 筆記

部分設備閱讀本文會存在程式碼錯亂的情況,可點擊閱讀原文鏈接到部落格中進行查看

一、Python作用域基礎

作用域針對的是變數。在使用同一個變數名時,會出現作用域問題。如右邊的x,在函數里與函數外是不同的變數。 • 一個def內定義的變數名能夠被def內部使用。不能在函數外部引用這個變數名。 • def內的變數名與def外的變數名並不衝突。在def內賦值的與def外賦值的相同變數名是不同的變數。 變數名有如下三種不同作用域: 1、全局:在def外定義的變數名是全局變數 2、本地:在def內部定義的叫做本地變數 3、其它:嵌套def時,各自的變數也是獨立的。

二、作用域法則

內嵌的模組是全局作用域,全局作用域的作用範圍僅限於單個文件,每次的函數的調用都創建了一個新的本地作用域,賦值的變數名除非聲明全局變數或非本地變數(Python3.x有非本地變數說法),否則均為本地變數。所有其它的變數名都可以歸納為本地、全局或者內置。

變數名使用時,查找順序:LEGB L:本地變數名 - 在本地是否使用此變數名賦值過。 E:上一層結構中def或lambda的本地變數名 - 上一層是否使用此變數名賦值過。 G:全局變數名 - 在整個文件里是否對變數名賦值過。 B:內置變數名 - Python內部是否存在此變數名。 要是都找不到相應變數名,就會報錯。

內置變數:exit、open等。  >>> l = 'global'; e = 'global'; g = 'global'  #全局變數  >>>def myenclosefunc():  ...     e = 'enclose';l = 'enclose'  #enclose func變數  ...     def mylocalfunc():  ...             l = 'local'  #本地變數  ...             print('in local: l={},e={},g={}'.format(l,e,g))  ...     mylocalfunc()  ...     print('in enclose: l={},e={},g={}'.format(l,e,g))  ...  >>> myenclosefunc()  in local: l=local,e=enclose,g=global  in enclose: l=enclose,e=enclose,g=global  >>> print('in global: l={},e={},g={}'.format(l,e,g))  in global: l=global,e=global,g=global

三、global語句

在函數內,想改變全局變數,可以使用global語句來定義此變數為全局變數,如下所示。

>>> g = 'global'  >>> l = 'global'  >>> def myfunc():  ...     global g  ...     g = 'local'  ...     l = 'local'  ...  >>> g,l  ('global', 'global')  >>> myfunc()  >>> g,l  ('local', 'global')

在myfunc函數里,都重新賦值了g與l,但在函數執行後,只有g改變了。當使用global g 之後,當前函數里所使用的所有對變數g的更改都會對全局變數g進行更改。 除了這個方法,還有引用自己的方法(交互模式里,可以import main)與sys.modules的方法(可以使用引用過的所有模組,交互模式里本身可以用main方式),具體如下。

>>> x = 2  >>> import __main__  >>> __main__.x  2  >>> def glob():  ...     __main__.x += 1  ...  >>> glob()  >>> x  3
>>> x = 2  >>> import  sys  >>> sys.modules['__main__'].x  2  >>> def glob():  ...     sys.modules['__main__'].x += 1  ...  >>> glob()  >>> x  3

四、作用域與嵌套函數

被嵌套函數的作用域也是上級函數里:

>>> def outer():  ...     def inner():  ...             print('inner')  ...

在這裡,想調用inner函數,必須是在函數outer裡面,不能直接使用。這個時候,我們可以返回內部函數的方法來提取內部函數:

>>> def outer():  ...     def inner():  ...             print('inner')  ...     return inner()  ...  >>> func1 = outer()  inner

工廠函數

工廠函數為:根據要求的對象,一個能夠記住嵌套作用域的變數值的函數。這種功能,使用類可以更好的實現,但使用函數也能簡單實現。

>>> def funcl(x):  ...     def action(y):  #嵌套函數  ...             return x ** y  #返回x ** y的值  ...     return action  #返回嵌套的函數  ...  >>> a = funcl(3)  #定義x值為3後的嵌套函數賦值  >>> a(2)  9  >>> a(3)  27

可以使用lambda,把之前函數變為:

>>> def func(x):  ...     return lambda y :x ** y  ...  >>> a = func(3)  >>> a(2)  9

關於lamdba的使用可以參考:https://www.cnblogs.com/evening/archive/2012/03/29/2423554.html

循環變數

>>> def makeAction():  ...     acts = []  ...     for i in range(5):  ...             acts.append(lambda x:i ** x)  ...     return acts  ...

執行後,返回值可能不是我們想像的結果:

>>> funclist = makeAction()  >>> funclist[0](2)  16  >>> funclist[4](2)  16

原因在於,i x的時候,i的值取的是for循環結束後的最後一個(4)。我們需要寫成lambda x, i=i: i x,這樣,lambda里的i是本地變數,跟for循環里的i不衝突,優化後如下:

>>> def makeAction():  ...     acts = []  ...     for i in range(5):  ...             acts.append(lambda x,i = i : i ** x)  ...     return acts  ...  >>> funclist = makeAction()  >>> funclist[0](2)  0  >>> funclist[2](2)  4  >>> funclist[4](2)  16

五、nonlocal語句

nonlocal關鍵字用來在函數或其他作用域中使用外層(非全局)變數,更多用法可以參考https://blog.csdn.net/youngbit007/article/details/64905070

>>> x = 1  #全局變數x為1  >>> y = 1  #全局變數y為1  >>> def printxy():  ...     x = 2  #printxy里的本地變數x為2  ...     y = 2  #printxy里的本地變數y為2  ...     def setxy():  ...             nonlocal x  #設定x為nonlocal  ...             global y  #設定y為global  ...             x =3  ...             y =3  ...     setxy()  ...     print(x)  ...     print(y)  ...  >>> printxy()  3  #printxy里的x為3  2  #printxy里的y沒變,仍為2  >>> x,y  (1, 3)  #全局變數x沒變,y變為3