日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

Python闭包与装饰器

發(fā)布時間:2024/4/15 python 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python闭包与装饰器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Python閉包與裝飾器


一、閉包
? ? ? 函數(shù)是一個對象,所以可以對象的形式作為某個函數(shù)的結(jié)果返回。函數(shù)執(zhí)行完后內(nèi)部變量將會被回收。在閉包中,由于內(nèi)部函數(shù)存在對外部函數(shù)的變量的引用,所以即使外部函數(shù)執(zhí)行完畢,該變量依然存在。 ? ? ?閉包簡單來說,外部函數(shù)FunOut()里面包含一個內(nèi)部函數(shù)FunIn(),并且外部函數(shù)返回內(nèi)部函數(shù)的對象FunIn,內(nèi)部函數(shù)存在對外部函數(shù)的變量的引用。那么這個內(nèi)部函數(shù)FunIn就叫做閉包。你在調(diào)用函數(shù)FunA的時候傳遞的參數(shù)就是自由變量。
def FunOut(name):#外部函數(shù)包含內(nèi)部函數(shù)def FunIn(age):print 'name:', name, 'age:', age #內(nèi)部函數(shù)存在對外部函數(shù)變量的引用,如namereturn FunIn #返回內(nèi)部函數(shù)對象FunIn Funin= FunOut('Alian') #Alian傳給參數(shù)name,并且返回函數(shù)FunIn對象給Funin Funin(25) #此時Funin相當(dāng)于調(diào)用函數(shù)FunIn,因此25傳給參數(shù)age 運行輸出: name: Alian age: 25? ? ?這里面調(diào)用FunOut()的時候就產(chǎn)生了一個閉包FunIn,并且該閉包持有自由變量name,因此這也意味著,當(dāng)函數(shù)FunOut()的生命周期結(jié)束之后,自由變量name依然存在,因為它被閉包引用了,所以不會被回收。
def line_conf(a, b):def line(x):return ax + breturn lineline1 = line_conf(1, 1) line2 = line_conf(4, 5) print(line1(5), line2(5))? ? ?這個例子中,函數(shù)line與環(huán)境變量a,b構(gòu)成閉包。在創(chuàng)建閉包的時候,我們通過line_conf的參數(shù)a,b說明了這兩個環(huán)境變量的取值,這樣,我們就確定了函數(shù)的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數(shù)a,b,就可以獲得不同的直線表達函數(shù)。由此,我們可以看到,閉包也具有提高代碼可復(fù)用性的作用。
? ? ?如果沒有閉包,我們需要每次創(chuàng)建直線函數(shù)的時候同時說明a,b,x。這樣,我們就需要更多的參數(shù)傳遞,也減少了代碼的可移植性。利用閉包,我們實際上創(chuàng)建了泛函。line函數(shù)定義一種廣泛意義的函數(shù)。這個函數(shù)的一些方面已經(jīng)確定(必須是直線),但另一些方面(比如a和b參數(shù)待定)。隨后,我們根據(jù)line_conf傳遞來的參數(shù),通過閉包的形式,將最終函數(shù)確定下來。
二、裝飾器概念 ? ? ? ?Python的學(xué)習(xí)教程:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 ? ? ??裝飾器本質(zhì)上是一個Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數(shù)對象。裝飾器接受其他函數(shù)為參數(shù)并返回一個裝飾過的函數(shù)(或其他對象)。

由于函數(shù)也是一個對象,而且函數(shù)對象可以被賦值給變量,所以,通過變量也能調(diào)用該函數(shù)。

>>> def now(): ... print '2013-12-25' ... >>> f = now >>> f() 2013-12-25

函數(shù)對象有一個__name__屬性,可以拿到函數(shù)的名字:

>>> now.__name__ 'now' >>> f.__name__ 'now'

現(xiàn)在,假設(shè)我們要增強now()函數(shù)的功能,比如,在函數(shù)調(diào)用前后自動打印日志,但又不希望修改now()函數(shù)的定義,這種在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)。

本質(zhì)上,decorator就是一個返回函數(shù)的高階函數(shù)。所以,我們要定義一個能打印日志的decorator,可以定義如下:

def log(func):def wrapper(*args, **kw):print 'call %s():' % func.__name__return func(*args, **kw)return wrapper

觀察上面的log,因為它是一個decorator,所以接受一個函數(shù)作為參數(shù),并返回一個函數(shù)。我們要借助Python的@語法,把decorator置于函數(shù)的定義處:

@log def now():print '2013-12-25'

調(diào)用now()函數(shù),不僅會運行now()函數(shù)本身,還會在運行now()函數(shù)前打印一行日志:

>>> now() call now(): 2013-12-25

