精品专区-精品自拍9-精品自拍三级乱伦-精品自拍视频-精品自拍视频曝光-精品自拍小视频

網站建設資訊

NEWS

網站建設資訊

Python閉包是什么及怎么使用

這篇文章主要介紹“Python閉包是什么及怎么使用”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Python閉包是什么及怎么使用”文章能幫助大家解決問題。

讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:域名申請、網絡空間、營銷軟件、網站建設、臨澧網站維護、網站推廣。

概要

計算機中有些英文專業詞匯,字面直譯,難免因缺少上下文而顯得蒼白拗口,須得多方鋪墊,方能味得古怪下面的原理。閉包(closure)便是一個這樣牽扯了許多上下文的概念,包括編程語言最基本的綁定(binding),環境(environments),變量作用域(scope)以及函數是第一等公民(function as the first-class)等等。

Binding(綁定)

在Python中,binding(綁定) 是編程語言最基本的抽象手法,它將一個值綁定到一個變量上,并且稍后可以引用或者修改該變量。下面是幾種不同層次的綁定,每組語句在運行時將一個名字與對應值綁定到其定義所在的環境中。

  • 將名字綁定到一塊內存,通過賦值語句實現,當然函數調用時,形參和實參結合也是綁定:
In [1]: square = 4 
  • 將名字綁定到一組復合運算,即     函數定義,利用     def 關鍵字實現:
In [1]: def square(x):
            return x*x
 
  • 將名字綁定到一個數據集合,即     類定義,使用     class 實現:
In [1]: class square:
            def __init__(self, x):
                self.x = x

            def value(self):
                return self.x * self.x
 

依照執行順序,同名多次綁定,后面會覆蓋前面

In [1]: square = 3

In [2]: square
Out[2]: 3

In [3]: def square(x):
   ...:     return x * x
   ...:
   ...:

In [4]: square
Out[4]: 

In [5]: class square:
   ...:     def __init__(self, x):
   ...:         self.x = x
   ...:

In [6]: square
Out[6]: __main__.square
 

說這些都是抽象,是因為它們提供了對數據、復合操作或數據集合的封裝手段,即將一個名稱與復雜的數據或邏輯進行捆綁,使調用者不用關心其實現細節,并可以據此來構建更復雜的工程??梢哉f綁定是編程的基石。

回到本文的主題上來,閉包是對一組復合語句的抽象,也就是函數,只不過是一種特殊的函數,至于這個特殊性在哪,這里先賣個關子,等稍后引入更多概念后再進行闡述。

Scope (作用域)

scope(作用域),顧名思義,也就是某個binding 能罩多大的范圍,或者說可以在多大范圍內訪問的到一個變量。每個函數定義會生成一個局部定義域。

Python,和大多數編程語言一樣,使用的是靜態作用域(static scoping,有時也稱 lexical scoping)規則。在函數嵌套定義的時候,內層函數內可以訪問外層函數的變量值。因此你可以把作用域想象成一個容器,即它是可以嵌套的,并且內層作用域會擴展外層作用域,而最外層作用域即全局作用域。

上一小節提到了,多次同名綁定,后面會覆蓋先前,其實有隱含前提:在同一作用域內。如果是嵌套作用域,其實是隱藏的關系,內層函數的變量定義會遮蔽外層函數同一名字定義,但是在外層作用域中,該變量仍是原值:

In [16]: a = 4

In [17]: def outer(): 
    ...:     a = 5
    ...:     print(a)
    ...:     def inner():
    ...:         a = 6
    ...:         print(a)
    ...:     inner()
    ...:     print(a)
    ...:

In [18]: outer()
5
6
5

In [19]: print(a)
4

作用域其實也可以從另一個角度理解,即我們在某個環境(environment)中,在確定一個name binding 值的時候,會從最內層作用域順著往外找,找到的第一個該名字 binding 的對應的值即為該 name 引用到的值。

需要強調的時候,函數的嵌套定義會引起定義域的嵌套,或者說環境擴展(內層擴展外層)關系。類的定義又稍有不同,class 定義會引入新的 namespace(命名空間),命名空間和作用域是常拿來對比的概念,但這里按下不表,感興趣的可以自己去查查資料。

說到這里,要提一下,一個常被說起的反直覺例子:

In [50]: a = 4

In [51]: def test():
    ...:     print(a) # 這里應該輸出什么?
    ...:     a = 5
    ...:

In [52]: test()
---------------------------------------------------------------------------
UnboundLocalError                         
Traceback (most recent call last)
 in ()
----> 1 test()

 in test()
      1 def test():
----> 2     print(a)
      3     a = 5
      4

UnboundLocalError: local variable 'a' referenced before assignment
 

想象中,上面 print 處應該輸出 4 或者 5 才對,為什么會報錯呢?這是因為 test 函數在被解釋器解析的時候,分詞器會掃一遍 test 函數定義中的所有 token(符號),看到賦值語句 a=5 的存在,就會明確 a 是一個局部變量,因此不會輸出 4。而在執行到 print(a) 的時候,在局部環境中,a 還未被binding,因此會報 UnboundLocalError。

稍微擴展說明一下,雖然 Python 是解釋執行的,即輸入一句,解釋一句,執行一句。但是對于代碼塊(即頭部語句,冒號與其關聯的縮進塊所構成的復合語句(compound sentences),常見的有函數定義,類定義,循環語句等等)來說,還是會整體先掃一遍的。 

