亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python中的閉包實(shí)例詳解

 更新時(shí)間:2014年08月29日 15:37:17   投稿:shichen2014  
這篇文章主要介紹了Python中的閉包,針對(duì)閉包的定義、用法及注意事項(xiàng)進(jìn)行了實(shí)例講解,有助于讀者深入理解閉包的概念及用法,需要的朋友可以參考下

一般來(lái)說(shuō)閉包這個(gè)概念在很多語(yǔ)言中都有涉及,本文主要談?wù)刾ython中的閉包定義及相關(guān)用法。Python中使用閉包主要是在進(jìn)行函數(shù)式開(kāi)發(fā)時(shí)使用。詳情分析如下:

一、定義

python中的閉包從表現(xiàn)形式上定義(解釋)為:如果在一個(gè)內(nèi)部函數(shù)里,對(duì)在外部作用域(但不是在全局作用域)的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包(closure).這個(gè)定義是相對(duì)直白的,好理解的,不像其他定義那樣學(xué)究味道十足(那些學(xué)究味道重的解釋,在對(duì)一個(gè)名詞的解釋過(guò)程中又充滿了一堆讓人抓狂的其他陌生名詞,不適合初學(xué)者)。下面舉一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明。

>>>def addx(x): 
>>> def adder(y): return x + y 
>>> return adder 
>>> c = addx(8) 
>>> type(c) 
<type 'function'> 
>>> c.__name__ 
'adder' 
>>> c(10) 
18

結(jié)合這段簡(jiǎn)單的代碼和定義來(lái)說(shuō)明閉包:
如果在一個(gè)內(nèi)部函數(shù)里:adder(y)就是這個(gè)內(nèi)部函數(shù),
對(duì)在外部作用域(但不是在全局作用域)的變量進(jìn)行引用:x就是被引用的變量,x在外部作用域addx里面,但不在全局作用域里,
則這個(gè)內(nèi)部函數(shù)adder就是一個(gè)閉包。

再稍微講究一點(diǎn)的解釋是,閉包=函數(shù)塊+定義函數(shù)時(shí)的環(huán)境,adder就是函數(shù)塊,x就是環(huán)境,當(dāng)然這個(gè)環(huán)境可以有很多,不止一個(gè)簡(jiǎn)單的x。

二、使用閉包注意事項(xiàng)

1.閉包中是不能修改外部作用域的局部變量的

>>> def foo(): 
...  m = 0 
...  def foo1(): 
...   m = 1 
...   print m 
... 
...  print m 
...  foo1() 
...  print m 
...
>>> foo()
0
1
0

從執(zhí)行結(jié)果可以看出,雖然在閉包里面也定義了一個(gè)變量m,但是其不會(huì)改變外部函數(shù)中的局部變量m。

2.以下這段代碼是在python中使用閉包時(shí)一段經(jīng)典的錯(cuò)誤代碼

def foo(): 
 a = 1 
 def bar(): 
  a = a + 1 
  return a 
 return bar

這段程序的本意是要通過(guò)在每次調(diào)用閉包函數(shù)時(shí)都對(duì)變量a進(jìn)行遞增的操作。但在實(shí)際使用時(shí)

>>> c = foo() 
>>> print c() 
Traceback (most recent call last): 
 File "<stdin>", line 1, in <module> 
 File "<stdin>", line 4, in bar 
UnboundLocalError: local variable 'a' referenced before assignment 

這是因?yàn)樵趫?zhí)行代碼 c = foo()時(shí),python會(huì)導(dǎo)入全部的閉包函數(shù)體bar()來(lái)分析其的局部變量,python規(guī)則指定所有在賦值語(yǔ)句左面的變量都是局部變量,則在閉包bar()中,變量a在賦值符號(hào)"="的左面,被python認(rèn)為是bar()中的局部變量。再接下來(lái)執(zhí)行print c()時(shí),程序運(yùn)行至a = a + 1時(shí),因?yàn)橄惹耙呀?jīng)把a(bǔ)歸為bar()中的局部變量,所以python會(huì)在bar()中去找在賦值語(yǔ)句右面的a的值,結(jié)果找不到,就會(huì)報(bào)錯(cuò)。解決的方法很簡(jiǎn)單

def foo(): 
 a = [1] 
 def bar(): 
  a[0] = a[0] + 1 
  return a[0] 
 return bar

只要將a設(shè)定為一個(gè)容器就可以了。這樣使用起來(lái)多少有點(diǎn)不爽,所以在python3以后,在a = a + 1 之前,使用語(yǔ)句nonlocal a就可以了,該語(yǔ)句顯式的指定a不是閉包的局部變量。

3.還有一個(gè)容易產(chǎn)生錯(cuò)誤的事例也經(jīng)常被人在介紹python閉包時(shí)提起,我一直都沒(méi)覺(jué)得這個(gè)錯(cuò)誤和閉包有什么太大的關(guān)系,但是它倒是的確是在python函數(shù)式編程是容易犯的一個(gè)錯(cuò)誤,我在這里也不妨介紹一下。先看下面這段代碼

for i in range(3): 
 print i

