欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Python函數(shù)閉包是什么?怎么實現(xiàn)裝飾器

很多初次接觸到python的小伙伴可能并不理解閉包是什么,為什么有閉包,閉包有什么用,那么今天博主就從這三點來為大家講解一下python的閉包

為成都等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及成都網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為成都網(wǎng)站建設(shè)、成都做網(wǎng)站、成都網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!

一、閉包是什么

官方定義:

在計算機科學中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是引用了自由變量的函數(shù)。這個被引用的自由變量將和這個函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以,有另一種說法認為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。閉包在運行時可以有多個實例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實例。

我的理解

在一個函數(shù)的內(nèi)部嵌套了一個函數(shù),并且這個函數(shù)引用了外部函數(shù)中的局部變量,那么這個內(nèi)部函數(shù)就稱為閉包

Python 中的閉包

Python 中閉包的實現(xiàn)得益于Python是一門面向?qū)ο缶幊痰恼Z言,函數(shù)在Python中也不例外,在Python中函數(shù)也是作為對象(函數(shù)對象)的方式進行存在

下面我們來看一個例子你就清楚了:

Python函數(shù)閉包是什么?怎么實現(xiàn)裝飾器

在這里我們首先定義了一個函數(shù),然后我們使用Python中的內(nèi)部方法(type),type(args)的作用是查看傳入?yún)?shù)args的類型,我們可以看到func的類型就是一個函數(shù)對象

我們都知道函數(shù)是可以返回值的,所以在Python中函數(shù)對象也可以作為值進行返回,這樣就為閉包的實現(xiàn)提供了基礎(chǔ).

下面我們來看一下Python的閉包實現(xiàn)過程:

Python函數(shù)閉包是什么?怎么實現(xiàn)裝飾器

在這里我們首先定義了一個名為outer的函數(shù),然后在這個函數(shù)的內(nèi)部我們又定義了一個名為"out_value"的變量,然后又在函數(shù)的內(nèi)部定義了一個名為inner的內(nèi)部函數(shù),在inner函數(shù)內(nèi)我們又引用了外部函數(shù)(outer)的變量,這樣我們就稱inner為閉包

我們再來看看閉包的一些屬性

我們調(diào)用并執(zhí)行outer函數(shù)并將其返回值賦給變量f,我們通過打印可以看到,變量f的類型為函數(shù)類型,因為是函數(shù)對象所以f必然是具有 ‘_call_’ 屬性的(就是函數(shù)后面的() 也就是將函數(shù)執(zhí)行) 然后我們再來看f變量的名稱,可以看到f是名為inner的函數(shù)對象,為什么會這樣是因為,在outer函數(shù)執(zhí)行完畢后,在其返回的時候我們將在outer函數(shù)內(nèi)部定義的inner函數(shù)對象進行返回了,所以f變量的名稱為inner

我們既然了解了python中閉包的基本實現(xiàn)方法,那我們再來多看幾個例子:

上面這個例子大家在沒有看答案的情況下,大家猜一下結(jié)果會是怎樣呢,可能有部分小伙伴已經(jīng)看透了這其中的貓膩,那部分沒有看出的小伙伴心中的結(jié)果是不是

0

1

2

這樣呢? 那我這里就不賣關(guān)子了,其實上面的這種結(jié)果是錯誤的,正確的結(jié)果是:

2

2

2

為什么會這樣呢?

那是因為在outer函數(shù)內(nèi),inner相對于outer來說其只是內(nèi)部定義的一個函數(shù),變量 i 的作用域還是在outer中,所以

現(xiàn)在inner對于outer來說并不是閉包,所以 i 的改變是可以改變inner內(nèi)部變量i的值的.當循環(huán)最后執(zhí)行完畢時,i變量的值是2 這時i就不會改變了,然后在返回的是這個閉包列表,所以打印出來的應(yīng)該全是2

