Python 装饰器学习以及实际使用场景实践
前言
前幾天在看Flask框架,對于非常神奇的@語法,不是非常的理解,回來補裝飾器的功課。閱讀很多的關于裝飾器的文章,自己整理一下,適合自己的思路的方法和例子,與大家分享。
app = Flask(__name__)@app.route("/") def hello():return "Hello World!"1、裝飾器是什么
裝飾器是Python語言中的高級語法。主要的功能是對一個函數、方法、或者類進行加工,作用是為已經存在的對象添加額外的功能,提升代碼的可讀性。
裝飾器是設計模式的一種,被用于有切面需求的場景,較為經典的有插入日志、性能測試、事務處理等
2、裝飾器的語法
裝飾器的語法如下:
當前Python的裝飾器語法如下: @dec2 @dec1 def func(arg1, arg2, ...):....return funx上面的代碼相當于:def func(arg1, arg2, ...):pass func = dec2(dec1(func))裝飾器可以用def的形式來定義。裝飾器接收一個可調用對象作為輸入參數,并返回一個新的可調用對象。
裝飾器新建了一個可調用對象,也就是return 返回的函數funx,在新增的函數中,可以添加我們需要的功能,并通過調用原有函數來實現原有函數的功能。
3、裝飾器的使用
3.1不帶參數的裝飾器
定義裝飾器非常的簡單:
def deco(func):"""無參數調用decorator聲明時必須有一個參數,這個參數將接收要裝飾的方法"""print "before myfunc() called."func()print "after myfunc() called."return func@deco def myfunc():print " myfunc() called."myfunc() myfunc()定義好裝飾器后,就可以通過@語法來使用了,在函數的定義前調用@+裝飾器函數名,即可使用。上面這個裝飾器在使用的時候有一個問題,即只在第一次被調用,并且原來的函數多執行一次。執行輸出如下:
before myfunc() called. myfunc() called.after myfunc() called.myfunc() called. --函數多執行一次的輸出myfunc() called. --第二次調用,裝飾器不生效要保證新函數每次被調用,使用下面的方法來定義裝飾器
def deco(func):"""無參數調用decorator聲明時必須有一個參數,這個參數將接收要裝飾的方法"""def _deco():print "before myfunc() called."func()print "after myfunc() called."#return func 不需要返回funcretrun _deco @deco def myfunc():print " myfunc() called."return 'OK'myfunc() myfunc()函數輸出如下:
before myfunc() called.myfunc() called.after myfunc() called. before myfunc() called.myfunc() called.after myfunc() called.這樣可以看到,裝飾器每次都得到了調用。
3.2帶參數的函數進行裝飾器
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 + bmyfunc(1, 2) myfunc(3, 4)輸出:
before myfunc() called.
myfunc() called.
After myfunc() called. result: 3
內嵌函數的形參和返回值與原函數相同,裝飾函數返回內嵌包裝函數。
3.3裝飾器帶參數
def decoWithArgs(arg): """由于有參數的decorator函數在調用時只會使用應用時的參數而不接收被裝飾的函數做為參數,所以必須返回一個decorator函數, 由它對被裝飾的函數進行封裝處理""" def newDeco(func): #定義一個新的decorator函數def replaceFunc(): #在decorator函數里面再定義一個內嵌函數,由它封裝具體的操作print "Enter decorator %s" %arg #進行額外操作return func() #對被裝飾函數進行調用return replaceFunc return newDeco #返回一個新的decorator函數@decoWithArgs("demo") def MyFunc(): #應用@decoWithArgs修飾的方法print "Enter MyFunc"MyFunc() #調用被裝飾的函數輸出:
nter decorator demo
Enter MyFunc
這個情形適用于原來的函數沒有參數,新增加打印的情況。常見適用的地方是增加函數的打印日志。
3.4對參數數量不確定的函數進行裝飾
下面的例子是一個郵件異步發送的例子,函數的參數數據部確定,裝飾器實現了對于郵件發送函數的異步發送。
from threading import Threaddef 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_bodymsg.html = html_bodysend_async_email(msg)并且這個裝飾器可以適用一切需要異步處理的功能,做到非常好的代碼復用。
3.5讓裝飾器帶類參數
class locker:def __init__(self):print("locker.__init__() should be not called.")@staticmethoddef acquire():print("locker.acquire() called.(這是靜態方法)")@staticmethoddef release():print(" locker.release() called.(不需要對象實例)")def deco(cls):'''cls 必須實現acquire和release靜態方法'''def _deco(func):def __deco():print("before %s called [%s]." % (func.__name__, cls))cls.acquire()try:return func()finally:cls.release()return __decoreturn _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 )裝飾器總結
當我們對某個方法應用了裝飾方法后, 其實就改變了被裝飾函數名稱所引用的函數代碼塊入口點,使其重新指向了由裝飾方法所返回的函數入口點。由此我們可以用decorator改變某個原有函數的功能,添加各種操作,或者完全改變原有實現。
參考文章
感謝以下幾位大神:
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
轉載于:https://www.cnblogs.com/StitchSun/p/4600835.html
總結
以上是生活随笔為你收集整理的Python 装饰器学习以及实际使用场景实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: luogu2034
- 下一篇: 深度优先遍历解决连通域求解问题-pyth