Ⅰ python裝飾器是什麼意思
裝飾器是程序開發中經常會用到的一個功能,用好了裝飾器,開發效率如虎添翼,所以這也是Python面試中必問的問題,但對於好多小白來講,這個功能 有點繞,自學時直接繞過去了,然後面試問到了就掛了,因為裝飾器是程序開發的基礎知識,這個都 不會,別跟人家說你會Python, 看了下面的文章,保證你學會裝飾器。
1、先明白這段代碼
####第一波####
deffoo():
print'foo'
foo#表示是函數
foo()#表示執行foo函數
####第二波####
deffoo():
print'foo'
foo=lambdax:x+1
foo()#執行下面的lambda表達式,而不再是原來的foo函數,因為函數foo被重新定義了
2、需求來了
初創公司有N個業務部門,1個基礎平台部門,基礎平台負責提供底層的功能,如:資料庫操作、redis調用、監控API等功能。業務部門使用基礎功能時,只需調用基礎平台提供的功能即可。如下:
###############基礎平台提供的功能如下###############
deff1():
print'f1'
deff2():
print'f2'
deff3():
print'f3'
deff4():
print'f4'
###############業務部門A調用基礎平台提供的功能###############
f1()
f2()
f3()
f4()
###############業務部門B調用基礎平台提供的功能###############
f1()
f2()
f3()
f4()
目前公司有條不紊的進行著,但是,以前基礎平台的開發人員在寫代碼時候沒有關注驗證相關的問題,即:基礎平台的提供的功能可以被任何人使用。現在需要對基礎平台的所有功能進行重構,為平台提供的所有功能添加驗證機制,即:執行功能前,先進行驗證。
老大把工作交給 Low B,他是這么做的:
跟每個業務部門交涉,每個業務部門自己寫代碼,調用基礎平台的功能之前先驗證。誒,這樣一來基礎平台就不需要做任何修改了。
當天Low B 被開除了…
老大把工作交給 Low BB,他是這么做的:
###############基礎平台提供的功能如下###############
deff1():
#驗證1
#驗證2
#驗證3
print'f1'
deff2():
#驗證1
#驗證2
#驗證3
print'f2'
deff3():
#驗證1
#驗證2
#驗證3
print'f3'
deff4():
#驗證1
#驗證2
#驗證3
print'f4'
###############業務部門不變###############
###業務部門A調用基礎平台提供的功能###
f1()
f2()
f3()
f4()
###業務部門B調用基礎平台提供的功能###
f1()
f2()
f3()
f4()
過了一周 Low BB 被開除了…
老大把工作交給 Low BBB,他是這么做的:
只對基礎平台的代碼進行重構,其他業務部門無需做任何修改
###############基礎平台提供的功能如下###############
defcheck_login():
#驗證1
#驗證2
#驗證3
pass
deff1():
check_login()
print'f1'
deff2():
check_login()
print'f2'
deff3():
check_login()
print'f3'
deff4():
check_login()
print'f4'
老大看了下Low BBB 的實現,嘴角漏出了一絲的欣慰的笑,語重心長的跟Low BBB聊了個天:
老大說:
寫代碼要遵循開發封閉原則,雖然在這個原則是用的面向對象開發,但是也適用於函數式編程,簡單來說,它規定已經實現的功能代碼不允許被修改,但可以被擴展,即:
封閉:已實現的功能代碼塊
開放:對擴展開發
如果將開放封閉原則應用在上述需求中,那麼就不允許在函數 f1 、f2、f3、f4的內部進行修改代碼,老闆就給了Low BBB一個實現方案:
defw1(func):
definner():
#驗證1
#驗證2
#驗證3
returnfunc()
returninner
@w1
deff1():
print'f1'
@w1
deff2():
print'f2'
@w1
deff3():
print'f3'
@w1
deff4():
print'f4'
對於上述代碼,也是僅僅對基礎平台的代碼進行修改,就可以實現在其他人調用函數 f1 f2 f3 f4 之前都進行【驗證】操作,並且其他業務部門無需做任何操作。
Low BBB心驚膽戰的問了下,這段代碼的內部執行原理是什麼呢?
老大正要生氣,突然Low BBB的手機掉到地上,恰恰屏保就是Low BBB的女友照片,老大一看一緊一抖,喜笑顏開,交定了Low BBB這個朋友。詳細的開始講解了:
單獨以f1為例:
defw1(func):
definner():
#驗證1
#驗證2
#驗證3
returnfunc()
returninner
@w1
deff1():
print'f1'
當寫完這段代碼後(函數未被執行、未被執行、未被執行),python解釋器就會從上到下解釋代碼,步驟如下:
def w1(func): ==>將w1函數載入到內存
@w1
沒錯,從表面上看解釋器僅僅會解釋這兩句代碼,因為函數在沒有被調用之前其內部代碼不會被執行。
從表面上看解釋器著實會執行這兩句,但是 @w1 這一句代碼里卻有大文章,@函數名是python的一種語法糖。
如上例@w1內部會執行一下操作:
執行w1函數,並將 @w1 下面的函數作為w1函數的參數,即:@w1 等價於 w1(f1)
所以,內部就會去執行:
def inner:
#驗證
return f1() # func是參數,此時 func 等於 f1
return inner # 返回的 inner,inner代表的是函數,非執行函數
其實就是將原來的 f1 函數塞進另外一個函數中
將執行完的 w1 函數返回值賦值給@w1下面的函數的函數名
w1函數的返回值是:
def inner:
#驗證
return 原來f1() # 此處的 f1 表示原來的f1函數
然後,將此返回值再重新賦值給 f1,即:
新f1 =def inner:
#驗證
return 原來f1()
所以,以後業務部門想要執行 f1 函數時,就會執行 新f1 函數,在 新f1 函數內部先執行驗證,再執行原來的f1函數,然後將 原來f1 函數的返回值 返回給了業務調用者。
如此一來, 即執行了驗證的功能,又執行了原來f1函數的內容,並將原f1函數返回值 返回給業務調用著
Low BBB 你明白了嗎?要是沒明白的話,我晚上去你家幫你解決吧!!!
先把上述流程看懂,之後還會繼續更新…
3、問答時間
問題:被裝飾的函數如果有參數呢?
#一個參數
defw1(func):
definner(arg):
#驗證1
#驗證2
#驗證3
returnfunc(arg)
returninner
@w1
deff1(arg):
print'f1'
#兩個參數
defw1(func):
definner(arg1,arg2):
#驗證1
#驗證2
#驗證3
returnfunc(arg1,arg2)
returninner
@w1
deff1(arg1,arg2):
print'f1'
#三個參數
defw1(func):
definner(arg1,arg2,arg3):
#驗證1
#驗證2
#驗證3
returnfunc(arg1,arg2,arg3)
returninner
@w1
deff1(arg1,arg2,arg3):
print'f1'
問題:可以裝飾具有處理n個參數的函數的裝飾器?
defw1(func):
definner(*args,**kwargs):
#驗證1
#驗證2
#驗證3
returnfunc(*args,**kwargs)
returninner
@w1
deff1(arg1,arg2,arg3):
print'f1'
問題:一個函數可以被多個裝飾器裝飾嗎?
defw1(func):
definner(*args,**kwargs):
#驗證1
#驗證2
#驗證3
returnfunc(*args,**kwargs)
returninner
defw2(func):
definner(*args,**kwargs):
#驗證1
#驗證2
#驗證3
returnfunc(*args,**kwargs)
returninner
@w1
@w2
deff1(arg1,arg2,arg3):
print'f1'
問題:還有什麼更吊的裝飾器嗎?
#!/usr/bin/envpython
#coding:utf-8
defBefore(request,kargs):
print'before'
defAfter(request,kargs):
print'after'
defFilter(before_func,after_func):
defouter(main_func):
defwrapper(request,kargs):
before_result=before_func(request,kargs)
if(before_result!=None):
returnbefore_result;
main_result=main_func(request,kargs)
if(main_result!=None):
returnmain_result;
after_result=after_func(request,kargs)
if(after_result!=None):
returnafter_result;
returnwrapper
returnouter
@Filter(Before,After)
defIndex(request,kargs):
print'index'
Ⅱ Python裝飾器是怎麼實現的
簡單來講,可以不嚴謹地把Python的裝飾器看做一個包裝函數的函數。 比如,有一個函數: def func(): print 'func() run.' if '__main__' == __name__: func() 運行後將輸出: func() run. 現在需要在函數運行前後列印一條日誌
Ⅲ python裝飾器的作用和功能
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用於有切面需求的場景,比如:插入日誌、性能測試、事務處理、緩存、許可權校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能
Ⅳ python裝飾器有什麼用
先來個形象比方
內褲可以用來遮羞,但是到了冬天它沒法為我們防風禦寒,聰明的人們發明了長褲,有了長褲後寶寶再也不冷了,裝飾器就像我們這里說的長褲,在不影響內褲作用的前提下,給我們的身子提供了保暖的功效。
再回到我們的主題
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用於有切面需求的場景,比如:插入日誌、性能測試、事務處理、緩存、許可權校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。
先來看一個簡單例子:
def foo():
print('i am foo')
現在有一個新的需求,希望可以記錄下函數的執行日誌,於是在代碼中添加日誌代碼:
def foo():
print('i am foo')
logging.info("foo is running")
bar()、bar2()也有類似的需求,怎麼做?再寫一個logging在bar函數里?這樣就造成大量雷同的代碼,為了減少重復寫代碼,我們可以這樣做,重新定義一個函數:專門處理日誌 ,日誌處理完之後再執行真正的業務代碼
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func()def bar():
print('i am bar')use_logging(bar)
邏輯上不難理解,
但是這樣的話,我們每次都要將一個函數作為參數傳遞給use_logging函數。而且這種方式已經破壞了原有的代碼邏輯結構,之前執行業務邏輯時,執行運行bar(),但是現在不得不改成use_logging(bar)。那麼有沒有更好的方式的呢?當然有,答案就是裝飾器。
簡單裝飾器
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapperdef bar():
print('i am bar')bar = use_logging(bar)bar()
函數use_logging就是裝飾器,它把執行真正業務方法的func包裹在函數裡面,看起來像bar被use_logging裝飾了。在這個例子中,函數進入和退出時
,被稱為一個橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。
@符號是裝飾器的語法糖,在定義函數的時候使用,避免再一次賦值操作
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper@use_loggingdef foo():
print("i am foo")@use_loggingdef bar():
print("i am bar")bar()
如上所示,這樣我們就可以省去bar =
use_logging(bar)這一句了,直接調用bar()即可得到想要的結果。如果我們有其他的類似函數,我們可以繼續調用裝飾器來修飾函數,而不用重復修改函數或者增加新的封裝。這樣,我們就提高了程序的可重復利用性,並增加了程序的可讀性。
裝飾器在Python使用如此方便都要歸因於Python的函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被賦值給其他變數,可以作為返回值,可以被定義在另外一個函數內。
帶參數的裝飾器
裝飾器還有更大的靈活性,例如帶參數的裝飾器:在上面的裝飾器調用中,比如@use_logging,該裝飾器唯一的參數就是執行業務的函數。裝飾器的語法允許我們在調用時,提供其它參數,比如@decorator(a)。這樣,就為裝飾器的編寫和使用提供了更大的靈活性。
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator@use_logging(level="warn")def foo(name='foo'):
print("i am %s" % name)foo()
上面的use_logging是允許帶參數的裝飾器。它實際上是對原有裝飾器的一個函數封裝,並返回一個裝飾器。我們可以將它理解為一個含有參數的閉包。當我
們使用@use_logging(level="warn")調用的時候,Python能夠發現這一層的封裝,並把參數傳遞到裝飾器的環境中。
類裝飾器
再來看看類裝飾器,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器還可以依靠類內部的\_\_call\_\_方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
functools.wraps
使用裝飾器極大地復用了代碼,但是他有一個缺點就是原函數的元信息不見了,比如函數的docstring、__name__、參數列表,先看例子:
裝飾器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
函數
@loggeddef f(x):
"""does some math"""
return x + x * x
該函數完成等價於:
def f(x):
"""does some math"""
return x + x * xf = logged(f)
不難發現,函數f被with_logging取代了,當然它的docstring,__name__就是變成了with_logging函數的信息了。
print f.__name__ # prints 'with_logging'print f.__doc__ # prints None
這個問題就比較嚴重的,好在我們有functools.wraps,wraps本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器函數中,這使得裝飾器函數也有和原函數一樣的元信息了。
from functools import wrapsdef logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging@loggeddef f(x):
"""does some math"""
return x + x * xprint f.__name__ # prints 'f'print f.__doc__ # prints 'does some math'
內置裝飾器
@staticmathod、@classmethod、@property
裝飾器的順序
@a@b@cdef f ():
等效於
f = a(b(c(f)))
Ⅳ python 裝飾器原理
官方文檔如下:
Python 2.2 extended Python's object model by adding static methods and class methods, but it didn't extend Python's syntax to provide any new way of defining static or class methods. Instead, you had to write a statement in the usual way, and pass the resulting method to a or function that would wrap up the function as a method of the new type.
Python
Ⅵ Python中的裝飾器是作什麼用的在哪可以找到這些教程。
裝飾器是Python語法糖的一種,可以用來簡化代碼,讓代碼更加簡潔
裝飾器的作用是在不改變函數代碼和調用方式的前提下,為已有函數功能添加額外的功能。
可以通過裝飾器對代碼實現許可權管理,用戶驗證,日誌管理,緩存判斷,參數檢查等等。
以上內容均來自傳智播客,自己可以去搜搜,很多課。
Ⅶ 學習Python需要哪些准備
小蝸這里整理了一份Python全棧開發系統的學習路線,每個階段所要掌握的知識都已列出,題主可參考這份大綱來進行學習規劃;
第一階段:專業核心基礎
階段目標:
1. 熟練掌握Python的開發環境與編程核心知識
2. 熟練運用Python面向對象知識進行程序開發
3. 對Python的核心庫和組件有深入理解
4. 熟練應用SQL語句進行資料庫常用操作
5. 熟練運用Linux操作系統命令及環境配置
6. 熟練使用MySQL,掌握資料庫高級操作
7. 能綜合運用所學知識完成項目
知識點:
Python編程基礎、Python面向對象、Python高級進階、MySQL資料庫、Linux操作系統。
1、Python編程基礎,語法規則,函數與參數,數據類型,模塊與包,文件IO,培養扎實的Python編程基本功,同時對Python核心對象和庫的編程有熟練的運用。
2、Python面向對象,核心對象,異常處理,多線程,網路編程,深入理解面向對象編程,異常處理機制,多線程原理,網路協議知識,並熟練運用於項目中。
3、類的原理,MetaClass,下劃線的特殊方法,遞歸,魔術方法,反射,迭代器,裝飾器,UnitTest,Mock。深入理解面向對象底層原理,掌握Python開發高級進階技術,理解單元測試技術。
4、資料庫知識,範式,MySQL配置,命令,建庫建表,數據的增刪改查,約束,視圖,存儲過程,函數,觸發器,事務,游標,PDBC,深入理解資料庫管理系統通用知識及MySQL資料庫的使用與管理。為Python後台開發打下堅實基礎。
5、Linux安裝配置,文件目錄操作,VI命令,管理,用戶與許可權,環境配置,Docker,Shell編程Linux作為一個主流的伺服器操作系統,是每一個開發工程師必須掌握的重點技術,並且能夠熟練運用。
第二階段:PythonWEB開發
階段目標:
1. 熟練掌握Web前端開發技術,HTML,CSS,JavaScript及前端框架
2. 深入理解Web系統中的前後端交互過程與通信協議
3. 熟練運用Web前端和Django和Flask等主流框架完成Web系統開發
4. 深入理解網路協議,分布式,PDBC,AJAX,JSON等知識
5. 能夠運用所學知識開發一個MiniWeb框架,掌握框架實現原理
6. 使用Web開發框架實現貫穿項目
知識點:
Web前端編程、Web前端高級、Django開發框架、Flask開發框架、Web開發項目實戰。
1、Web頁面元素,布局,CSS樣式,盒模型,JavaScript,JQuery與Bootstrap掌握前端開發技術,掌握JQuery與BootStrap前端開發框架,完成頁面布局與美化。
2、前端開發框架Vue,JSON數據,網路通信協議,Web伺服器與前端交互熟練使用Vue框架,深入理解HTTP網路協議,熟練使用Swagger,AJAX技術實現前後端交互。
3、自定義Web開發框架,Django框架的基本使用,Model屬性及後端配置,Cookie與Session,模板Templates,ORM數據模型,Redis二級緩存,RESTful,MVC模型掌握Django框架常用API,整合前端技術,開發完整的WEB系統和框架。
4、Flask安裝配置,App對象的初始化和配置,視圖函數的路由,Request對象,Abort函數,自定義錯誤,視圖函數的返回值,Flask上下文和請求鉤子,模板,資料庫擴展包Flask-Sqlalchemy,資料庫遷移擴展包Flask-Migrate,郵件擴展包Flask-Mail。掌握Flask框架的常用API,與Django框架的異同,並能獨立開發完整的WEB系統開發。
第三階段:爬蟲與數據分析
階段目標:
1. 熟練掌握爬蟲運行原理及常見網路抓包工具使用,能夠對HTTP及HTTPS協議進行抓包分析
2. 熟練掌握各種常見的網頁結構解析庫對抓取結果進行解析和提取
3. 熟練掌握各種常見反爬機制及應對策略,能夠針對常見的反爬措施進行處理
4. 熟練使用商業爬蟲框架Scrapy編寫大型網路爬蟲進行分布式內容爬取
5. 熟練掌握數據分析相關概念及工作流程
6. 熟練掌握主流數據分析工具Numpy、Pandas和Matplotlib的使用
7. 熟練掌握數據清洗、整理、格式轉換、數據分析報告編寫
8. 能夠綜合利用爬蟲爬取豆瓣網電影評論數據並完成數據分析全流程項目實戰
知識點:
網路爬蟲開發、數據分析之Numpy、數據分析之Pandas。
1、爬蟲頁面爬取原理、爬取流程、頁面解析工具LXML,Beautifulfoup,正則表達式,代理池編寫和架構、常見反爬措施及解決方案、爬蟲框架結構、商業爬蟲框架Scrapy,基於對爬蟲爬取原理、網站數據爬取流程及網路協議的分析和了解,掌握網頁解析工具的使用,能夠靈活應對大部分網站的反爬策略,具備獨立完成爬蟲框架的編寫能力和熟練應用大型商業爬蟲框架編寫分布式爬蟲的能力。
2、Numpy中的ndarray數據結構特點、numpy所支持的數據類型、自帶的數組創建方法、算術運算符、矩陣積、自增和自減、通用函數和聚合函數、切片索引、ndarray的向量化和廣播機制,熟悉數據分析三大利器之一Numpy的常見使用,熟悉ndarray數據結構的特點和常見操作,掌握針對不同維度的ndarray數組的分片、索引、矩陣運算等操作。
3、Pandas裡面的三大數據結構,包括Dataframe、Series和Index對象的基本概念和使用,索引對象的更換及刪除索引、算術和數據對齊方法,數據清洗和數據規整、結構轉換,熟悉數據分析三大利器之一Pandas的常見使用,熟悉Pandas中三大數據對象的使用方法,能夠使用Pandas完成數據分析中最重要的數據清洗、格式轉換和數據規整工作、Pandas對文件的讀取和操作方法。
4、matplotlib三層結構體系、各種常見圖表類型折線圖、柱狀圖、堆積柱狀圖、餅圖的繪制、圖例、文本、標線的添加、可視化文件的保存,熟悉數據分析三大利器之一Matplotlib的常見使用,熟悉Matplotlib的三層結構,能夠熟練使用Matplotlib繪制各種常見的數據分析圖表。能夠綜合利用課程中所講的各種數據分析和可視化工具完成股票市場數據分析和預測、共享單車用戶群里數據分析、全球幸福指數數據分析等項目的全程實戰。
第四階段:機器學習與人工智慧
階段目標:
1. 理解機器學習相關的基本概念及系統處理流程
2. 能夠熟練應用各種常見的機器學習模型解決監督學習和非監督學習訓練和測試問題,解決回歸、分類問題
3. 熟練掌握常見的分類演算法和回歸演算法模型,如KNN、決策樹、隨機森林、K-Means等
4. 掌握卷積神經網路對圖像識別、自然語言識別問題的處理方式,熟悉深度學習框架TF裡面的張量、會話、梯度優化模型等
5. 掌握深度學習卷積神經網路運行機制,能夠自定義卷積層、池化層、FC層完成圖像識別、手寫字體識別、驗證碼識別等常規深度學習實戰項目
知識點:
1、機器學習常見演算法、sklearn數據集的使用、字典特徵抽取、文本特徵抽取、歸一化、標准化、數據主成分分析PCA、KNN演算法、決策樹模型、隨機森林、線性回歸及邏輯回歸模型和演算法。熟悉機器學習相關基礎概念,熟練掌握機器學習基本工作流程,熟悉特徵工程、能夠使用各種常見機器學習演算法模型解決分類、回歸、聚類等問題。
2、Tensorflow相關的基本概念,TF數據流圖、會話、張量、tensorboard可視化、張量修改、TF文件讀取、tensorflow playround使用、神經網路結構、卷積計算、激活函數計算、池化層設計,掌握機器學習和深度學習之前的區別和練習,熟練掌握深度學習基本工作流程,熟練掌握神經網路的結構層次及特點,掌握張量、圖結構、OP對象等的使用,熟悉輸入層、卷積層、池化層和全連接層的設計,完成驗證碼識別、圖像識別、手寫輸入識別等常見深度學習項目全程實戰。
Ⅷ 關於python裝飾器的問題
裝飾器函數參數要傳函數,而不是字元串。
裝飾器函數特點:
1,參數為函數對象
2,使用內部函數
3,返回函數對象
在你的代碼中:
裝飾器函數是arg_func(sex)
內部函數是func1()
被裝飾函數是man()和woman()
所以代碼要改成:
defarg_func(sex):
deffunc1():
sex()
if(sex.__name__=='man'):
print("youcan't")
if(sex.__name__=='woman'):
print("youcan")
returnfunc1
@arg_func
defman():
print('goodgoodstudy')
@arg_func
defwoman():
print('goodgoodstudy')
man()
woman()
PS:裝飾器就是為了簡化代碼,增加可讀性,方便團隊開發,在不修改原函數代碼的前提下,通過封裝修改功能,而@修飾就是為了通過原函數名調用時,不直接執行原函數,而是把原函數傳遞到裝飾器函數,通過內部函數(閉包)來調用原函數。這樣好處,就是統一調用方式。
Ⅸ 什麼是Python裝飾器
所謂裝飾器就是把函數包裝一下,為函數添加一些附加功能,裝飾器就是一個函數,參數為被包裝的函數,返回包裝後的函數:你可以試下:
defd(fp):
def_d(*arg,**karg):
print"dosthbeforefp.."
r=fp(*arg,**karg)
print"dosthafterfp.."
returnr
return_d
@d
deff():
print"callf"
#上面使用@d來表示裝飾器和下面是一個意思
#f=d(f)
f()#調用f
Ⅹ 如何理解Python裝飾器
理解Python中的裝飾器
@makebold
@makeitalic
def say():
return "Hello"
列印出如下的輸出:
<b><i>Hello<i></b>
你會怎麼做?最後給出的答案是:
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## 返回 <b><i>hello world</i></b>
現在我們來看看如何從一些最基礎的方式來理解Python的裝飾器。英文討論參考Here。
裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較為經典的有插入日誌、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。
1.1. 需求是怎麼來的?
裝飾器的定義很是抽象,我們來看一個小例子。
def foo():
print 'in foo()'
foo()
這是一個很無聊的函數沒錯。但是突然有一個更無聊的人,我們稱呼他為B君,說我想看看執行這個函數用了多長時間,好吧,那麼我們可以這樣做:
import time
def foo():
start = time.clock()
print 'in foo()'
end = time.clock()
print 'used:', end - start
foo()
很好,功能看起來無懈可擊。可是蛋疼的B君此刻突然不想看這個函數了,他對另一個叫foo2的函數產生了更濃厚的興趣。
怎麼辦呢?如果把以上新增加的代碼復制到foo2里,這就犯了大忌了~復制什麼的難道不是最討厭了么!而且,如果B君繼續看了其他的函數呢?
1.2. 以不變應萬變,是變也
還記得嗎,函數在Python中是一等公民,那麼我們可以考慮重新定義一個函數timeit,將foo的引用傳遞給他,然後在timeit中調用foo並進行計時,這樣,我們就達到了不改動foo定義的目的,而且,不論B君看了多少個函數,我們都不用去修改函數定義了!
import time
def foo():
print 'in foo()'
def timeit(func):
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
timeit(foo)
看起來邏輯上並沒有問題,一切都很美好並且運作正常!……等等,我們似乎修改了調用部分的代碼。原本我們是這樣調用的:foo(),修改以後變成了:timeit(foo)。這樣的話,如果foo在N處都被調用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調用的代碼無法修改這個情況,比如:這個函數是你交給別人使用的。
1.3. 最大限度地少改動!
既然如此,我們就來想想辦法不修改調用的代碼;如果不修改調用代碼,也就意味著調用foo()需要產生調用timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個參數……想辦法把參數統一吧!如果timeit(foo)不是直接產生調用效果,而是返回一個與foo參數列表一致的函數的話……就很好辦了,將timeit(foo)的返回值賦值給foo,然後,調用foo()的代碼完全不用修改!
#-*- coding: UTF-8 -*-
import time
def foo():
print 'in foo()'
# 定義一個計時器,傳入一個,並返回另一個附加了計時功能的方法
def timeit(func):
# 定義一個內嵌的包裝函數,給傳入的函數加上計時功能的包裝
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
# 將包裝後的函數返回
return wrapper
foo = timeit(foo)
foo()
這樣,一個簡易的計時器就做好了!我們只需要在定義foo以後調用foo之前,加上foo = timeit(foo),就可以達到計時的目的,這也就是裝飾器的概念,看起來像是foo被timeit裝飾了。在在這個例子中,函數進入和退出時需要計時,這被稱為一個橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。與傳統編程習慣的從上往下執行方式相比較而言,像是在函數執行的流程中橫向地插入了一段邏輯。在特定的業務領域里,能減少大量重復代碼。面向切面編程還有相當多的術語,這里就不多做介紹,感興趣的話可以去找找相關的資料。
這個例子僅用於演示,並沒有考慮foo帶有參數和有返回值的情況,完善它的重任就交給你了 :)
上面這段代碼看起來似乎已經不能再精簡了,Python於是提供了一個語法糖來降低字元輸入量。
import time
def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
return wrapper
@timeit
def foo():
print 'in foo()'
foo()
重點關注第11行的@timeit,在定義上加上這一行與另外寫foo = timeit(foo)完全等價,千萬不要以為@有另外的魔力。除了字元輸入少了一些,還有一個額外的好處:這樣看上去更有裝飾器的感覺。
-------------------
要理解python的裝飾器,我們首先必須明白在Python中函數也是被視為對象。這一點很重要。先看一個例子:
def shout(word="yes") :
return word.capitalize()+" !"
print shout()
# 輸出 : 'Yes !'
# 作為一個對象,你可以把函數賦給任何其他對象變數
scream = shout
# 注意我們沒有使用圓括弧,因為我們不是在調用函數
# 我們把函數shout賦給scream,也就是說你可以通過scream調用shout
print scream()
# 輸出 : 'Yes !'
# 還有,你可以刪除舊的名字shout,但是你仍然可以通過scream來訪問該函數
del shout
try :
print shout()
except NameError, e :
print e
#輸出 : "name 'shout' is not defined"
print scream()
# 輸出 : 'Yes !'
我們暫且把這個話題放旁邊,我們先看看python另外一個很有意思的屬性:可以在函數中定義函數:
def talk() :
# 你可以在talk中定義另外一個函數
def whisper(word="yes") :
return word.lower()+"...";
# ... 並且立馬使用它
print whisper()
# 你每次調用'talk',定義在talk裡面的whisper同樣也會被調用
talk()
# 輸出 :
# yes...
# 但是"whisper" 不會單獨存在:
try :
print whisper()
except NameError, e :
print e
#輸出 : "name 'whisper' is not defined"*
函數引用
從以上兩個例子我們可以得出,函數既然作為一個對象,因此:
1. 其可以被賦給其他變數
2. 其可以被定義在另外一個函數內
這也就是說,函數可以返回一個函數,看下面的例子:
def getTalk(type="shout") :
# 我們定義另外一個函數
def shout(word="yes") :
return word.capitalize()+" !"
def whisper(word="yes") :
return word.lower()+"...";
# 然後我們返回其中一個
if type == "shout" :
# 我們沒有使用(),因為我們不是在調用該函數
# 我們是在返回該函數
return shout
else :
return whisper
# 然後怎麼使用呢 ?
# 把該函數賦予某個變數
talk = getTalk()
# 這里你可以看到talk其實是一個函數對象:
print talk
#輸出 : <function shout at 0xb7ea817c>
# 該對象由函數返回的其中一個對象:
print talk()
# 或者你可以直接如下調用 :
print getTalk("whisper")()
#輸出 : yes...
還有,既然可以返回一個函數,我們可以把它作為參數傳遞給函數:
def doSomethingBefore(func) :
print "I do something before then I call the function you gave me"
print func()
doSomethingBefore(scream)
#輸出 :
#I do something before then I call the function you gave me
#Yes !
這里你已經足夠能理解裝飾器了,其他它可被視為封裝器。也就是說,它能夠讓你在裝飾前後執行代碼而無須改變函數本身內容。
手工裝飾
那麼如何進行手動裝飾呢?
# 裝飾器是一個函數,而其參數為另外一個函數
def my_shiny_new_decorator(a_function_to_decorate) :
# 在內部定義了另外一個函數:一個封裝器。
# 這個函數將原始函數進行封裝,所以你可以在它之前或者之後執行一些代碼
def the_wrapper_around_the_original_function() :
# 放一些你希望在真正函數執行前的一些代碼
print "Before the function runs"
# 執行原始函數
a_function_to_decorate()
# 放一些你希望在原始函數執行後的一些代碼
print "After the function runs"
#在此刻,"a_function_to_decrorate"還沒有被執行,我們返回了創建的封裝函數
#封裝器包含了函數以及其前後執行的代碼,其已經准備完畢
return the_wrapper_around_the_original_function
# 現在想像下,你創建了一個你永遠也不遠再次接觸的函數
def a_stand_alone_function() :
print "I am a stand alone function, don't you dare modify me"
a_stand_alone_function()
#輸出: I am a stand alone function, don't you dare modify me
# 好了,你可以封裝它實現行為的擴展。可以簡單的把它丟給裝飾器
# 裝飾器將動態地把它和你要的代碼封裝起來,並且返回一個新的可用的函數。
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#輸出 :
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
現在你也許要求當每次調用a_stand_alone_function時,實際調用卻是a_stand_alone_function_decorated。實現也很簡單,可以用my_shiny_new_decorator來給a_stand_alone_function重新賦值。
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#輸出 :
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
# And guess what, that's EXACTLY what decorators do !
裝飾器揭秘
前面的例子,我們可以使用裝飾器的語法:
@my_shiny_new_decorator
def another_stand_alone_function() :
print "Leave me alone"
another_stand_alone_function()
#輸出 :
#Before the function runs
#Leave me alone
#After the function runs
當然你也可以累積裝飾:
def bread(func) :
def wrapper() :
print "</''''''\>"
func()
print "<\______/>"
return wrapper
def ingredients(func) :
def wrapper() :
print "#tomatoes#"
func()
print "~salad~"
return wrapper
def sandwich(food="--ham--") :
print food
sandwich()
#輸出 : --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs :
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
使用python裝飾器語法:
@bread
@ingredients
def sandwich(food="--ham--") :
print food
sandwich()
#輸出 :
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>