把@log放到now()函數(shù)的定義處,相當(dāng)于執(zhí)行了語句:

now = log(now)

由于log()是一個decorator,返回一個函數(shù),所以,原來的now()函數(shù)仍然存在,只是現(xiàn)在同名的now變量指向了新的函數(shù),于是調(diào)用now()將執(zhí)行新函數(shù),即在log()函數(shù)中返回的wrapper()函數(shù)。

wrapper()函數(shù)的參數(shù)定義是(*args, **kw),因此,wrapper()函數(shù)可以接受任意參數(shù)的調(diào)用。在wrapper()函數(shù)內(nèi),首先打印日志,再緊接著調(diào)用原始函數(shù)。

如果decorator本身需要傳入?yún)?shù),那就需要編寫一個返回decorator的高階函數(shù),寫出來會更復(fù)雜。比如,要自定義log的文本:

def log(text):def decorator(func):def wrapper(*args, **kw):print '%s %s():' % (text, func.__name__)return func(*args, **kw)return wrapperreturn decorator

這個3層嵌套的decorator用法如下:

@log('execute') def now():print '2013-12-25'

執(zhí)行結(jié)果如下:

>>> now() execute now(): 2013-12-25

和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:

>>> now = log('execute')(now)

我們來剖析上面的語句,首先執(zhí)行l(wèi)og('execute'),返回的是decorator函數(shù),再調(diào)用返回的函數(shù),參數(shù)是now函數(shù),返回值最終是wrapper函數(shù)。

以上兩種decorator的定義都沒有問題,但還差最后一步。因為我們講了函數(shù)也是對象,它有__name__等屬性,但你去看經(jīng)過decorator裝飾之后的函數(shù),它們的__name__已經(jīng)從原來的'now'變成了'wrapper':

>>> now.__name__ 'wrapper'

因為返回的那個wrapper()函數(shù)名字就是'wrapper',所以,需要把原始函數(shù)的__name__等屬性復(fù)制到wrapper()函數(shù)中,否則,有些依賴函數(shù)簽名的代碼執(zhí)行就會出錯。

不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內(nèi)置的functools.wraps就是干這個事的,所以,一個完整的decorator的寫法如下:

import functoolsdef log(func):@functools.wraps(func)def wrapper(*args, **kw):print 'call %s():' % func.__name__return func(*args, **kw)return wrapper

或者針對帶參數(shù)的decorator:

import functoolsdef log(text):def decorator(func):@functools.wraps(func)def wrapper(*args, **kw):print '%s %s():' % (text, func.__name__)return func(*args, **kw)return wrapperreturn decorator

import functools是導(dǎo)入functools模塊。模塊的概念稍候講解。現(xiàn)在,只需記住在定義wrapper()的前面加上@functools.wraps(func)即可。

小結(jié)

在面向?qū)ο?#xff08;OOP)的設(shè)計模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現(xiàn),而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator可以用函數(shù)實現(xiàn),也可以用類實現(xiàn)。

decorator可以增強函數(shù)的功能,定義起來雖然有點復(fù)雜,但使用起來非常靈活和方便。

請編寫一個decorator,能在函數(shù)調(diào)用的前后打印出'begin call'和'end call'的日志。

再思考一下能否寫出一個@log的decorator,使它既支持:

@log def f():pass

又支持:

@log('execute') def f():pass

(1)無參數(shù)裝飾器

# coding=utf-8 def log(func):def wrapper(i, j):print('call %s():' % func.__name__)return func(i, j)return wrapper @log def now(i, j):print('i=%s ,j=%d' % (i, j)) now(1, 2)輸出結(jié)果: call now(): i=1 ,j=2 ? ? 上例,無參的裝飾器,相當(dāng)于直接調(diào)用: now=log(now) now(1, 2)
(2)有參數(shù)裝飾器 ? ??如果decorator本身需要傳入?yún)?shù),則需要使用三層嵌套關(guān)系了 # coding=utf-8 def log(text):def decorator(func):def wrapper(i, j):print('%s %s():' % (text, func.__name__))return func(i, j)return wrapperreturn decorator @log('execute') def now(i, j):print('i=%s ,j=%d' %(i ,j )) now = log("Alian")(now) now(1, 2)
1. 原文請參考:http://blog.csdn.net/thy38/article/details/4471421 2.Python裝飾器學(xué)習(xí) :http://blog.csdn.NET/thy38/article/details/4471421
3. Python裝飾器與面向切面編程 :http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
4. Python裝飾器的理解: http://apps.hi.baidu.com/share/detail/17572338

總結(jié)

以上是生活随笔為你收集整理的Python闭包与装饰器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。