Python第二十六天 python裝飾器

Python第二十六天 python裝飾器html

 

 

裝飾器
Python 2.4 開始提供了裝飾器( decorator ),裝飾器做爲修改函數的一種便捷方式,爲工程師編寫程序提供了便利性和靈活性
裝飾器本質上就是一個函數,這個函數接受其餘函數做爲參數,並將其以一個新的修改後的函數進行替換。python

 

 

裝飾器的做用
一、注入參數。爲函數提供默認參數,生成新的參數等
二、記錄函數的行爲。能夠統計函數的調用次數,緩存函數的結果,計算函數調用耗費的時間
三、預處理與後處理
四、修改調用時的上下文編程

 

函數能夠賦值給另一個變量名
函數能夠嵌套
函數對象能夠做爲另一個函數的參數
裝飾器僅僅是利用前面的這些Python 知識加上Python 語法實現的一種高級語法緩存

 

 

函數對象
在Python 語言中, def 語句定義了一個函數對象,並將其賦值給函數名。
也就是說,函數名其實只是一個變量,這個變量引用了這個函數對象。
所以,咱們能夠將函數賦值給另一個變量名,經過這個新的變量名調用函數。app

def say_hi():
    print ("HI")
hello = say_hi
hello()

 

 

 

嵌套函數
在Python 語言中, def 語句是一個實時執行的語句,當它運行的時候會建立一個新的函數對象,並將其賦值給一個變量名
這裏所說的變量名就是函數的名稱。由於def 是一個語句,所以,函數定義能夠出如今其餘語句之中。編程語言

def outer(x , y) :
    def inner () :
        return x + y
    return inner
f = outer(l , 2)
print(f())

在這個例子中,咱們定義了一個名爲outer 的函數, 並在outer 函數內部定義了inner 函數
outer 函數以返回值的形式返回inner 函數,咱們將返回值保存在變量f 中,f 引用的是outer 函數內部的inner 函數
因此,當咱們調用函數f 時,實際是調用的inner 函數函數

 

 

 

回調函數
回調函數是指將函數做爲參數傳遞給另一個函數,並在另一個函數中進行調用。
回調函數並非Python 語言特有的,在各個編程語言中都存在。ui

def greeting(f):
    f()
    
def say_hi():
    print ("HI")
    
def say_hello():
     print("HELLO")
     
greeting(say_hi)
greeting(say_hello)

 

 

裝飾器
在Python 的裝飾器語法中,內層函數的參數是被裝飾的函數參數,外層函數的參數是被裝飾的函數。spa

def say_hi():
    print("hi")
    
def bread(f):
    def wrapper(*args, **kwargs ):
        print("begin call {0}".format(f.__name__))
        f()
        print("finish call {0}".format(f.__name__))
    return wrapper
    
say_hi_copy = bread(say_hi)
say_hi_copy()

上面這段代碼的執行結果以下:
begin call say_hi
hi
finish call say_hipwa

 

改造爲裝飾器

@bread
def say_hi(username='someone'):   #被裝飾函數,被裝飾函數的參數必定要用關鍵字參數,不能用位置參數,不然不能裝入字典!!!!!!!
    print("hi")
    print(username)
    
def bread(f):    #裝飾器函數
    def wrapper(*args, **kwargs ):
        print("begin")
        f()
        print("finish")
        print(kwargs.get("username"))  #讀取被裝飾函數的參數,被裝飾函數的參數必定要用關鍵字參數,不能用位置參數,不然不能裝入字典
    return wrapper
    
say_hi()

這段程序和前面的程序做用如出一轍,產生的結果也相同。區別在於,前面的程序顯示地調用了bread 函數來封裝say_hi 函數
這段程序經過Python 的語法糖來封裝say_hi函數。在Python 中, say_hi 函數定義語句前一行的"@bread"語句表示對該函數應用bread裝飾器
其中,"@"是裝飾器的語法,"bread"是裝飾器的名稱

 

 

 

 

