Python3(九) 閉包

一. 一切皆對象

函數式編程並無標準定義,若是代碼很是繁瑣則考慮使用。python

學習閉包的概念,不是python獨有的。web

其餘大多數語言中的函數只是一段可執行的代碼,並非對象。編程

python中的函數是對象,一切皆對象。能夠把函數賦值給變量:閉包

a = 1架構

a = '2'app

a = def函數式編程

甚至能夠把函數看成另一個函數的參數傳遞或者當成返回值返回,而C#中要封裝成委託。函數

二.什麼是閉包:閉包=函數+函數定義時的環境變量

咱們嘗試從概念上去理解一下閉包。學習

在一些語言中,在函數中能夠(嵌套)定義另外一個函數時,若是內部的函數引用了外部的函數的變量,則可能產生閉包。閉包能夠用來在一個函數與一組「私有」變量之間建立關聯關係。在給定函數被屢次調用的過程當中,這些私有變量可以保持其持久性。—— 維基百科spa

用比較容易懂的人話說,就是當某個函數被當成對象返回時,夾帶了外部變量,就造成了一個閉包。

1.

code1

def curve_pre():
 
    def curve():
 
        pass
 
curve()    #找不到,由於curve()的做用域僅限於curve_pre()的內部

code2

def curve_pre():
 
    def curve():
        print('This is a function')
 
        pass
 
    return curve    #函數能夠做爲結果返回
 
f = curve_pre()    #函數能夠賦值
 
f()#執行函數,不會報錯
#This is a function

code3

def curve_pre():
 
    a = 25         #局部變量在curve的外部
 
    def curve(x):  #接受拋物線的x值
 
        return a * x * x
 
    return curve    #返回一個函數
 
 
f = curve_pre()
 
print(f(2))         #調用curve()函數
 
#100

code4

a = 10
 
def f1(x):
 
    return a * x * x
 
print(f1(2))
 
#40         #局部變量找不到會去上一級找

code 3 和 code4 對比

def curve_pre():
 
    a = 25 #局部變量在curve的外部
 
    def curve(x): #接受拋物線的x值
 
        return a * x * x
 
    return curve #返回一個函數
 
 
 
a = 10     #定義a = 10
 
f = curve_pre()
 
print(f(2)) #調用curve()函數
 
#100        #仍然是a = 25的取值,取得是定義時的環境變量,這就是閉包

函數及其外部環境變量所構成的總體叫作閉包

2.環境變量要在函數外部,但不能是全局變量:

a = 25    #a定義爲了全局變量
 
def curve_pre():
 
    def curve(x): #接受拋物線的x值
 
        return a * x * x
 
    return curve #返回一個函數
 
 
 
a = 10
 
f = curve_pre()
 
print(f(2)) #調用curve()函數
 
#40    #a的值被改變了,由於沒有造成閉包

查看:

def curve_pre():
 
    a = 25 #局部變量在curve的外部
 
    def curve(x): #接受拋物線的x值
 
        return a * x * x
 
    return curve #返回一個函數
 
 
 
a = 10
 
f = curve_pre()
 
print(f.__closure__)
 
print(f.__closure__[0].cell_contents)#取出閉包的環境變量
 
print(f(2)) #調用curve()函數
 
 
#(<cell at 0x0031AAF0: int object at 0x0FF93A80>,)
 
#25    #這裏是a的值
 
#100

三.一個事例看看閉包 

1.閉包的意義:閉包保存的是一個環境,把函數現場保存起來了。

              閉包 = 函數 + 函數定義時的環境變量(不能是全局變量)

2. 閉包的經典誤區      從外層向內層分析

def f1():
 
    a = 10
 
    def f2():
 
        a = 20    #對a從新賦值局部變量,不會影響到外部
 
        print(a)
 
    print(a)    #10
 
    f2()    #20 #f1內部調用f2
 
    print(a)    #10
 
f1()
 
#10
 
#20
 
#10

驗證其是否是一個閉包:

1

def f1():
 
    a = 10
 
    def f2():
 
        a = 20
 
        # print(a)
 
    #print(a)
 
    f2()
 
    #print(a)
 
 
 
f = f1()    #f1是None類型
 
print(f.__closure__)    #報錯

 

(2)加上返回值,仍然不是閉包函數:

def f1():
 
    a = 10
 
    def f2():
 
        a = 20    #a被認爲是一個局部變量了,就不認爲是個環境變量了
 
        return a
 
    return f2
 
 
 
f = f1()
 
print(f.__closure__)    #沒有__closure__屬性
 
#None

(3)去掉f2()中的賦值後是閉包函數:

def f1():
 
    a = 10
 
    def f2():
 
        #a = 20    #刪除a = 20,此時a被python認爲是一個局部變量
 
        return a
 
    return f2
 
 
 
f = f1()
 
print(f.__closure__)
 
#(<cell at 0x02F5AAF0: int object at 0x0FF93990>,)

緣由:環境變量不能看成一個變量去賦值,而是必定要去引用外部。

四.出個題,用閉包解決!

閉包不是必不可少的東西,只是能夠使你的代碼架構更加合理。

題目:

旅行者,x = 0 爲起點,沒走一步加1,計算出旅行者當前所處的位置。

 

關鍵點:每次調用時須要調用上次的結果

1.先用非閉包解決一下

origin = 0
 
 
def go(step):
 
    global origin    #將origin變成全局變量
 
    new_pos = origin + step
 
    origin = new_pos  #等號左邊的origin被python認識是局部變量
 
    return origin
 
 
 
print(go(2))
 
print(go(3))
 
print(go(6))
 
#2
 
#5
 
#11

2.再用閉包解決一下

origin = 0
 
def factory(pos):    #工廠模式
 
    def go(step):
 
        nonlocal pos #強制聲明不是局部變量
 
        new_pos = pos + step
 
        pos = new_pos
 
        return new_pos
 
    return go
 
tourist = factory(origin)
 
print(tourist(2))
print(origin)
 
print(tourist(3))
 
print(tourist(6))
 
#2
#0
 
#5
 
#11

並無改變全局變量origin的值

 

五.小談函數式編程

  • python和Javascript都會使用閉包。
  • 閉包特色:在一個函數外部間接調用函數內部的變量,從模塊級別間接調用局部變量。
  • 極易形成內存泄漏。

閉包的概念

相關文章
相關標籤/搜索