First-Class Function(函數是第一等公民)

一般來說,組成編程語言的元素,如變量、函數和類,會被設定不同的限制,而具有最少限制的元素,被我們稱為該編程語言中的一等公民。而一等公民最常見的特權有:

  1. 可以被     綁定到名字上
  2. 可以作為參數在函數中傳遞
  3. 可以作為返回值被函數作為結果返回
  4. 可以被包含在其他數據結構中

套用到 Python 中的函數,即一個函數可以被賦值給某個變量,可以被其他函數接收和返回,可以定義在其他函數中(即嵌套定義):

In [32]: def test():
    ...:     print('hello world')
    ...:

In [33]: t = test # 賦值給變量

In [34]: t()
hello world

In [35]: def wrapper(func):
    ...:     print('wrapper')
    ...:     func()
    ...:

In [36]: wrapper(t) # 作為參數傳遞
wrapper
hello world

In [37]: def add_num(a): 
    ...:     def add(b): # 嵌套定義
    ...:         return a + b
    ...:     return add # 作為函數的返回值
    ...:
    ...:

In [38]: add5 = add_num(5)

In [39]: add5(4)
Out[39]: 9
 

并不是在所有語言中,函數都是一等公民,比如 Java8 以前的 Java,上面四項權利 Java7 中的函數后幾項都沒有。使用函數作為第一等公民的做法,我們成為函數式編程。在這個大數據時代,由于對并發的友好性,傳統過程式語言(比如 Cpp、Java)都在新版本上逐漸支持函數式編程范式。

在這里,能夠操作其他函數的函數(即以其他函數作為參數或者返回值的函數),叫做高階函數。高階函數使得語言的表達能力大大增強,但同時,也增加了編程復雜度。 

Stack Call(棧式調用)

每個函數調用,會在環境中產生一個 frame棧幀),并且在棧幀中會進行一些綁定,然后壓入函數調用棧中。在函數調用結束時,棧幀會被彈出,其中所進行的綁定也被解除,即垃圾回收,對應的局部作用域也隨之消亡。

In [47]: def test():
    ...:     x = 4
    ...:     print(x)
    ...:

In [48]: test()
4

In [49]: x
---------------------------------------------------------------------------
NameError                                 
Traceback (most recent call last)
 in ()
----> 1 x

NameError: name 'x' is not defined
 

即在調用結束后,局部定義的變量  x 在外邊是訪問不到的。但是如之前例子中,返回的 add 函數卻引用了已經調用結束的 add_num 中的變量 a,怎么解釋這種現象呢?可以記住一條,也是之前提到過的:

函數嵌套定義時,內部定義的函數所在的環境會自動擴展其定義所在環境

因此在外部函數返回后,返回的內部函數依然維持了其定義時的擴展環境,也可以理解為由于內部函數引用的存在,外部函數的環境中所有的綁定并沒有被回收。 

Closure(閉包)

千呼萬喚始出來,以為是高潮,其實已結束。

閉包就是建立在前面的這些概念上的,上面提到的某個例子:

In [37]: def add_num(a): 
    ...:     def add(b): # 嵌套定義
    ...:         return a + b
    ...:     return add # 作為函數的返回值
    ...:
    ...:

In [38]: add5 = add_num(5)

In [39]: add5(4)
Out[39]: 9

其實就是閉包。撿起之前伏筆,給出我對閉包的一個理解:它是一種高階函數,并且外層函數(例子中的add_num)將其內部定義的函數(add)作為返回值返回,同時由于返回的內層函數擴展了外層函數的環境,也就是對其產生了一個引用,那么在調用返回的內部函數(add5)的時候,能夠引用到其(add)定義時的外部環境(在例子中,即 a 的值)。

關于“Python閉包是什么及怎么使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注創新互聯行業資訊頻道,小編每天都會為大家更新不同的知識點。


分享文章:Python閉包是什么及怎么使用
轉載來于:http://m.jcarcd.cn/article/pghesd.html
主站蜘蛛池模板: 人妖系列精品视频 | 国产日韩欧美顶级片 | 国产一区二区影视 | 日本精品aⅴ在线 | 日韩视频第1 | 国内成人一区 | 国产精品看 | 国产精品午夜视频 | 91网首页| 精品国产v| 无码精品毛片成人影院 | 区三区在线观看 | 国产精品一二三四 | 日本免费在线 | 午夜写真福利 | 国产一区二区自拍 | 日韩欧美国产精 | 国产在线观看www | 国产婬妇視频网站 | 国产呦福利导航 | 中文字幕一区二区三 | 国产视频二区 | 国产欧美日韩一区二 | 欧美亚洲日韩国产网 | 日本系列1 | 欧美鲁丝片一区二区 | 91午夜福利伦理 | 国产精品一级二级 | 国产精品免费视频 | 成人公开在线导航网 | 成人免费电影 | 日韩欧美爽爽的影院 | www.91玉足| 精品欧美一区二 | 美乳一区二区 | 91福利国产免费 | 国产精选在线观看 | 国产性天天综合网 | 欧洲喜剧片 | 韩国伦理中文字幕 | 国产精品成人观看视 |