1. 程式人生 > >python~裝飾器詳解

python~裝飾器詳解

裝飾器的概念

在程式碼執行期間在不改變原函式定義的基礎上,動態給該函式增加功能的方式, 稱之為裝飾器。

裝飾器是一個很著名的設計模式,較為經典的有插入日誌、效能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函式中與函式功能本身無關的雷同程式碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的物件新增額外的功能。

總體來說,裝飾器其實也是一個函式,一個用來包裝函式的函式,返回一個修改之後的函式物件,將其重新賦值給原來的識別符號,並永久喪失對原始函式物件的訪問。

裝飾器的本質:一個閉包函式

裝飾器的功能:在不修改原函式及其呼叫方式的情況下對原函式功能進行擴充套件

裝飾器應用示例:使用@將裝飾器應用到函式

import time

def now():
    print ("current time is %s" % time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))

def log(func):
    def wrapper(*args,**kw):
        print ("call %s" % func.__name__)
        return func(*args,**kw)
    return wrapper

#now()
#print (type(log(now)))
log(now)()
========================================================================================

#使用@將裝飾器應用到函式
import time

def log(func):
    “定義裝飾器”
    def wrapper(*args,**kw):
        print ("call %s" % func.__name__)
        return func(*args,**kw)
    return wrapper

@log
def now():
    print ("current time is %s" % time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))

now()   #等同於不加@時的log(now)()呼叫

#使用閉包和使用裝飾器後的執行結果是一樣的

裝飾器分類

裝飾器分為無引數decorator和有引數decorator

無引數decorator:生成一個新的裝飾器函式

有引數decorator:裝飾函式先處理引數,再生成一個新的裝飾器函式,然後對函式進行裝飾

裝飾器的具體定義:

1、把要裝飾的方法作為輸入引數;

2、在函式體內可以進行任意的操作(可以想象其中會有很多應用場景);

3、只要確保最後返回一個可執行的函式即可(可以是原來的輸入引數函式,也可以是一個新函式)

裝飾器學習九步法—第一步

最簡單的函式,準備附加額外功能

程式碼示例:

def myfunc():
    print ("myfunc() called!")

myfunc()
myfunc()

裝飾器學習九步法—第二步

使用裝飾函式在函式執行前和執行後分別附加額外功能

程式碼示例:

#裝飾函式的引數是被裝飾的函式物件,返回原函式物件
#裝飾的實質語句:myfunc=deco(myfunc)

def deco(func):
    print ("before myfunc() called!")
    func()
    print ("after myfunc() called!")
    return func

def myfunc():
    print ("myfunc() called!")

deco(myfunc)
print ("*"*10)
myfunc=deco(myfunc)   
print ("*"*10)
myfunc()
myfunc()

裝飾器學習九步法—第三步

使用語法糖@來裝飾函式,相當於“myfunc = deco(myfunc)” 但發現新函式只在第一次被呼叫,且原函式多呼叫了一次

程式碼示例:

def deco(func):
    print ("before myfunc() called!")
    func()
    print ("after myfunc() called!")
    return func

@deco
def myfunc():
    print ("myfunc() called!")

myfunc()
print ("*"*10)
myfunc()

裝飾器學習九步法—第四步

使用內嵌包裝函式來確保每次新函式都被呼叫

程式碼示例:

def deco(func):
    def _deco():
        print ("before myfunc() called!")
        func()
        print ("after myfunc() called!")
    return _deco


@deco               #等價於不帶@時,myfunc=deco(myfunc)
def myfunc():
    print ("myfunc() called!")

print (myfunc.__closure__)
myfunc()
print ("*"*10)
myfunc()

裝飾器學習九步法—第五步

對帶引數的函式進行裝飾

def deco(func):
    def _deco(a,b):
        print ("before myfunc() called!")
        func(a,b)
        print ("after myfunc() called!")
    return _deco

@deco               #等價於不帶@,myfunc=deco(myfunc)
def myfunc(a,b):
    print ("myfunc() called!",a*b)

#print (myfunc.__closure__)
myfunc(1,2)
print ("*"*10)
myfunc(2,3)

裝飾器學習九步法—第六步

對引數數量不確定的函式進行裝飾,引數用(*args, **kwargs),自動適應變參和命名引數

