首頁  >  文章  >  後端開發  >  深入了解Python裝飾器函數

深入了解Python裝飾器函數

WBOY
WBOY轉載
2022-06-24 12:50:421927瀏覽

這篇文章為大家帶來了關於python的相關知識,其中主要整理了裝飾器函數的相關問題,包括了裝飾器的形成過程、本質與功能、進階與最佳化等等內容,下面一起來看一下,希望對大家有幫助。

深入了解Python裝飾器函數

推薦學習:python

#假如我寫了一個函數f

def f():
    print('hello')

之後我想知道這段函數執行所需的時間,這好辦,我只要將程式碼改為如下就行

import time
def f():
    start = time.time()   #获取程序执行开始的时间
    print('hello')
    end = time.time()     #获取程序执行结束的时间
    print(end - start)    #得出函数f执行所要时间

f()

但之後我有寫了無數個函數f2,f3……fn,我想知道每個函數執行所需要的時間,那如果都像上面一樣改,豈不是很鬧?還是不行,因為這樣實在太麻煩了。那怎麼辦呢?於是靈機一動,寫了一個timer函數。 。 。

import time
def timer(func):
    start = time.time()
    func()
    print(time.time() - start)

def f():
    print('hello')


def f2():
    print('xorld')

timer(f)
timer(f2)

這樣看起來是不是簡單多啦?不管我們寫了多少個函數都可以呼叫這個計時函數來計算函數的執行時間

但是如果我只想用原來的方式f1(),f2(),fn()呼叫了這個函數,函數在原本執行輸出的結果不變的前提下還可以增加計算時間的功能,而不是呼叫timer(f),timer(f2)才能計算時間,該怎麼辦?

看了下面的裝飾函數你就會知道如何解決這個問題



一、裝飾器—— 形成過程

以下就是解決上面問題的程式碼的簡單版本:

import time

def f():
    print('hello')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

f = timer(f)
f()

還是這句話我只想用原來的方式f1(),f2(),fn()呼叫了這個函數,函數在原本執行輸出的結果不變的前提下還可以增加計算時間的功能,但我還是要在函數f 執行前寫f = timer(f)在這一串代碼,是不是覺得礙眼? python的開發者也覺得礙眼,所以python的開發者就為我們提供了一句語法糖來解決這個問題!



二、裝飾器 —— 初識文法糖

用@timmer取代f = timer(f),這就是一句文法糖。

import time
def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

@timer   #==> 写着这句话就相当于执行了f = timer(f)
def f():
    print('hello')


f()


三、裝飾器-本質與功能

1、實質

        裝飾器的本質就是一個閉包函數

2、功能

        在不修改原函數及其呼叫方式的情況下擴展為原始函數功能

#4、裝飾器—— 裝飾帶參數,傳回值的裝飾器

  • 1、裝飾帶一個參數的函數

剛才我們寫的裝飾器都是裝飾不帶參數的函數,現在要裝飾一個有參數的函數怎麼辦呢?

import time
def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        print(time.time() - start)
    return inner

@timer
def f(a):
    print(a)

f('hello')
  • 2、裝飾多個有不同參數但無回傳值的函數

其實裝飾帶參的函數並不是什麼難事,但假如你有兩個函數,需要傳遞的參數不一樣呢,比如  函數func1有兩個參數func1(a ,b),函數func 2只有一個參數func2(a), 且它們都想用這個裝飾器裝飾,做到計算函數執行時間?這怎麼辦呢?那就用下面程式​​碼。

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func1 = timer(func1)
def func1(a,b):
    print('in func1')

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))

输出结果:
in func1
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
  • 3、裝飾多個帶有不同參數且有回傳值的函數

現在參數的問題已經完美的解決了,可是如果你的函數是有回傳值的呢?用上面的程式碼你就拿不到回傳值了那究竟要如何解決這個問題呢?那就看下面的程式碼吧!

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))

