天天看點

清清爽爽了解Python裝飾器

不知不覺已經連續寫了13天了,很享受每天寫公衆号這個過程,通過寫作将自己每天學習的Python知識分享出來,但是學習的東西是零散的,需要自己通過一定的邏輯順序将零散的知識串起來,這很鍛煉自己的邏輯能力和寫作能力。

其實我也很讨厭很多知識混雜在一起,讓人摸不着頭緒,但是每當自己整理好一個知識點後,就覺得非常清爽,理清了頭緒、鞏固了知識。

今天一起學習一下Python裡面裝飾器這個概念吧!

需要實作記錄多個函數運作的時間這個功能,那最直接的做法如下所示

import time

def f1():
    print(time.time())
    print('This is a function')


def f2():
    print(time.time())
    print('This is a function')

f1()
f2()           

複制

1557198935.042486          
This is a function
1557198935.042486
This is a function           

複制

但是,這樣做是違背Python的原則——對修改是封閉的,對擴充是開放的。

于是可以稍加修改,為了列印兩個函數的運作時間,又單獨編寫了一個專門列印時間的函數,将函數作為一個變量傳入到這個列印時間的函數中,然後在下面進行調用。

import time

def f1():
    print('This is a function')

def f2():
    print('This is a function')

def print_current_time(func):
    print(time.time())
    func()

print_current_time(f1)
print_current_time(f2)           

複制

但是這段代碼也不好,表面看着由一個列印時間的函數負責,其實列印時間的這個函數和其他的兩個函數相關聯性很不好,它的實質和下面這段代碼是沒差別的。

import time

def f1():
    print(time.time())

def f2():
    print(time.time())

print('This is a function')
f1()
print('This is a function')
f2()           

複制

為了解決這個問題,我們就需要用到今天的主角——裝飾器。

重新定義一個裝飾器函數,再嵌套一個wrapper函數,這個函數主要負責封裝。具體代碼如下:

import time

def decorator(func):     #裝飾器
    def wrapper():       #被封裝
        print(time.time())
        func()
    return wrapper

def f1():
    print('This is a function')

f = decorator(f1)
f()           

複制

大家可以看到雖然定義了裝飾器這個函數,但是調用的時候依然很麻煩,和上面定義一個列印時間的函數沒什麼差別,看不出來有什麼優勢?

為了進一步說明裝飾器的優勢,這裡需要引入文法糖這個概念。直接在函數f1的前面加入文法糖,就可以實作列印時間的功能。

import time

def decorator(func):     #裝飾器
    def wrapper():       #被封裝
        print(time.time())
        func()
    return wrapper

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

f1()           

複制

大家可以看到這裡,通過直接調用f1()就可以實作功能。

現在考慮給2個函數列印運作時間,而且給兩個函數傳入不同個數的參數,比如說,f1(func_name)、f2(func_name1,func_name2),那該怎麼實作這個功能呢?

在這裡裝飾器隻有一個,我們怎麼将它變成一個通用的,展現出裝飾器的複用性?這裡需要用到可變參數args這個概念,它表示一組參數,具體操作如下:

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)

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

f1('test func')
f2('test func1','test func2')           

複制

如果再将難度提升一下,在前兩個函數的基礎,增加第三個函數,且第三個函數傳入三個參數,那又該怎麼操作呢?

這裡需要用到**kw這個形參,它表示一個字典,将它傳入到wrapper函數中,具體代碼如下

import time

def decorator(func):     #裝飾器
    def wrapper(*args,**kw):  #被封裝
        print(time.time())
        func(*args,**kw)
    return wrapper

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

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

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

f1('test func')
f2('test func1','test func2')
f3('test func1','test func2',a=1,b=2,c='1,2,3')           

複制

關于裝飾器就學到這,對于一個初學者來說,了解這些基本的裝飾器已經夠了,平時在自己編碼的過程中,可以根據業務需求,有意識的用到裝飾器,這樣會讓你的代碼更加fanastic。