淺談python函數(shù)之作用域(python3.5)
1 基本概念
1.1 命名空間 (namespace)
命名空間是變量名到對象的映射(name -> obj)。目前大多數(shù)的命名空間以類似于python字典的形式實(shí)現(xiàn),實(shí)現(xiàn)形式在未來可能發(fā)生變化。命名空間舉例:內(nèi)置變量(內(nèi)置函數(shù)abs, 內(nèi)置的異常等),模塊中的全局變量,函數(shù)調(diào)用時的局部變量。在某種意義上講,對象的屬性也形成一個命名空間。重要的是,不同的命名空間中的變量沒有任何關(guān)聯(lián),兩個不同的命名空間中可以包含相同的變量名。
命名空間有不同的創(chuàng)建時間和生命周期:
•內(nèi)置變量命名空間在python解釋器啟動時創(chuàng)建,并且在解釋器運(yùn)行期間永遠(yuǎn)不會被刪除;
•一個模塊的命名空間在模塊被導(dǎo)入時創(chuàng)建,并且到解釋器退出會一直存在;
•函數(shù)的本地(局部)命名空間在函數(shù)調(diào)用時創(chuàng)建,函數(shù)退出時刪除;
•解釋器頂層執(zhí)行的語句都是 __main__ 模塊的組成部分,它們有自己的命名空間。
注:內(nèi)置變量實(shí)際上同樣是以模塊的形式存在,模塊名為 builtins 。
1.2 作用域 (scope)
作用域是Python程序中可以直接訪問一個命名空間內(nèi)變量的文本區(qū)域,可直接訪問即命名空間內(nèi)的變量在該文本區(qū)域內(nèi)可見、可引用。
•本地(局部)作用域:函數(shù)或者類的內(nèi)部
•全局作用域:整個程序的運(yùn)行環(huán)境。
全局作用域中無法直接訪問本地作用域中定義的變量:
def func1(): name = 1 print(func1) # <function func1 at 0x101a03d08> print(name) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # NameError: name 'name' is not defined
本地作用域中的變量定義:
•在python中,變量賦值即定義。在局部作用域內(nèi)被賦值的變量,除非由 global 或者 nonlocal 聲明,否則全部為局部變量,函數(shù)調(diào)用時存在于函數(shù)命名空間。
•global var : 聲明變量 var 為全局變量,它所有的引用和賦值都在模塊的命名空間進(jìn)行。
•nonlocal var : 將外層函數(shù)命名空間中的變量 var 綁定到本地作用域,使其在本地作用域可重新賦值。如果變量沒有被聲明為 nonlocal,這些變量在本地作用域僅可讀,嘗試給變量賦值則會在本地命名空間創(chuàng)建一個同名變量。
nonlocal聲明的變量在上層函數(shù)中必須存在,否則報錯:
test = 'global variable' def scope_test(): def inner(): nonlocal test print(test) scope_test() # SyntaxError: no binding for nonlocal 'test' found
2 示例
2.1 本地作用域中變量的搜索遵守LEGB規(guī)則
1.L-Local(function):函數(shù)或類的命名空間,其中的變量稱為本地變量
2.E-Enclosing function locals:外層函數(shù)的命名空間(例如closure),包含被聲明為non-local的變量
3.G-Global(module):函數(shù)定義所在模塊的命名空間,其中的變量稱為全局變量
4.B-Builtin(Python):Python內(nèi)置模塊的名字空間
def scope_test(): def do_local(): spam = "local spam" def do_nonlocal(): nonlocal spam # 遞歸向上尋找上層函數(shù)命名空間中的spam變量 spam = "nonlocal spam" def do_global(): global spam # 在全局變量中尋找spam變量,沒有則創(chuàng)建 spam = "global spam" spam = "test spam" do_local() print("After local assignment:", spam) # 輸出本地變量 spam do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam) scope_test() print("In global scope:", spam)
結(jié)果
<SPAN style="FONT-SIZE: 14px">1 After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam </SPAN>
2.2 閉包
閉包:在嵌套函數(shù)中,如果內(nèi)層函數(shù)引用了外層函數(shù)的變量,就形成了一個閉包。
自由變量:被引用的外層函數(shù)變量,稱為內(nèi)層函數(shù)的自由變量。
def fn(): a = 1 def closure(): nonlocal a a += 1 print(a) return closure inner = fn() print(inner.__closure__) # (<cell at 0x10240b408: int object at 0x100277bc0>,) inner() # 2 inner() # 3
外層函數(shù)執(zhí)行完,其命名空間刪除。但是因?yàn)?a 是內(nèi)層函數(shù)的自由變量,所以變量 a 被保留,可以看作是 closure 函數(shù)對象的一個附加屬性。
3 靜態(tài)檢測
3.1 本地變量
python是在編譯def語句時靜態(tài)檢測其本地變量的。
a = 1 def local_test(): a += 1 print(a) local_test() # UnboundLocalError: local variable 'a' referenced before assignment print(b) # NameError: name 'b' is not defined
在編譯local_test函數(shù)時,python就確定了變量 a 為函數(shù)的本地變量。所以在執(zhí)行 a += 1 是會直接在本地命名空間尋找變量a。
3.2 命名空間搜索鏈
name = "lzl" def f1(): print(name) def f2(): name = "eric" f1() f2() # lzl
一個函數(shù)的變量搜索路徑是在它定義的時候決定的,不受它調(diào)用位置的影響。
f1定義在全局作用域中,其變量的搜索路徑為:本地命名空間 --> 模塊命名空間。所以最后的輸出結(jié)果為‘lzl'。
4 匿名函數(shù)
Python借助lambda關(guān)鍵字定義匿名函數(shù),格式如下:
lambda 參數(shù)列表: 表達(dá)式
lambda x: x + 1 # 函數(shù)功能類型于下面的函數(shù) def _(x): return x + 1
示例
下面一段代碼的輸出結(jié)果是什么:
li = [lambda :x for x in range(10)] print(li[0]())
其等價形式:
def fn(): return x li = [] for x in range(10): li.append(fn) li[0]() # fn() -> 9,根據(jù)變量搜索規(guī)則,x在函數(shù)中沒有定義,在全局變量中查找
以上這篇淺談python函數(shù)之作用域(python3.5)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
python實(shí)現(xiàn)while循環(huán)打印星星的四種形狀
今天小編就為大家分享一篇python實(shí)現(xiàn)while循環(huán)打印星星的四種形狀,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11Python實(shí)現(xiàn)HTML轉(zhuǎn)Word的示例代碼
這篇文章主要為大家詳細(xì)介紹了使用Python實(shí)現(xiàn)HTML轉(zhuǎn)Word的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12python函數(shù)裝飾器構(gòu)造和參數(shù)傳遞
這篇文章主要介紹了python函數(shù)裝飾器構(gòu)造和參數(shù)傳遞,下面通過一個小案例來簡單的理解什么是裝飾器,需要的小伙伴可以參考一下2022-03-03Python中使用dwebsocket實(shí)現(xiàn)后端數(shù)據(jù)實(shí)時刷新
dwebsocket是Python中一款用于實(shí)現(xiàn)WebSocket協(xié)議的庫,可用于后端數(shù)據(jù)實(shí)時刷新。在Django中結(jié)合使用dwebsocket和Channels,可以實(shí)現(xiàn)前后端的實(shí)時通信,支持雙向數(shù)據(jù)傳輸和消息推送,適用于實(shí)時聊天、數(shù)據(jù)監(jiān)控、在線游戲等場景2023-04-04