您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)python中裝飾器怎么使用的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。
經(jīng)常用于有切面需求的場景,比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場景。裝飾器是解決這類問題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。
概括的講,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。
def foo(): print('i am foo')
現(xiàn)在有一個(gè)新的需求,希望可以記錄下函數(shù)的執(zhí)行日志,于是在代碼中添加日志代碼:
def foo(): print('i am foo') print("foo is running")
假設(shè)現(xiàn)在有100個(gè)函數(shù)需要增加這個(gè)需求,并且后續(xù)可能還要對(duì)這一百個(gè)函數(shù)都增加執(zhí)行前打印日志的需求,怎么辦?還一個(gè)個(gè)改嗎?
當(dāng)然不了,這樣會(huì)造成大量雷同的代碼,為了減少重復(fù)寫代碼,我們可以這樣做,重新定義一個(gè)函數(shù):專門處理日志 ,日志處理完之后再執(zhí)行真正的業(yè)務(wù)代碼。
def use_logging(func): print("%s is running" % func.__name__) func() def bar(): print('i am bar') use_logging(bar) 運(yùn)行結(jié)果: #bar is running #i am bar
函數(shù)use_logging就是裝飾器,它把執(zhí)行真正業(yè)務(wù)方法的func包裹在函數(shù)里面,看起來像bar被use_logging裝飾了。在這個(gè)例子中,函數(shù)進(jìn)入和退出時(shí) ,被稱為一個(gè)橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。
通過以上use_logging函數(shù)我們?cè)黾恿巳罩竟δ?,不管以后有多少函?shù)需要增加日志或者修改日志的格式我們只需要修改use_logging函數(shù),并執(zhí)行use_logging(被裝飾的函數(shù))就達(dá)到了我們想要的效果。
def use_logging(func): print("%s is running" % func.__name__) return func @use_logging def bar(): print('i am bar') bar()
python提供了@符號(hào)作為裝飾器的語法糖,使我們更方便的應(yīng)用裝飾函數(shù);但使用語法糖要求裝飾函數(shù)必須return一個(gè)函數(shù)對(duì)象。因此我們將上面的func函數(shù)使用內(nèi)嵌函數(shù)包裹并return。
裝飾器相當(dāng)于執(zhí)行了裝飾函數(shù)use_loggin后又返回被裝飾函數(shù)bar,因此bar()被調(diào)用的時(shí)候相當(dāng)于執(zhí)行了兩個(gè)函數(shù)。等價(jià)于use_logging(bar)()
def use_logging(func): def _deco(): print("%s is running" % func.__name__) func() return _deco @use_logging def bar(): print('i am bar') bar()
現(xiàn)在我們的參數(shù)需要傳入兩個(gè)參數(shù)并計(jì)算值,因此我們需要對(duì)內(nèi)層函數(shù)進(jìn)行改動(dòng)傳入我們的兩個(gè)參數(shù)a和b,等價(jià)于use_logging(bar)(1,2)
def use_logging(func): def _deco(a,b): print("%s is running" % func.__name__) func(a,b) return _deco @use_logging def bar(a,b): print('i am bar:%s'%(a+b)) bar(1,2)
我們裝飾的函數(shù)可能參數(shù)的個(gè)數(shù)和類型都不一樣,每一次我們都需要對(duì)裝飾器做修改嗎?這樣做當(dāng)然是不科學(xué)的,因此我們使用python的變長參數(shù)*args和**kwargs來解決我們的參數(shù)問題。
不帶參數(shù)裝飾器版本,這個(gè)格式適用于不帶參數(shù)的裝飾器。
經(jīng)過以下修改,我們已經(jīng)適應(yīng)了各種長度和類型的參數(shù)。這個(gè)版本的裝飾器可以裝飾任意類型的無參數(shù)函數(shù)。
def use_logging(func): def _deco(*args,**kwargs): print("%s is running" % func.__name__) func(*args,**kwargs) return _deco @use_logging def bar(a,b): print('i am bar:%s'%(a+b)) @use_logging def foo(a,b,c): print('i am bar:%s'%(a+b+c)) bar(1,2) foo(1,2,3)
帶參數(shù)的裝飾器,這個(gè)格式適用于帶參數(shù)的裝飾器。
某些情況我們需要讓裝飾器帶上參數(shù),那就需要編寫一個(gè)返回一個(gè)裝飾器的高階函數(shù),寫出來會(huì)更復(fù)雜。比如:
#! /usr/bin/env python # -*- coding:utf-8 -*- # __author__ = "TKQ" def use_logging(level): def _deco(func): def __deco(*args, **kwargs): if level == "warn": print "%s is running" % func.__name__ return func(*args, **kwargs) return __deco return _deco @use_logging(level="warn") def bar(a,b): print('i am bar:%s'%(a+b)) bar(1,3) # 等價(jià)于use_logging(level="warn")(bar)(1,3)
使用裝飾器極大地復(fù)用了代碼,但是他有一個(gè)缺點(diǎn)就是原函數(shù)的元信息不見了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看例子:
def use_logging(func): def _deco(*args,**kwargs): print("%s is running" % func.__name__) func(*args,**kwargs) return _deco @use_logging def bar(): print('i am bar') print(bar.__name__) bar() #bar is running #i am bar #_deco #函數(shù)名變?yōu)開deco而不是bar,這個(gè)情況在使用反射的特性的時(shí)候就會(huì)造成問題。因此引入了functools.wraps解決這個(gè)問題。
使用functools.wraps:
import functools def use_logging(func): @functools.wraps(func) def _deco(*args,**kwargs): print("%s is running" % func.__name__) func(*args,**kwargs) return _deco @use_logging def bar(): print('i am bar') print(bar.__name__) bar() #result: #bar is running #i am bar #bar ,這個(gè)結(jié)果是我們想要的。OK啦!
import functools def use_logging(arg): if callable(arg):#判斷參入的參數(shù)是否是函數(shù),不帶參數(shù)的裝飾器調(diào)用這個(gè)分支 @functools.wraps(arg) def _deco(*args,**kwargs): print("%s is running" % arg.__name__) arg(*args,**kwargs) return _deco else:#帶參數(shù)的裝飾器調(diào)用這個(gè)分支 def _deco(func): @functools.wraps(func) def __deco(*args, **kwargs): if arg == "warn": print "warn%s is running" % func.__name__ return func(*args, **kwargs) return __deco return _deco @use_logging("warn") # @use_logging def bar(): print('i am bar') print(bar.__name__) bar()
使用類裝飾器可以實(shí)現(xiàn)帶參數(shù)裝飾器的效果,但實(shí)現(xiàn)的更加優(yōu)雅簡潔,而且可以通過繼承來靈活的擴(kuò)展.
class loging(object): def __init__(self,level="warn"): self.level = level def __call__(self,func): @functools.wraps(func) def _deco(*args, **kwargs): if self.level == "warn": self.notify(func) return func(*args, **kwargs) return _deco def notify(self,func): # logit只打日志,不做別的 print "%s is running" % func.__name__ @loging(level="warn")#執(zhí)行__call__方法 def bar(a,b): print('i am bar:%s'%(a+b)) bar(1,3)
class email_loging(Loging): ''' 一個(gè)loging的實(shí)現(xiàn)版本,可以在函數(shù)調(diào)用時(shí)發(fā)送email給管理員 ''' def __init__(self, email='admin@myproject.com', *args, **kwargs): self.email = email super(email_loging, self).__init__(*args, **kwargs) def notify(self,func): # 發(fā)送一封email到self.email print "%s is running" % func.__name__ print "sending email to %s" %self.email @email_loging(level="warn") def bar(a,b): print('i am bar:%s'%(a+b)) bar(1,3)
1、簡單易用,與C/C++、Java、C# 等傳統(tǒng)語言相比,Python對(duì)代碼格式的要求沒有那么嚴(yán)格;2、Python屬于開源的,所有人都可以看到源代碼,并且可以被移植在許多平臺(tái)上使用;3、Python面向?qū)ο螅軌蛑С置嫦蜻^程編程,也支持面向?qū)ο缶幊蹋?、Python是一種解釋性語言,Python寫的程序不需要編譯成二進(jìn)制代碼,可以直接從源代碼運(yùn)行程序;5、Python功能強(qiáng)大,擁有的模塊眾多,基本能夠?qū)崿F(xiàn)所有的常見功能。
感謝各位的閱讀!關(guān)于“python中裝飾器怎么使用”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。