那么我們再來看一個例子,假如現(xiàn)在我有一個需求,內(nèi)容是我需要有一個函數(shù),我在調(diào)用這個函數(shù)時打印出當前是第幾次調(diào)用,如果了解迭代器的小伙伴應(yīng)該實現(xiàn)起來很輕松,但是這里需要用閉包的方式實現(xiàn)這個函數(shù),怎么實現(xiàn)呢.

可能有小伙伴就有了下面的思路:

Python函數(shù)閉包是什么?怎么實現(xiàn)裝飾器

        鄭州人流專科醫(yī)院 http://www.03912316666.com/

但是很遺憾的告訴你,這樣的做法是錯誤的,不妨我們來看一下運行結(jié)果:

分析錯誤原因,大概的意思就是 局部變量“cnt”在賦值之前引用 ,它告訴我們不能在引用后進行賦值,因為這個變量的作用域還是在外部函數(shù)內(nèi)的,那怎樣解決這樣的問題呢,Python就提供了很好的一個保留字用來聲明 非局部變量( nonlocal 關(guān)鍵字)

我們來看一下修改后的執(zhí)行效果:

可以看到通過nonlocal 聲明變量cnt之后就得到了我們想要的結(jié)果

下面是官方對閉包過程中變量作用域的一些解釋:

““Cell” objects are used to implement variables referenced by multiple scopes. For each such variable, a cell object is created to store the value; the local variables of each stack frame that references the value contains a reference to the cells from outer scopes which also use that variable. When the value is accessed, the value contained in the cell is used instead of the cell object itself. This de-referencing of the cell object requires support from the generated byte-code; these are not automatically de-referenced when accessed. Cell objects are not likely to be useful elsewhere.”

“Cell”對象用于實現(xiàn)由多個作用域引用的變量。對于每個這樣的變量,創(chuàng)建一個cell對象來存儲值;引用該值的每個堆棧幀的局部變量包含對來自外部作用域的單元格的引用,外部作用域也使用該變量。當訪問該值時,將使用單元格中包含的值,而不是單元格對象本身。單元格對象的取消引用需要生成的字節(jié)碼的支持;這些不會在訪問時自動取消引用。單元格對象不太可能在其他地方有用。

這樣大家應(yīng)該就明白了.

二、為什么要有閉包

閉包避免了使用全局變量,此外,閉包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)連起來。這一點與面向?qū)ο缶幊淌欠浅n愃频模诿鎸ο缶幊讨校瑢ο笤试S我們將某些數(shù)據(jù)(對象的屬性)與一個或者多個方法相關(guān)聯(lián)。

一般來說,當對象中只有一個方法時,這時使用閉包是更好的選擇。

下面我們使用兩種不同的方法來實現(xiàn)同一種需求,在比較下就可以知道那種方式更有優(yōu)越性:

需求: 我們有一個名為Number 的數(shù)字對象,我們要對其實現(xiàn)加法運算的法則

用類來實現(xiàn):

用閉包來實現(xiàn)

我們可以看到用閉包的方式來實現(xiàn)時可以使得代碼更為的簡便,這就是為什要使用閉包的原因.

閉包的用處 ——裝飾器

既然前面講了這么多閉包的知識,和應(yīng)用,那么這里就來說一下閉包在Python中最大的一個用處,那就是"裝飾器".

一聽這個名字就知道裝飾器的作用,裝飾器嘛那肯定是用來裝飾的嘛,那它是用來裝飾什么的呢,其實就是用來裝飾Python中的一些對象的.

可能小伙伴在了解裝飾器前,在接觸到類的時候就看見有的類方法上面有一些特殊的符號.

像這樣的

像這樣用 @ 符號進行修飾的關(guān)鍵字其實就是裝飾器.

那么我們先來定義一個簡單的裝飾器來看一下:

上面的 decorator便是我們自定義的一個裝飾器,在下面我們定義的f函數(shù)我們對其使用了裝飾器進行裝飾,