def deco(func):
    def _deco(*args,**kwargs):
        print ("before myfunc() called!")
        func(*args,**kwargs)
        print ("after myfunc() called!")
    return _deco

@deco               #等價於不帶@,myfunc=deco(myfunc)
def myfunc(*args,**kwargs):
    print ("myfunc() called!",(*args))
    for key,value in kwargs.items():
        print (key,value)

#print (myfunc.__closure__)
myfunc(1,2,word1=1)
print ("*"*10)
myfunc(2,3,word2=2)

裝飾器學習九步法—第七步

在示例4的基礎上,讓裝飾器帶引數, 和上一示例相比在外層多了一層包裝。 裝飾函式名實際上應更有意義些。

程式碼示例:

def deco(arg):
    def _deco(func):
        def _deco():
            print ("before %s called [%s]" % (func.__name__,arg))
            func()
            print ("after %s called [%s]" % (func.__name__,arg))
        return _deco
    return _deco

@deco("23")    #語法糖的作用等價於myfunc=deco("23")(myfunc)
def myfunc():
    print ("myfunc() called!")

#myfunc=deco("23")(myfunc)
myfunc()
========================================================================
def deco(arg):
    def _deco(func):
        def _deco():
            print ("before %s called [%s]" % (func.__name__,arg))
            func()
            print ("after %s called [%s]" % (func.__name__,arg))
        return _deco
    return _deco

@deco("mymodule")    #語法糖的作用等價於myfunc=deco("mymodule")(myfunc)
def myfunc():
    print ("myfunc() called!")

@deco("yourmodule")    #語法糖的作用等價於myfunc2=deco("yourmodule")(myfunc)
def myfunc2():
    print ("myfunc() called!")

myfunc()
print ("*"*20)
myfunc2()

裝飾器學習九步法—第八步

讓裝飾器帶類引數

程式碼示例:

#encoding:utf-8

class Locker(object):
    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):
    def _deco(func):
        def _deco():
            print ("before %s called [%s]" % (func.__name__,cls))
            cls.acquire()
            try:
                func()
            finally:
                cls.release()
        return _deco
    return _deco

@deco(Locker)  
def myfunc():
    print ("myfunc called!")

myfunc()
#b=deco(Locker)(myfunc)   --->相當於@deco(Locker)
#b()                      --->函式物件呼叫

裝飾器學習九步法—第九步

裝飾器帶類引數,並分拆公共類到其他py檔案中,同時演示了對一個函式應用多個裝飾器

程式碼示例:

#encoding:utf-8

“模組mylocker中的程式碼塊”
class MyLocker(object):

    def __init__(self):
        print ("mylocker.__init__() called!")

    @staticmethod
    def acquire():
        print ("mylocker.acquire() called!")

    @staticmethod
    def unlock():
        print ("mylocker.unlock() called!")

class LockErex(MyLocker):

    @staticmethod
    def acquire():
        print ("LockErex.acquire() called!")

    @staticmethod
    def unlock():
        print ("LockErex.unlock() called!")


#裝飾函式
def lockhelper(cls):
    def _deco(func):
        def _deco(*args,**kw):
            print ("before %s called." % func.__name__)
            cls.acquire()
            try:
                return func(*args,**kw)
            finally:
                cls.unlock()            
        return _deco
    return _deco


#模組zhigang中的程式碼塊
from mylocker import *

class Example(object):

    @lockhelper(MyLocker)
    def myfunc(self):
        print ("myfunc() called.")

    @lockhelper(MyLocker)     # --》 lockhelper(MyLocker)(_deco)
    @lockhelper(LockErex)     # --》 lockhelper(LockErex)(myfunc2) --》返回_deco函式物件 _deco 即myfunc2
    def myfunc2(self,a,b):
        print ("myfunc2() called.")
        return a+b

if __name__=="__main__":
    ex=Example()
    #ex.myfunc()
    #print ("*"*20)
    print (ex.myfunc2(1,2))
    #執行過程myfunc2(1,2)=lockhelper(MyLocker)(lockhelper(LockErex)(myfunc(1,2)))

內建裝飾器

Python中內建的裝飾器有三個:

staticmethod:定義例項方法為靜態方法

classmethod:定義例項方法為類方法

property:對類屬性的操作