Python 裝飾器學習以及實際使用場景實踐

jopen 9年前發布 | 22K 次閱讀 Python Python開發

,不是非常的理解,回來補裝飾器的功課。閱讀很多的關于裝飾器的文章,自己整理一下,適合自己的思路的方法和例子,與大家分享。

app = Flask(name)

@app.route("/") def hello(): return "Hello World!"</pre>

1、裝飾器是什么

裝飾器是Python語言中的高級語法。主要的功能是對一個函數、方法、或者類進行加工,作用是為已經存在的對象添加額外的功能,提升代碼的可讀性。裝飾器是設計模式的一種,被用于有切面需求的場景,較為經典的有插入日志、性能測試、事務處理等

2、裝飾器的語法

裝飾器的語法如下:

當前Python的裝飾器語法如下:
@dec2
@dec1
def func(arg1, arg2, ...):
    ....
    return funx

上面的代碼相當于:

def func(arg1, arg2, ...): pass func = dec2(dec1(func))</pre>

裝飾器可以用def的形式來定義。 裝飾器接收一個可調用對象作為輸入參數,并返回一個新的可調用對象。

裝飾器新建了一個可調用對象,也就是return 返回的函數funx,在新增的函數中,可以添加我們需要的功能,并通過調用原有函數來實現原有函數的功能。

</div>

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()</pre>

定義好裝飾器后,

,即可使用。上面這個裝飾器在使用的時候有一個問題,即只在第一次被調用,并且原來的函數多執行一次。執行輸出如下:

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 不需要返回func
retrun _deco

@deco def myfunc(): print " myfunc() called." return 'OK'

myfunc() myfunc()</pre>

函數輸出如下:

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 + b
myfunc(1, 2)
myfunc(3, 4)

輸出:

before myfunc() called.

myfunc() called.

After myfunc() called. result: 3

</div>

before myfunc() called.
myfunc() called.
After myfunc() called. result: 7

內嵌函數的形參和返回值與原函數相同,裝飾函數返回內嵌包裝函數。

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

</div>

這個情形適用于原來的函數沒有參數,新增加打印的情況。 常見適用的地方是增加函數的打印日志。

3.4對參數數量不確定的函數進行裝飾

下面的例子是一個郵件異步發送的例子,函數的參數數據部確定,裝飾器實現了對于郵件發送函數的異步發送。

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)

并且這個裝飾器可以適用一切需要異步處理的功能,做到非常好的代碼復用。

3.5讓裝飾器帶類參數

class locker:
    def __init__(self):
        print("locker.__init__() should be not called.")
    @staticmethod
    def acquire():
        print("locker.acquire() called.(這是靜態方法)")
    @staticmethod
    def 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 __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 )</pre>

裝飾器總結

當我們對某個方法應用了裝飾方法后, 其實就改變了被裝飾函數名稱所引用的函數代碼塊入口點,使其重新指向了由裝飾方法所返回的函數入口點。由此我們可以用decorator改變某個原有函數的功能,添加各種操作,或者完全改變原有實現。

</div>

參考文章

感謝以下幾位大神:

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

原文 http://www.cnblogs.com/StitchSun/p/4600835.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!