python中decorator-裝飾器的用法

裝飾器的定義

“”"
裝飾器:
把一個函數當作參數,返回一個替代版的函數
本質上就是一個返回函數的函數

“在不改變原函數的基礎上,給函數增加功能”
“”"
在企業中,別人定義好的函數我們一般是不能改動的,那麼如果我們想在這個函數的基礎上進行一些改動,裝飾器就是一個很好的工具:


比如我們定義一個函數,他的功能是打印出hello world

def fun():
    print('hello world')

如果我們想讓在打印helloworld之前打印別的東西,且不改變原函數,我們可以:
#一
在這裏插入圖片描述
這樣的話如果次數太多會變的麻煩
#二:

def fun():
    print('hello world')
# print('linux to linux')
# fun()

def fun1():
    print('linux to linux')
    fun()
fun1()

在這裏插入圖片描述
這樣的話函數名變成了fun1,調用方式改變,體現不出原函數。
#三:

def f1():
    print('hello world')
#定義裝飾器
def decorator(fun):
    def wrapper():
        print('linux to linux')
        fun()
    return wrapper         這裏不加括號,加上括號代表括號的運行結果,不加代表函數本身。

f = decorator(f1)           #把f1這個函數傳進去,用f調用 
f()

在這裏插入圖片描述
這樣函數名也變了,也有些體現不出原函數。

#四:
python中的語法糖是專門調用裝飾器的

放置於函數的上方,以@開頭,加上定義的裝飾器函數,就可以直接調用裝飾器的函數,先運行裝飾器函數,在運行已經定義的函數。

def decorator(fun):
    def wrapper():
        print('linux to linux')
        fun()
    return wrapper
@decorator         #裝飾器      
def f1():
    print('hello world')

f1()

在這裏插入圖片描述


練習:
已知函數:

