python之路——裝飾器函數


閱讀目錄

楔子

作為一個會寫函數的python開發,我們從今天開始要去公司上班了。寫了一個函數,就交給其他開發用了。

def func1():
    print('in func1')

季度末,公司的領導要給大家發績效獎金了,就提議對這段日子所有人開發的成果進行審核,審核的標准是什么呢?就是統計每個函數的執行時間。

這個時候你要怎么做呀?

你一想,這好辦,把函數一改:

import time
def func1():
    start = time.time()
    print('in func1')
    print(time.time() - start)

func1()

來公司半年,寫了2000+函數,挨個改一遍,1個禮拜過去了,等領導審核完,再挨個給刪了。。。又1個禮拜過去了。。。這是不是很鬧心?

你覺得不行,不能讓自己費勁兒,告訴所有開發,現在你們都在自己原本的代碼上加上一句計算時間的語句?

import time
def func1():
    print('in func1')

start = time.time()
func1()
print(time.time() - start)

還是不行,因為這樣對於開發同事來講實在是太麻煩了。

那怎么辦呢?你靈機一動,寫了一個timer函數。。。

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

def func1():
    print('in func1')


def func2():
    print('in func2')

timer(func1)
timer(func2)

這樣看起來是不是簡單多啦?不管我們寫了多少個函數都可以調用這個計時函數來計算函數的執行時間了。。。盡管現在修改成本已經變得很小很小了,但是對於同事來說還是改變了這個函數的調用方式,假如某同事因為相信你,在他的代碼里用你的方法用了2w多次,那他修改完代碼你們友誼的小船也就徹底地翻了。

你要做的就是,讓你的同事依然調用func1,但是能實現調用timer方法的效果。

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

def func1():
    print('in func1')

func1 =timer  #要是能這樣的就完美了。。。可惜報錯
func1()

非常可惜,上面這段代碼是會報錯的,因為timer方法需要傳遞一個func參數,我們不能在賦值的時候傳參,因為只要執行func1 = timer(func1),timer方法就直接執行了,下面的那句func1根本就沒有意義。到這里,我們的思路好像陷入了僵局。。。

 

裝飾器的形成過程

import time

def func1():
    print('in func1')

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

func1 = timer(func1)
func1()
裝飾器——簡單版1

忙活了這么半天,終於初具規模了!現在已經基本上完美了,唯一礙眼的那句話就是還要在做一次賦值調用。。。

你覺得礙眼,python的開發者也覺得礙眼,所以就為我們提供了一句語法糖來解決這個問題!

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

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


func1()
裝飾器——語法糖

到這里,我們可以簡單的總結一下:

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

  裝飾器的功能:在不修改原函數及其調用方式的情況下對原函數功能進行擴展

還有最后一個問題要解決,剛剛我們討論的裝飾器都是裝飾不帶參數的函數,現在要裝飾一個帶參數的函數怎么辦呢?

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

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

func1(1)
裝飾器——帶參數的裝飾器

其實裝飾帶參的函數並不是什么難事,但假如你有兩個函數,需要傳遞的參數不一樣呢?

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'))
裝飾器——成功hold住所有函數傳參

現在參數的問題已經完美的解決了,可是如果你的函數是有返回值的呢?

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'))
裝飾器——帶返回值的裝飾器

剛剛那個裝飾器已經非常完美了,但是正常我們情況下查看函數的一些信息的方法在此處都會失效

def index():
    '''這是一個主頁信息'''
    print('from index')

print(index.__doc__)    #查看函數注釋的方法
print(index.__name__)   #查看函數名的方法
查看函數信息的一些方法

為了不讓他們失效,我們還要在裝飾器上加上一點來完善它:

from functools import wraps

def deco(func):
    @wraps(func) #加在最內層函數正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
print(index.__name__)
裝飾器——wraps demo

 

開放封閉原則

  1.對擴展是開放的

    為什么要對擴展開放呢?

    我們說,任何一個程序,不可能在設計之初就已經想好了所有的功能並且未來不做任何更新和修改。所以我們必須允許代碼擴展、添加新功能。

  2.對修改是封閉的

    為什么要對修改封閉呢?

    就像我們剛剛提到的,因為我們寫的一個函數,很有可能已經交付給其他人使用了,如果這個時候我們對其進行了修改,很有可能影響其他已經在使用該函數的用戶。

裝飾器完美的遵循了這個開放封閉原則。

裝飾器的主要功能和裝飾器的固定結構

裝飾器的主要功能:

在不改變函數調用方式的基礎上在函數的前、后添加功能。

裝飾器的固定格式:

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
裝飾器的固定格式——wraps版

 

帶參數的裝飾器

假如你有成千上萬個函數使用了一個裝飾器,現在你想把這些裝飾器都取消掉,你要怎么做?

一個一個的取消掉? 沒日沒夜忙活3天。。。

過兩天你領導想通了,再讓你加上。。。

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''執行函數之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''執行函數之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()
帶參數的裝飾器

 

多個裝飾器裝飾同一個函數

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

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

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

@wrapper2
@wrapper1
def f():
    print('in f')

f()
多個裝飾器裝飾同一個函數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM