2020-08-23 Python 裝飾器的理解

1. 為什么要使用裝飾器

比如下面代碼要增加一個打印時間的功能,可以通過對原函數(shù)進行更改,或者再增加一個函數(shù)完成。
eg:

import time

def f2():
    print("This is f2")

增加一個打印時間的功能

a.直接更改原函數(shù)實現(xiàn)

def f2():
    print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
    print("This is f2")

b.通過新增加一個函數(shù)實現(xiàn)

def print_now_time():
    print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
    f2()

但是如果要是有很多個函數(shù),那么就要對很多個函數(shù)重新改寫,或者重新定義一個函數(shù)來實現(xiàn)。
那么有沒有辦法,在保持原有函數(shù)調(diào)用的方法不變,實現(xiàn)增加一個打印時間的功能。那么就引出了裝飾器。

def deco1(func):
    def inner():
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        return func()
    return inner

@deco1
def f2():
    print("This is f2")

f2()

通過定義裝飾器,在不改變原有函數(shù)調(diào)用方法,實現(xiàn)了新的功能。這個是我理解的裝飾器的作用。

2.裝飾器執(zhí)行的順序

eg:

import time

def deco1(func):
    print('裝飾器開始運行了!')
    def inner():
        print('函數(shù)執(zhí)行開始了!')
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        return func()
        print('函數(shù)執(zhí)行結(jié)束了!')
    print('裝飾器運行結(jié)束了!')
    return inner

@deco1
def f2():
    print("This is f2")

f2()

運行結(jié)果:
裝飾器開始運行了!
裝飾器運行結(jié)束了!
函數(shù)執(zhí)行開始了!
2020-08-23 10:19:52
This is f2
函數(shù)執(zhí)行結(jié)束了!

通過以上我們可以知道,裝飾器在程序初始化的時候,就已經(jīng)開始運行。但裝飾器里面的函數(shù)是在程序開始執(zhí)行的時候,才開始調(diào)用。

那么如果有多個裝飾器,他們的執(zhí)行順序又是怎么樣的呢?
eg:

import time

def deco1(func):
    print('裝飾器1開始運行了!')
    def inner():
        print('函數(shù)1執(zhí)行開始了!')
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        time.sleep(3)
        return func()
        print('函數(shù)1執(zhí)行結(jié)束了!')
    print('裝飾器1運行結(jié)束了!')
    return inner

def deco2(func):
    print('裝飾器2開始運行了!')
    def inner():
        print('函數(shù)2執(zhí)行開始了!')
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        time.sleep(2)
        return func()
        print('函數(shù)2執(zhí)行結(jié)束了!')
    print('裝飾器2運行結(jié)束了!')
    return inner

@deco1
@deco2
def f2():
    print("This is f2")

運行結(jié)果:
裝飾器2開始運行了!
裝飾器2運行結(jié)束了!
裝飾器1開始運行了!
裝飾器1運行結(jié)束了!
函數(shù)1執(zhí)行開始了!
2020-08-23 10:24:58
函數(shù)2執(zhí)行開始了!
2020-08-23 10:25:01
This is f2
函數(shù)2執(zhí)行結(jié)束了!
函數(shù)1執(zhí)行結(jié)束了!

從上面我們可以看到,裝飾器是從內(nèi)向外開始初始化,裝飾器內(nèi)的程序是從外開始先內(nèi)運行。

3.以上函數(shù)是未帶參數(shù),如果需要裝飾的函數(shù)帶參數(shù),那么裝飾器又是怎么樣的呢?

eg:

import time

def dec3(func):
    def inner(*args,**kwargs):
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        return func(*args,**kwargs)
    return inner

@dec3
def f3(name):
    print('Good morning',name)

f3('Jon')

輸出結(jié)果:

2020-08-23 10:34:14
Good morning Jon

4. 函數(shù)可以帶參數(shù)去執(zhí)行了,那么裝飾器是否可以帶上參數(shù)呢?

eg:

import time

def judeg(times):
    def dec4(func):
        def inner(*args,**kwargs):
            hour=time.localtime().tm_hour - times
            if hour >=0:
                print('現(xiàn)在距離{times}點已過{hour}小時'.format(times=times,hour=hour))
            else:
                print('現(xiàn)在距離{times}點還有{hour}小時'.format(times=times,hour=-hour))
            return func(*args,**kwargs)
        return inner
    return dec4

@judeg(12)
def f4(name):
    print("Hello",name)

f4('Mike')

運行結(jié)果:

現(xiàn)在距離12點還有1小時
Hello Mike

5. 類裝飾器

eg:

class Foo:
    def __init__(self,func):
        self._func = func

    def __call__(self):
        print("裝飾器開始運行!")
        self._func()
        print("裝飾器運行結(jié)束!")
@Foo
def f1():
    print('Hello')


f1()

參考1
參考2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容