獲取正確函數屬性
對於一個函數,咱們能夠經過name 屬性獲得函數的名字,經過doc 屬性獲得函數的幫助信息。
可是, 一個被裝飾器裝飾過的函數,默認狀況下,咱們經過doc 和name 獲取屬性時,獲得的倒是裝飾器中嵌套函數的信息

標準庫的functools 模塊中的wraps 裝飾器。wraps 裝飾器的做用是,複製函數屬性至被裝飾的函數
使用functools.wraps 裝飾器裝飾wrapper 函數。經過這樣簡單的修改就能夠獲取到add 函數的正確屬性

from __future__ import print_function
import time
import functools

def benchmark(func):
    @functools.wraps(func)
    def wrapper(*args , **kwargs):
       t = time.time()
       res = func(*args , ** kwargs)
       print(func.__name__, time.time() - t)
       return res
    return wrapper

@benchmark
def add (a, b):
'''Calculate the sum of two numbers'''
    return a + b

print(add.__name__)
print(add.__doc__)

 

 


inspect 模塊
inspect 模塊提供了許多有用的函數來獲取活躍對象的信息。其中, getcallargs 函數用來獲取函數的參數信息
getcallargs 會返回一個字典,該字典保存了函數的全部參數,包括關鍵字參數和位置參數。
也就是說, getcallargs 可以根據函數的定義和傳遞給函數的參數,推測出哪個值傳遞給函數的哪個參數。
getcallargs 推測出這些信息之後,以一個字典的形式返回給咱們全部的參數和取值。

import functools
import inspect
def check_is_admin(func):
    @functools.wraps(func)
    def wrapper(* args, * *kwargs):
        func_args = inspect.getcallargs(func , *args , ** kwargs)
        if func_args.get('username')! = 'admin' :
            raise Exception("This user is not allowed to put/get elem")
        return f( * args, * *kwargs )
    return wrapper

 

 

 

給裝飾器傳遞參數
在Python 的裝飾器語法中,內層函數的參數是被裝飾的函數參數,外層函數的參數是被裝飾的函數。
那麼,若是裝飾器自己也有參數應該怎麼辦呢?在Python 的裝飾器語法中, 若是裝飾器自己也有參數,則須要再嵌套一層函數。
這也是爲何讀者看到的Python裝飾器有時候是兩層嵌套, 有時候是三層嵌套的緣由

下面是一個帶參數的裝飾器。在這個裝飾器的實現中,最外層的函數是裝飾器的名稱。
這個裝飾器的做用是將被裝飾的函數執行屢次。具體執行的次數由裝飾器的參數指定

from __future__ import print_function

def times(length=1):
    def bread(func):
        def wrapper(* args ,* *kwargs):
            for i in range(length):
                func(*args, **kwargs)  # func表明被裝飾函數sandwich(),因此被裝飾函數有什麼參數這裏就要寫什麼,例如Django裏面每一個view函數都有request參數,裝飾view函數就要寫成func(request, *args, **kwargs)
        return wrapper
    return bread
    
    
@times(5)   #5傳入到length,執行5次sandwich
def sandwich(name):
    print (name)
    
sandwich('hello')

 

裝飾器的前後順序

例子中,never_cache就要先於login_required被調用。

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

使用method_decorator有時會致使TypeError異常,由於參數傳遞的緣由。固然,通常人碰不到的,碰獲得的人都不通常,都能本身查Django官方文檔掌握問題緣由。

 

參考

http://www.liujiangblog.com/blog/37/


def record_functime():
    '''
    函數執行時間統計裝飾器
    :return:
    '''
    def stopwatch(callback):
            @functools.wraps(callback)
            def wrapper(*args, **kwargs):
                start = time.time()
                body = callback(*args, **kwargs)
                end = time.time()
                timelog = 'function name:'+callback.__name__ +'; execute time:' +str(start) +'; elapsed time:'+str(end - start)
                log_helper.info(timelog)
                return body
            return wrapper
    return stopwatch
相關文章
相關標籤/搜索