python裝飾器詳解

https://blog.csdn.net/xiangxianghehe/article/details/77170585

你會Python嘛?
我會!
那你給我講下Python裝飾器吧!
Python裝飾器啊?我沒用過哎

以上是我一個哥們面試時候發生的真實對白。

———————————————-分割線——————————————————————————

簡言之,python裝飾器就是用于拓展原來函數功能的一種函數,這個函數的特殊之處在于它的返回值也是一個函數,使用python裝飾器的好處就是在不用更改原函數的代碼前提下給函數增加新的功能。?
一般而言,我們要想拓展原來函數代碼,最直接的辦法就是侵入代碼里面修改,例如:

import time
def func():print("hello")time.sleep(1)print("world")

這是我們最原始的的一個函數,然后我們試圖記錄下這個函數執行的總時間,那最簡單的做法就是:

#原始侵入,篡改原函數
import time
def func():startTime = time.time()print("hello")time.sleep(1)print("world")endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)

但是如果你的Boss在公司里面和你說:“小祁,這段代碼是我們公司的核心代碼,你不能直接去改我們的核心代碼。”那該怎么辦呢,我們仿照裝飾器先自己試著寫一下:

#避免直接侵入原函數修改,但是生效需要再次執行函數
import timedef deco(func):startTime = time.time()func()endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)def func():print("hello")time.sleep(1)print("world")if __name__ == '__main__':f = funcdeco(f)#只有把func()或者f()作為參數執行,新加入功能才會生效print("f.__name__ is",f.__name__)#f的name就是func()print()#func()

這里我們定義了一個函數deco,它的參數是一個函數,然后給這個函數嵌入了計時功能。然后你可以拍著胸脯對老板說,看吧,不用動你原來的代碼,我照樣拓展了它的函數功能。?
然后你的老板有對你說:“小祁,我們公司核心代碼區域有一千萬個func()函數,從func01()到func1kw(),按你的方案,想要拓展這一千萬個函數功能,就是要執行一千萬次deco()函數,這可不行呀,我心疼我的機器。”?
好了,你終于受夠你老板了,準備辭職了,然后你無意間聽到了裝飾器這個神器,突然發現能滿足你閆博士的要求了。?
我們先實現一個最簡陋的裝飾器,不使用任何語法糖和高級語法,看看裝飾器最原始的面貌:

#既不需要侵入,也不需要函數重復執行
import timedef deco(func):def wrapper():startTime = time.time()func()endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)return wrapper@deco
def func():print("hello")time.sleep(1)print("world")if __name__ == '__main__':f = func #這里f被賦值為func,執行f()就是執行func()f()
  • eco函數就是最原始的裝飾器,它的參數是一個函數,然后返回值也是一個函數。其中作為參數的這個函數func()就在返回函數wrapper()的內部執行。然后在函數func()前面加上@deco,func()函數就相當于被注入了計時功能,現在只要調用func(),它就已經變身為“新的功能更多”的函數了。?
    所以這里裝飾器就像一個注入符號:有了它,拓展了原來函數的功能既不需要侵入函數內更改代碼,也不需要重復執行原函數。
#帶有參數的裝飾器
import timedef deco(func):def wrapper(a,b):startTime = time.time()func(a,b)endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)return wrapper@deco
def func(a,b):print("hello,here is a func for add :")time.sleep(1)print("result is %d" %(a+b))if __name__ == '__main__':f = funcf(3,4)#func()

然后你滿足了Boss的要求后,Boss又說:“小祁,我讓你拓展的函數好多可是有參數的呀,有的參數還是個數不定的那種,你的裝飾器搞的定不?”然后你嘿嘿一笑,深藏功與名!

#帶有不定參數的裝飾器
import timedef deco(func):def wrapper(*args, **kwargs):startTime = time.time()func(*args, **kwargs)endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)return wrapper@deco
def func(a,b):print("hello,here is a func for add :")time.sleep(1)print("result is %d" %(a+b))@deco
def func2(a,b,c):print("hello,here is a func for add :")time.sleep(1)print("result is %d" %(a+b+c))if __name__ == '__main__':f = funcfunc2(3,4,5)f(3,4)#func()
  • ?
  • 老板說:“可以的,小祁,我這里一個函數需要加入很多功能,一個裝飾器怕是搞不定,裝飾器能支持多個嘛”?
    最后你就把這段代碼丟給了他:
#多個裝飾器import timedef deco01(func):def wrapper(*args, **kwargs):print("this is deco01")startTime = time.time()func(*args, **kwargs)endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)print("deco01 end here")return wrapperdef deco02(func):def wrapper(*args, **kwargs):print("this is deco02")func(*args, **kwargs)print("deco02 end here")return wrapper@deco01
@deco02
def func(a,b):print("hello,here is a func for add :")time.sleep(1)print("result is %d" %(a+b))if __name__ == '__main__':f = funcf(3,4)#func()'''
this is deco01
this is deco02
hello,here is a func for add :
result is 7
deco02 end here
time is 1003 ms
deco01 end here
'''

多個裝飾器執行的順序就是從最后一個裝飾器開始,執行到第一個裝飾器,再執行函數本身。

盜用評論里面一位童鞋的例子:

def dec1(func):  print("1111")  def one():  print("2222")  func()  print("3333")  return one  def dec2(func):  print("aaaa")  def two():  print("bbbb")  func()  print("cccc")  return two  @dec1  
@dec2  
def test():  print("test test")  test()  

輸出:

aaaa  
1111  
2222  
bbbb  
test test  
cccc  
3333

參考:Python 裝飾器:http://python.jobbole.com/82344/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383638.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383638.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383638.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SQL Server【一】簡介和基本概念和命令

數據結構和數據庫的區別 數據庫是應用軟件級別研究數據的存儲和操作(主要針對磁盤上的數據) 數據結構是在系統軟件級別研究數據的存儲和操作(主要是針對內存中的數據) 對硬盤數操作是數據庫的強項,是數據庫研究的核心…

SQL Server【二】單表查詢

查詢 計算列 select * from emp; -- *通配符,表示所有的字段 -- from emp 從emp表查詢select empno, ename from emp; select ename as "員工姓名", sal*12 as "年薪" from emp;-- as可以省略,用于設置字段名 -- 注意用雙引號將字…

SQL Server【三】連接查詢

將兩個表或者兩個以上的表以一定的連接條件連接起來,從中檢索出滿足條件的數據。 內連接 使用inner join,inner可以省略 -- 查詢員工的姓名和部門名稱 select "E".ename as "員工姓名", "D".dname as "部門名稱&q…

Linux下網絡socket編程——實現服務器(select)與多個客戶端通信

一、關于socket通信 服務器端工作流程: 調用 socket() 函數創建套接字 用 bind() 函數將創建的套接字與服務端IP地址綁定調用listen()函數監聽socket() 函數創建的套接字,等待客戶端連接 當客戶端請求到來之后調用 accept()函數接受連接請求&#xff0c…

SQL Server【四】

identity 主鍵自動增長,用戶不需要為identity修飾的主鍵賦值 create table student (std_id int primary key identity(10,5),--(10,5)可以省略,默認為(1,1)std_name nvarchar(200) not null ) select * from student insert into student values (張三…

TCP服務器/客戶端實例(C/C )

1.1、Linux下的TCP服務器&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h>void error_handling(char *mess…

pip代理解決pip下載失敗問題

在用pip下載各種庫的時候發現速度實在是太慢了&#xff0c;還會有各種奇奇怪怪的問題&#xff0c;動不動就玄學失敗。 在網上找來找去找到知乎上一位大佬的回答&#xff1a;傳送門&#xff0c;用了豆瓣的代理。哇咔咔&#xff0c;媽媽再也不用擔心我下載失敗了。 代理&#x…

實現Linux select IO復用C/S服務器代碼

服務器端#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<sys/socket.h> #include<sys/stat.h> #include<arpa/inet.h> #include <sys/select.h>#define MAXBUF 256 #define MAXLISTEN…

Bellman-Ford算法和SPFA算法

Belloman-Ford算法 算法介紹 Dijkstra可以解決單源無負邊最短路徑問題。但是當遇到含有負邊的單源最短路徑問題就需要使用Bellman-Ford算法來解決。Bellman-Ford算法還可以檢測出負環。 算法步驟 源點s,數組d[u]d[u]d[u]表示s到u的最短距離初始化&#xff1a;d[s]0d[s]0d[s…

C語言實現單鏈表操作

SLIST_H #ifndef __SLIST_H__ #define __SLIST_H__ #include<cstdio> #include<malloc.h> #include<assert.h> typedef int ElemType; typedef struct Node { //定義單鏈表中的結點信息 ElemType data; //結點的數據域 struct Node *next; //結點的指針…

計算機網絡【4】傳輸層

概述 傳輸層是只有主機才有的層次 傳輸層的功能&#xff1a; 傳輸層提供進程和進程之間的邏輯通信&#xff08;網絡層提供主機與主機之間的邏輯通信&#xff09;復用和分用傳輸層對收到的報文進行差錯檢測 傳輸層有兩個協議&#xff1a; 面向連接的傳輸層控制協議TCP&…

Plotly繪圖

在做Python數據分析實驗的時候發現使用Plotly庫繪圖比較漂亮&#xff0c;在網上找到了一個比較好的教程&#xff0c;這里記錄一下&#xff0c;方便以后查找。 傳送門

計算機網絡【0】概述

計算機網絡概念和功能 概念 是一個將分散的、具有獨立功能的計算機系統&#xff0c;通過通信設備與線路連接起來&#xff0c;由功能完善的軟件實現資源共享和信息傳遞的系統。 計算機網絡是互連的、自治&#xff08;無主從關系&#xff09;的計算機集合。 功能 數據通信&am…

計算機網絡【1】物理層

物理層解決如何在連接各種計算機的傳輸媒體上傳輸數據比特流&#xff0c;而不是指具體的傳輸媒體。 確定與傳輸媒體接口有關的特性 機械特性&#xff1a;定義物理連接的特性&#xff0c;如規格、接口形狀、引線數目、引腳數目、排列電氣特性&#xff1a;規定傳輸二進制位時的電…

計算機網路【2】數據鏈路層

結點&#xff1a;主機、路由器 鏈路&#xff1a;兩個節點的物理通道 數據鏈路&#xff1a;邏輯通道&#xff0c;把實現 控制數據傳輸協議的硬件和軟件加到鏈路上就構成數據鏈路 幀&#xff1a;鏈路層的協議數據單元&#xff0c;封裝網絡層數據報 數據鏈路層在物理層提供服務的…

計算機網絡【5】應用層

應用層對應用程序的通信提供服務 應用層協議定義&#xff1a; 應用層的功能&#xff1a; 文件傳輸、訪問和管理電子郵件虛擬終端查詢服務和遠程作業登錄 重要協議&#xff1a;FTP、SMTP、POP3、HTTP、DNS 網絡應用模型 客戶/服務器模型&#xff08;Client/Server&#x…

操作系統【八】文件管理

文件&#xff1a;一組有意義的信息/數據集合 文件的屬性&#xff1a; 文件名&#xff1a;由創建文件的用戶決定文件名&#xff0c;主要是為了方便用戶找到文件。同一個目錄下不允許有重名文件標識符&#xff1a;一個系統內的個文件標識符唯一&#xff0c;對用戶來說毫無可讀性…

數據庫原理及應用【六】數據庫設計

數據依賴 函數依賴FD&#xff1a;一個屬性或者一組屬性的值可以決定另一個屬性的值 多值依賴MVD&#xff1a;一個屬性或者一組屬性的值可以決定另一個屬性的值的集合。FD是MVD的特例 符號表示&#xff1a;Name->->Course&#xff0c;課程多值依賴于姓名 連接依賴&#x…

數據可視化【一】JavaScript學習

本博客是我學習Curran Kelleher老師數據可視化課程的筆記&#xff0c;感興趣的小伙伴可以點擊這里學習。 three cores of data visualization: analysisdesignconstruction 推薦書籍《visualization analysis & design》 使用https://vizhub.com/進行編程學習&#xff…

數據庫原理及應用【二】數據模型

層次模型 tree Record and fieldParent-Child relationship(PCR) 每個記錄類型只有一個父節點 無法表達多對多信息 采用虛記錄解決多對多 網狀數據模型 系&#xff1a;主記錄->屬記錄 主記錄和屬記錄都可以有好多個 關系模型 表&#xff1a;table/relation 擁有更高的…