我們發(fā)現(xiàn)原本f函數(shù)內(nèi)只有 “>>>> 正在執(zhí)行” 這一行打印信息,但經(jīng)過裝飾器裝飾后,就變成了兩行的打印信息,

是不是很神奇,凡是都是有原因的,現(xiàn)在就來揭秘一下這里面的玄機.

其實 @ 符號在這里的作用就是 讓程序自動執(zhí)行一條這樣的語句 “f = decorator(f)” 不妨我們來看看就知道了:

Python函數(shù)閉包是什么?怎么實現(xiàn)裝飾器

咦 我們發(fā)現(xiàn)我們定義的函數(shù)f它的名字被換成了wrapper,這不正是我們裝飾器函數(shù)返回閉包嘛,這樣結(jié)合前面的閉包知識,大家發(fā)現(xiàn)裝飾器其實也沒那么難嘛.

好了我們再來看一些更復(fù)雜的裝飾器加深理解:

帶參的裝飾器:

上面我們實現(xiàn)來在裝飾器中傳入?yún)?shù)的做法,我們?yōu)楫斍暗暮瘮?shù)取了一個名字,然后在執(zhí)行的時候?qū)⑵浯蛴〕鰜?我們發(fā)現(xiàn)要進行傳參時,我們整個函數(shù)的嵌套變?yōu)榱巳龑?當然和前面不帶參的裝飾器的工作機理是差不多的,換湯不換藥. 帶參數(shù)的裝飾器無非在使用的過程中進行了如下的操作 f = set_name(“Nick”)(f) 這樣一說應(yīng)該就明白了.

裝飾器需要裝飾的函數(shù)帶參問題:

(1) 第一種做法:

顯然這樣的做法做出來的裝飾器通用性并不強,如果我們需要裝飾的函數(shù)參數(shù)一發(fā)生變化那我們的裝飾器就不能使用了,這肯定是我們不能接受的.

(1) 第二種做法:

和前一種方法效果一樣,但是使用包裹參數(shù)和包裹關(guān)鍵字參數(shù)進行傳參,通用性就強很多了,所以推薦這樣寫.

我們在講第一個裝飾器的時候大家就發(fā)現(xiàn)了一個問題,那就是函數(shù)經(jīng)過裝飾器修飾后,原來的函數(shù)名稱就發(fā)生了改變,統(tǒng)一的都命名成了我們裝飾器內(nèi)部定義的函數(shù)名,那我如果還是想要原來的函數(shù)名怎么辦呢,別急有下面的第一種做法:

這樣做其實就是強行改變其對象的名稱,這樣的做法肯定就顯得不是那么優(yōu)雅了,在Python中也是不推崇的.

我們再來看下面一種更為優(yōu)雅的寫法:

這下就對了,正如老爹所說我們要用魔法來對付魔法,這里用裝飾器的來解決裝飾器的問題,就顯得優(yōu)雅許多了,

如果感興趣的小伙伴不妨自己思考一下functools.wraps 這個裝飾器的實現(xiàn)思路,然后自己動手實現(xiàn)一次,相信對你學習裝飾器有一定的幫助.這里介于篇幅的原因就不講解實現(xiàn)的思路了.

下面來最后一個例子,我們用類的方式來實現(xiàn)一次裝飾器看看:

對于類中的_call_ 方法在前面已經(jīng)提過在這里就不講了,這里的裝飾器的調(diào)用過程和前面第一種裝飾器的調(diào)用過程是一樣的 f = MyDecorator(f) 相當于f就是一個由MyDecorator類實例化出來的一個對象,當這個對象f在執(zhí)行f()時就觸發(fā)了MyDecorator中的__call__方法.

當前標題:Python函數(shù)閉包是什么?怎么實現(xiàn)裝飾器
當前地址:http://www.chinadenli.net/article12/iphsgc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷網(wǎng)站導航響應(yīng)式網(wǎng)站企業(yè)建站網(wǎng)站建設(shè)面包屑導航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)