def f1(func_name):
print('This is a function ’ + func_name)

要在打印的字符串前打印時間,要求用裝飾器:
1,

import time

def decorator(func):
    def wrapper(*args):               # *args 表示可以接收多個參數
        print(time.time())            # 加的打印時間功能
        func(*args)
    return wrapper

@decorator
def f1(func_name):
    print('This is a function ' + func_name)
f1 ('test')

運行,
在這裏插入圖片描述
2,是否可以接收多個參數:

import time

def decorator(func):
    def wrapper(*args):
        print(time.time())
        func(*args)
    return wrapper

@decorator
def f2(func_name1,func_name2):
    print('This is a function ' + func_name1)
    print('This is a function ' + func_name2)
f2 ('test1','test2')

在這裏插入圖片描述
可以接受多個參數。

3,關鍵字參數

import time

def decorator(func):
    def wrapper(*args,**kwargs):         **kwargs代表接收關鍵字參數
        print(time.time())
        func(*args,**kwargs)
    return wrapper              #這是標準的裝飾器寫法

@decorator
def f3(func_name1,func_name2,**kwargs):
    print('This is a function ' + func_name1)
    print('This is a function ' + func_name2)
    print(kwargs)

f3('test1','test2',a=1,b=2,c='hello')

在這裏插入圖片描述


練習二:

“”"
用 裝飾器實現一個函數計時器
“”"

函數計時器就是在函數前後加上時間,然後兩個在一相減。

import time

def timetest(fun):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        fun(*args,**kwargs)
        end_time = time.time()
        print('the running time is: %.6f' %(end_time - start_time))
    return wrapper

@timetest
def fun_list(n):
    return [2 * i for i in range(n)]

#@timetest
#def fun_map(n):
#    return list(map(lambda x:x*2 ,range(n)))

print(fun_list(10000))           單純執行函數只是執行函數裏面的內容,所以要print打印出來

但是這樣的執行結果是這樣的:
在這裏插入圖片描述
因爲在執行裝飾器函數的時候,會先進入裝飾器函數中,fun(*args,**kwargs) 這個代碼只是運行了對應的 fun_list() 函數的內容,缺少了對其運行結果的處理,所以應定義返回值,wrapper 中並沒有返回值,返回值對應的是執行結果,只有定義了返回值,纔可以看到執行結果。

import time

def timetest(fun):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = fun(*args,**kwargs)       #這一行
        end_time = time.time()
        print('the running time is: %.6f' %(end_time - start_time))
        return res						#這一行
    return wrapper

@timetest
def fun_list(n):
    return [2 * i for i in range(n)]

#@timetest
#def fun_map(n):
#    return list(map(lambda x:x*2 ,range(n)))

print(fun_list(10000))

執行:
在這裏插入圖片描述

functools工具包中的wraps裝飾器作用及用法

在他人使用函數式,爲了使使用者易於讀懂,會使用wraps工具包保留原函數名和幫助信息文檔,通常卸載wrapper函數上方,用法如下:

import time
import functools

def timetest(fun):
    """This is a decorator fun"""
    @functools.wraps(fun)         不加這條命令會打印出 wrapper 的幫助文檔和函數名
    def wrapper(*args,**kwargs):
        """This is a wrapper function"""
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('the running time is: %.6f' %(end_time - start_time))
        return res
    return wrapper

@timetest

def fun_list(n):
    """This is the fun_list function"""          幫助文檔
    return [2 * i for i in range(n)]

# print(fun_list(10000))
print(fun_list.__doc__)
print(fun_list.__name__)

這時使用 print(fun_list.doc) 命令就可以看見原函數的幫助文檔
使用 print(fun_list.name) 命令就可以看見原函數名
在這裏插入圖片描述
在這裏插入圖片描述


練習二:

創建裝飾器, 要求如下:

  1. 創建add_log裝飾器, 被裝飾的函數打印日誌信息;
  2. 日誌格式爲: [字符串時間] 函數名: xxx, 運行時間:xxx, 運行返回 值結果:xxx
import time
import functools

def add_log(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('[%s] 函數名: %s, 運行時間: %.6f,運行返回值結果: %d' %(time.ctime(),fun.__name__,end_time - start_time,res) )
        return res
    return wrapper
    
@add_log
def add(x,y):
    time.sleep(1)
    return x+y

add(1,10)

執行結果:
在這裏插入圖片描述


##判斷參數是不是一個類型:

用 isinstance 判斷一個參數是不是後面跟的數據類型。
在這裏插入圖片描述

練習三:帶參數的裝飾器

編寫裝飾器required_types, 條件如下:

 1). 當裝飾器爲 @required_types(int,float) 確保函數接收到的每一>個參數都是int或者float類型;
 
 2). 當裝飾器爲 @required_types(list) 確保函數接收到的每一個參數 都是list類型;
 
 3). 當裝飾器爲 @required_types(str,int) 確保函數接收到的每一個>參數都是str或者int類型;
 
 4). 如果參數不滿足條件, 打印 TypeError:參數必須爲xxxx類型
import functools

def required_types(*kind):         #這個函數用來接收數據類型
    def required(fun):             #這個函數用來導入函數
        @functools.wraps(fun)
        def wrapper(*args,**kwargs):           #裝飾器函數
            for i in args:
                if not isinstance(i,kind):
                    print('TypeError: 參數必須爲%s,%s類型' %kind)
                    exit()
            else:
                res = fun(*args,**kwargs)
                return res
        return wrapper
    return required

@required_types(float,float)      #括號內的參數爲數據類型
def add(a,b):
    return a + b

print(add(1.2,2.1))

在這裏插入圖片描述
兩個數據都是浮點型時執行結果正常。
在這裏插入圖片描述
數據類型不符合裝飾器參數時報錯

練習四:多個裝飾器

#裝飾器1
def decorator_a(fun):
    def inner_a(*args,**kwargs):
        print('This is inner_a')
        res = fun(*args,**kwargs)
        return res
    return inner_a

#裝飾器2
def decorator_b(fun):
    def inner_b(*args,**kwargs):
        print('This is inner_b')
        b = fun(*args,**kwargs)
        return b
    return inner_b

#都放在函數上面,會按順序執行附加功能
@decorator_b
@decorator_a
def f(x):
    print('This is f')
    return x*2

print(f(1))

在這裏插入圖片描述
可以看出先執行了裝飾器 b ,因爲它放在上面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章