Python—裝飾器函數

来源:https://www.cnblogs.com/Golanguage/archive/2020/02/15/12314935.html
-Advertisement-
Play Games

楔子 作為一個會寫函數的python開發,我們從今天開始要去公司上班了。寫了一個函數,就交給其他開發用了。 def func1(): print('in func1') 季度末,公司的領導要給大家發績效獎金了,就提議對這段日子所有人開發的成果進行審核,審核的標準是什麼呢?就是統計每個函數的執行時間。 ...


楔子

作為一個會寫函數的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()

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

你覺得礙眼,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'))

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

裝飾器——帶返回值的裝飾器

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__)   #查看函數名的方法

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

裝飾器——warps demo

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__)

開放封閉原則

  1.對擴展是開放的

    為什麼要對擴展開放呢?

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

  2.對修改是封閉的

    為什麼要對修改封閉呢?

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

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

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

裝飾器的主要功能

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

裝飾器的固定格式

def timer(func):
    def inner(*args,**kwargs):
        '''執行函數之前要做的'''
        re = func(*args,**kwargs)
        '''執行函數之後要做的'''
        return re
    return inner
# wraps

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

帶參數的裝飾器

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

一個一個的取消掉? 沒日沒夜忙活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()

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • https://www.cnblogs.com/WUXIAOCHANG/p/10886534.html https://blog.csdn.net/pengjwhx/article/details/84867112 ...
  • 楔子 在講今天的內容之前,我們先來講一個故事,講的什麼呢?從前有座山,山裡有座廟,廟裡有個老和尚講故事,講的什麼呢?從前有座山,山裡有座廟,廟裡有個老和尚講故事,講的什麼呢?從前有座山,山裡有座廟,廟裡有個老和尚講故事,講的什麼呢?從前有座山,山裡有座廟,廟裡有個老和尚講故事,講的什麼呢...... ...
  • 楔子 在講新知識之前,我們先來複習複習函數的基礎知識。 問:函數怎麼調用? 函數名() 如果你們這麼說。。。那你們就對了!好了記住這個事兒別給忘記了,咱們繼續談下一話題。。。 來你們在自己的環境里列印一下自己的名字。 你們是怎麼打的呀? 是不是print('xxx'),好了,現在你們結合我剛剛說的函 ...
  • 第一個Django程式 從本章節開始將通過實現一個投票應用程式,來讓用戶逐步的瞭解Django。這個程式由兩步分組成: 公共站點,允許用戶訪問進行投票,和查看投票。 站點管理,允許添加,刪除,修改投票信息。 1、創建項目 本文繼承前一篇章節的環境(centos 7 python3.6.2 Djang ...
  • 註:若沒有特指是 靜態成員時,預設都是普通成員; 1 類中的普通成員 類中的成員變數 和 成員函數 是分開存儲的。其中, 1)每個對象都有獨立的成員變數;成員變數可以存儲在 棧空間、堆空間、全局數據區; 2)所有對象共用類的成員函數;成員函數 只能存儲在 代碼段; 2 類中的靜態成員(static) ...
  • 問題描述: Pycharm創建Django項目提示:HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out 解決方法: 這個問題是因為訪問files.pythonhosted.org超時引起的,換為國內 ...
  • javaSE學習筆記(15) 緩衝流、轉換流、序列化流 緩衝流 昨天複習了基本的一些流,作為IO流的入門,今天我們要見識一些更強大的流。比如能夠高效讀寫的緩衝流,能夠轉換編碼的轉換流,能夠持久化存儲對象的序列化流等等。這些功能更為強大的流,都是在基本的流對象基礎之上創建而來的,相當於是對基本流對象的 ...
  • 楔子 假如我現在有一個列表l=['a','b','c','d','e'],我想取列表中的內容,有幾種方式? 首先,我可以通過索引取值l[0],其次我們是不是還可以用for迴圈來取值呀? 你有沒有仔細思考過,用索引取值和for迴圈取值是有著微妙區別的。 如果用索引取值,你可以取到任意位置的值,前提是你 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...