输出结果:
in func2 and get a:aaaaaa
0.0
in func2 and get a:aaaaaa
0.0
fun2 over
  • 4、多個裝飾器裝飾同一個函數

有些時候,我們也會用到多個裝飾器裝飾同一個函數



有些時候,我們也會用到多個裝飾器裝飾同一個函數的情況。

ef wrapper1(func):   #func ----- f
    def inner1():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner1

def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner2

@wrapper2       #f = wrapper2(f) ----->> wrapper2(inner1)  == inner2
@wrapper1       #f = wrapper1(f) = inner
def f():
    print('in f')
f()    #===>>inner2
#多个装饰器装饰同一个函数

输出结果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
  • 五、裝飾器-  裝飾器進階與最佳化

1、帶參數的裝飾器

### ######上面那個裝飾器已經非常beautiful了,但是還有一個問題,如果我給程式碼中無數個函數都加了@timer這個語法糖,如果之後我又不想用它了那豈不是又要每個去將它註釋,沒日沒夜忙活3天?豈不是特別麻煩,為了使裝飾器不用時能夠更好的回收而不是一個一個去註釋或者刪除 我們引入帶有參數的裝飾器概念###
'''
为了使装饰器不用时能够更好的回收而不是一个一个去注释或者删除
我们引入带参数的装饰器概念
'''

import time
'''FLAGE的目的是用它控制装饰器的开关,
那么当我们不用的时候就不要一个一个去注释只需将True改为False就行'''

FLAGE = True
def timmer_out(flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if flag:
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args, **kwargs)
                return ret
        return inner
    return timmer
@timmer_out(FLAGE)

#timmer_out(FLAGE)
# 也相当于执行  timmer_out(FLAGE)--->>返回timmer———————>>@timmer(wahaha = timmer(wahaha))
def wahaha():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('wahahahahahaha')

wahaha()

@timmer_out(FLAGE)
def erguotou():
    time.sleep(0.1)         #不休息的话函数执行的太快难以计算时间
    print('erguotoutoutou')

erguotou()

输出结果:
wahahahahahaha
0.10152268409729004
erguotoutoutou
0.10795140266418457
  • 2、防止函数必要信息失效

'''
print(wahaha.__name__)      #查看字符串格式的函数名
print(wahaha.__doc__)       #查看一个函数的注释
'''
#下面用__name__查看holiday的函数名

from functools import wraps
def wrapper(func):
    @wraps(func)            #加在最内层函数正上方
    def inner(*args,**kwargs):
        print('在被装饰的函数执行之前做的事')
        ret = func(*args,**kwargs)
        print('在被装饰的函数执行之后做的事')
        return ret
    return inner

@wrapper        #holiday = wrapper(holiday)
def holiday(day):
    '''
    这是一个放假通知
    :param day:
    :return:
    '''
    print('全体放假%s天'%day)
    return '好开心'

print(holiday.__name__)
print(holiday.__doc__)
'''
结果是inner和None 但我们想要的是打印holiday的字符串格式的函数名和函数的注释这时该怎么办?
解决方法就是  from functools import wraps
使用语法是@wraps(被装饰的函数名)
'''

输出结果:
holiday

    这是一个放假通知
    :param day:
    :return:


六、装饰器 —— 装饰原则

  • 1、开放封闭原则

1.对原函数的功能扩展是开放的

        为什么要对功能扩展开放呢?

    对于任何一个程序来说,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许后来扩展、添加新功能。

2.对修改是封闭的

        为什么要对修改封闭呢?

                就像我们刚刚提到的,因为我们写的一个函数,很有可能在其他地方已经被导入使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经正在使用该函数的代码。

装饰器就完美遵循了这个开放封闭原则。这就是学装饰器的初衷



小结:

  • 1、装饰器的固定格式(模板)

#格式一

def timer(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner

#格式二

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

推荐学习:python

以上是深入了解Python裝飾器函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除