在程序里面經(jīng)常會(huì)出現(xiàn)這類(lèi)的循環(huán)語(yǔ)句,Python的問(wèn)題就在于,當(dāng)循環(huán)結(jié)束以后,循環(huán)體中的臨時(shí)變量i不會(huì)銷(xiāo)毀,而是繼續(xù)存在于執(zhí)行環(huán)境中。還有一個(gè)python的現(xiàn)象是,python的函數(shù)只有在執(zhí)行時(shí),才會(huì)去找函數(shù)體里的變量的值。

flist = [] 
for i in range(3): 
 def foo(x): print x + i 
 flist.append(foo) 
for f in flist: 
 f(2)

可能有些人認(rèn)為這段代碼的執(zhí)行結(jié)果應(yīng)該是2,3,4.但是實(shí)際的結(jié)果是4,4,4。這是因?yàn)楫?dāng)把函數(shù)加入flist列表里時(shí),python還沒(méi)有給i賦值,只有當(dāng)執(zhí)行時(shí),再去找i的值是什么,這時(shí)在第一個(gè)for循環(huán)結(jié)束以后,i的值是2,所以以上代碼的執(zhí)行結(jié)果是4,4,4.
解決方法也很簡(jiǎn)單,改寫(xiě)一下函數(shù)的定義就可以了。

for i in range(3): 
 def foo(x,y=i): print x + y 
 flist.append(foo) 

三、作用

說(shuō)了這么多,不免有人要問(wèn),那這個(gè)閉包在實(shí)際的開(kāi)發(fā)中有什么用呢?閉包主要是在函數(shù)式開(kāi)發(fā)過(guò)程中使用。以下介紹兩種閉包主要的用途。

用途1:當(dāng)閉包執(zhí)行完后,仍然能夠保持住當(dāng)前的運(yùn)行環(huán)境。

比如說(shuō),如果你希望函數(shù)的每次執(zhí)行結(jié)果,都是基于這個(gè)函數(shù)上次的運(yùn)行結(jié)果。我以一個(gè)類(lèi)似棋盤(pán)游戲的例子來(lái)說(shuō)明。假設(shè)棋盤(pán)大小為50*50,左上角為坐標(biāo)系原點(diǎn)(0,0),我需要一個(gè)函數(shù),接收2個(gè)參數(shù),分別為方向(direction),步長(zhǎng)(step),該函數(shù)控制棋子的運(yùn)動(dòng)。棋子運(yùn)動(dòng)的新的坐標(biāo)除了依賴于方向和步長(zhǎng)以外,當(dāng)然還要根據(jù)原來(lái)所處的坐標(biāo)點(diǎn),用閉包就可以保持住這個(gè)棋子原來(lái)所處的坐標(biāo)。

origin = [0, 0] # 坐標(biāo)系統(tǒng)原點(diǎn) 
legal_x = [0, 50] # x軸方向的合法坐標(biāo) 
legal_y = [0, 50] # y軸方向的合法坐標(biāo) 
def create(pos=origin): 
 def player(direction,step): 
  # 這里應(yīng)該首先判斷參數(shù)direction,step的合法性,比如direction不能斜著走,step不能為負(fù)等 
  # 然后還要對(duì)新生成的x,y坐標(biāo)的合法性進(jìn)行判斷處理,這里主要是想介紹閉包,就不詳細(xì)寫(xiě)了。 
  new_x = pos[0] + direction[0]*step 
  new_y = pos[1] + direction[1]*step 
  pos[0] = new_x 
  pos[1] = new_y 
  #注意!此處不能寫(xiě)成 pos = [new_x, new_y],原因在上文有說(shuō)過(guò) 
  return pos 
 return player 
 
player = create() # 創(chuàng)建棋子player,起點(diǎn)為原點(diǎn) 
print player([1,0],10) # 向x軸正方向移動(dòng)10步 
print player([0,1],20) # 向y軸正方向移動(dòng)20步 
print player([-1,0],10) # 向x軸負(fù)方向移動(dòng)10步 

輸出為:

[10, 0] 
[10, 20] 
[0, 20] 

用途2:閉包可以根據(jù)外部作用域的局部變量來(lái)得到不同的結(jié)果,這有點(diǎn)像一種類(lèi)似配置功能的作用,我們可以修改外部的變量,閉包根據(jù)這個(gè)變量展現(xiàn)出不同的功能。比如有時(shí)我們需要對(duì)某些文件的特殊行進(jìn)行分析,先要提取出這些特殊行。

def make_filter(keep): 
 def the_filter(file_name): 
  file = open(file_name) 
  lines = file.readlines() 
  file.close() 
  filter_doc = [i for i in lines if keep in i] 
  return filter_doc 
 return the_filter

如果我們需要取得文件"result.txt"中含有"pass"關(guān)鍵字的行,則可以這樣使用例子程序

filter = make_filter("pass")
filter_result = filter("result.txt")

以上兩種使用場(chǎng)景,用面向?qū)ο笠彩强梢院芎?jiǎn)單的實(shí)現(xiàn)的,但是在用Python進(jìn)行函數(shù)式編程時(shí),閉包對(duì)數(shù)據(jù)的持久化以及按配置產(chǎn)生不同的功能,是很有幫助的。

相信本文所述對(duì)大家的Python程序設(shè)計(jì)有一定的借鑒價(jià)值。

相關(guān)文章

最新評(píng)論