Python 裝飾器學(xué)習(xí)以及實際使用場景實踐
前言
前幾天在看Flask框架,,不是非常的理解,回來補裝飾器的功課。閱讀很多的關(guān)于裝飾器的文章,自己整理一下,適合自己的思路的方法和例子,與大家分享。
- app = Flask(__name__)
- @app.route("/")
- def hello():
- return "Hello World!"
1、裝飾器是什么
裝飾器是Python語言中的高級語法。主要的功能是對一個函數(shù)、方法、或者類進(jìn)行加工,作用是為已經(jīng)存在的對象添加額外的功能,提升代碼的可讀性。
裝飾器是設(shè)計模式的一種,被用于有切面需求的場景,較為經(jīng)典的有插入日志、性能測試、事務(wù)處理等
2、裝飾器的語法
裝飾器的語法如下:
當(dāng)前Python的裝飾器語法如下:
- @dec2
- @dec1
- def func(arg1, arg2, ...):
- ....
- return funx
- 上面的代碼相當(dāng)于:
- def func(arg1, arg2, ...):
- pass
- func = dec2(dec1(func))
裝飾器可以用def的形式來定義。裝飾器接收一個可調(diào)用對象作為輸入?yún)?shù),并返回一個新的可調(diào)用對象。
裝飾器新建了一個可調(diào)用對象,也就是return 返回的函數(shù)funx,在新增的函數(shù)中,可以添加我們需要的功能,并通過調(diào)用原有函數(shù)來實現(xiàn)原有函數(shù)的功能。
3、裝飾器的使用
3.1不帶參數(shù)的裝飾器
定義裝飾器非常的簡單:
- def deco(func):
- """無參數(shù)調(diào)用decorator聲明時必須有一個參數(shù),這個參數(shù)將接收要裝飾的方法"""
- print "before myfunc() called."
- func()
- print "after myfunc() called."
- return func
- @deco
- def myfunc():
- print " myfunc() called."
- myfunc()
- myfunc()
定義好裝飾器后,,,即可使用。上面這個裝飾器在使用的時候有一個問題,即只在***次被調(diào)用,并且原來的函數(shù)多執(zhí)行一次。執(zhí)行輸出如下:
- before myfunc() called.
- myfunc() called.
- after myfunc() called.
- myfunc() called. --函數(shù)多執(zhí)行一次的輸出
- myfunc() called. --第二次調(diào)用,裝飾器不生效
要保證新函數(shù)每次被調(diào)用,使用下面的方法來定義裝飾器
- def deco(func):
- """無參數(shù)調(diào)用decorator聲明時必須有一個參數(shù),這個參數(shù)將接收要裝飾的方法"""
- def _deco():
- print "before myfunc() called."
- func()
- print "after myfunc() called."
- #return func 不需要返回func
- retrun _deco
- @deco
- def myfunc():
- print " myfunc() called."
- return 'OK'
- myfunc()
- myfunc()
函數(shù)輸出如下:
- before myfunc() called.
- myfunc() called.
- after myfunc() called.
- before myfunc() called.
- myfunc() called.
- after myfunc() called.
這樣可以看到,裝飾器每次都得到了調(diào)用。
3.2帶參數(shù)的函數(shù)進(jìn)行裝飾器
- def deco(func):
- def _deco(a, b):
- print("before myfunc() called.")
- ret = func(a, b)
- print(" after myfunc() called. result: %s" % ret)
- return ret
- return _deco
- @deco
- def myfunc(a, b):
- print(" myfunc(%s,%s) called." % (a, b))
- return a + b
- myfunc(1, 2)
- myfunc(3, 4)
輸出:
- before myfunc() called.
- myfunc() called.
- After myfunc() called. result: 3
- before myfunc() called. myfunc() called. After myfunc() called. result: 7
內(nèi)嵌函數(shù)的形參和返回值與原函數(shù)相同,裝飾函數(shù)返回內(nèi)嵌包裝函數(shù)。
3.3裝飾器帶參數(shù)
- def decoWithArgs(arg):
- """由于有參數(shù)的decorator函數(shù)在調(diào)用時只會使用應(yīng)用時的參數(shù)而不接收被裝飾的函數(shù)做為參數(shù),
- 所以必須返回一個decorator函數(shù), 由它對被裝飾的函數(shù)進(jìn)行封裝處理"""
- def newDeco(func): #定義一個新的decorator函數(shù)
- def replaceFunc(): #在decorator函數(shù)里面再定義一個內(nèi)嵌函數(shù),由它封裝具體的操作
- print "Enter decorator %s" %arg #進(jìn)行額外操作
- return func() #對被裝飾函數(shù)進(jìn)行調(diào)用
- return replaceFunc
- return newDeco #返回一個新的decorator函數(shù)
- @decoWithArgs("demo")
- def MyFunc(): #應(yīng)用@decoWithArgs修飾的方法
- print "Enter MyFunc"
- MyFunc() #調(diào)用被裝飾的函數(shù)
輸出:
nter decorator demo
Enter MyFunc
這個情形適用于原來的函數(shù)沒有參數(shù),新增加打印的情況。常見適用的地方是增加函數(shù)的打印日志。
3.4對參數(shù)數(shù)量不確定的函數(shù)進(jìn)行裝飾
下面的例子是一個郵件異步發(fā)送的例子,函數(shù)的參數(shù)數(shù)據(jù)部確定,裝飾器實現(xiàn)了對于郵件發(fā)送函數(shù)的異步發(fā)送。
- from threading import Thread
- def async(f):
- def wrapper(*args, **kwargs):
- thr = Thread(target = f, args = args, kwargs = kwargs)
- thr.start()
- return wrapper
- @async
- def send_async_email(msg):
- mail.send(msg)
- def send_email(subject, sender, recipients, text_body, html_body):
- msg = Message(subject, sender = sender, recipients = recipients)
- msg.body = text_body
- msg.html = html_body
- send_async_email(msg)
并且這個裝飾器可以適用一切需要異步處理的功能,做到非常好的代碼復(fù)用。
3.5讓裝飾器帶類參數(shù)
- class locker:
- def __init__(self):
- print("locker.__init__() should be not called.")
- @staticmethod
- def acquire():
- print("locker.acquire() called.(這是靜態(tài)方法)")
- @staticmethod
- def release():
- print(" locker.release() called.(不需要對象實例)")
- def deco(cls):
- '''cls 必須實現(xiàn)acquire和release靜態(tài)方法'''
- def _deco(func):
- def __deco():
- print("before %s called [%s]." % (func.__name__, cls))
- cls.acquire()
- try:
- return func()
- finally:
- cls.release()
- return __deco
- return _deco
- @deco(locker)
- def myfunc():
- print(" myfunc() called.")
- myfunc()
- myfunc()
輸出為:
- before myfunc called [__main__.locker].
- locker.acquire() called.(this is staticmethon)
- myfunc() called.
- locker.release() called.(do't need object )
- before myfunc called [__main__.locker].
- locker.acquire() called.(this is staticmethon)
- myfunc() called.
- locker.release() called.(do't need object )
裝飾器總結(jié)
當(dāng)我們對某個方法應(yīng)用了裝飾方法后, 其實就改變了被裝飾函數(shù)名稱所引用的函數(shù)代碼塊入口點,使其重新指向了由裝飾方法所返回的函數(shù)入口點。由此我們可以用decorator改變某個原有函數(shù)的功能,添加各種操作,或者完全改變原有實現(xiàn)。
參考文章
感謝以下幾位大神:
http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html
http://www.cnblogs.com/Jifangliang/archive/2008/07/22/1248313.html
http://www.cnblogs.com/vamei/archive/2013/02/16/2820212.html