真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

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

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

一、閉包是什么

官方定義:

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

我的理解

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

Python 中的閉包

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

下面我們來(lái)看一個(gè)例子你就清楚了:

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

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

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

下面我們來(lái)看一下Python的閉包實(shí)現(xiàn)過(guò)程:

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

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

我們?cè)賮?lái)看看閉包的一些屬性

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

我們既然了解了python中閉包的基本實(shí)現(xiàn)方法,那我們?cè)賮?lái)多看幾個(gè)例子:

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

0

1

2

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

2

2

2

為什么會(huì)這樣呢?

那是因?yàn)樵趏uter函數(shù)內(nèi),inner相對(duì)于outer來(lái)說(shuō)其只是內(nèi)部定義的一個(gè)函數(shù),變量 i 的作用域還是在outer中,所以

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

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

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

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

        鄭州人流??漆t(yī)院 http://www.03912316666.com/

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

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

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

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

下面是官方對(duì)閉包過(guò)程中變量作用域的一些解釋:

““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”對(duì)象用于實(shí)現(xiàn)由多個(gè)作用域引用的變量。對(duì)于每個(gè)這樣的變量,創(chuàng)建一個(gè)cell對(duì)象來(lái)存儲(chǔ)值;引用該值的每個(gè)堆棧幀的局部變量包含對(duì)來(lái)自外部作用域的單元格的引用,外部作用域也使用該變量。當(dāng)訪問(wèn)該值時(shí),將使用單元格中包含的值,而不是單元格對(duì)象本身。單元格對(duì)象的取消引用需要生成的字節(jié)碼的支持;這些不會(huì)在訪問(wèn)時(shí)自動(dòng)取消引用。單元格對(duì)象不太可能在其他地方有用。

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

二、為什么要有閉包

閉包避免了使用全局變量,此外,閉包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)連起來(lái)。這一點(diǎn)與面向?qū)ο缶幊淌欠浅n?lèi)似的,在面對(duì)象編程中,對(duì)象允許我們將某些數(shù)據(jù)(對(duì)象的屬性)與一個(gè)或者多個(gè)方法相關(guān)聯(lián)。

一般來(lái)說(shuō),當(dāng)對(duì)象中只有一個(gè)方法時(shí),這時(shí)使用閉包是更好的選擇。

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

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

用類(lèi)來(lái)實(shí)現(xiàn):

用閉包來(lái)實(shí)現(xiàn)

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

閉包的用處 ——裝飾器

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

一聽(tīng)這個(gè)名字就知道裝飾器的作用,裝飾器嘛那肯定是用來(lái)裝飾的嘛,那它是用來(lái)裝飾什么的呢,其實(shí)就是用來(lái)裝飾Python中的一些對(duì)象的.

可能小伙伴在了解裝飾器前,在接觸到類(lèi)的時(shí)候就看見(jiàn)有的類(lèi)方法上面有一些特殊的符號(hào).

像這樣的

像這樣用 @ 符號(hào)進(jìn)行修飾的關(guān)鍵字其實(shí)就是裝飾器.

那么我們先來(lái)定義一個(gè)簡(jiǎn)單的裝飾器來(lái)看一下:

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

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

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

其實(shí) @ 符號(hào)在這里的作用就是 讓程序自動(dòng)執(zhí)行一條這樣的語(yǔ)句 “f = decorator(f)” 不妨我們來(lái)看看就知道了:

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

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

好了我們?cè)賮?lái)看一些更復(fù)雜的裝飾器加深理解:

帶參的裝飾器:

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

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

(1) 第一種做法:

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

(1) 第二種做法:

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

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

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

我們?cè)賮?lái)看下面一種更為優(yōu)雅的寫(xiě)法:

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

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

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

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


本文題目:Python函數(shù)閉包是什么?怎么實(shí)現(xiàn)裝飾器
本文鏈接:http://weahome.cn/article/iphsgc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部