python中的裝飾器-(重復閱讀)

---1---

假設我們要增強某個函數的功能,比如,在函數調用前后自動打印日志,但又不希望修改某個函數的定義,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator).

裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼并繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。

---2---

本質上,decorator就是一個返回函數的高階函數。所以,我們要定義一個能打印日志的decorator.

觀察下面的代碼:
?

def log(func):def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper
@log
def now():print('2015-3-25')

@log放到now()函數的定義處,相當于執行了語句:

now = log(now)

由于log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,于是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。

wrapper()函數的參數定義是(*args, **kw),因此,wrapper()函數可以接受任意參數的調用。在wrapper()函數內,首先打印日志,再緊接著調用原始函數。

---3---

如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數,寫出來會更復雜。比如,要自定義log的文本:

def log(text):def decorator(func):def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)return wrapperreturn decorator

這個3層嵌套的decorator用法如下:

@log('execute')
def now():print('2015-3-25')

和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:

>>> now = log('execute')(now)

我們來剖析上面的語句,首先執行log('execute'),返回的是decorator函數,再調用返回的函數,參數是now函數,返回值最終是wrapper函數。

以上兩種decorator的定義都沒有問題,但還差最后一步。因為我們講了函數也是對象,它有__name__等屬性,但你去看經過decorator裝飾之后的函數,它們的__name__已經從原來的'now'變成了'wrapper'

>>> now.__name__
'wrapper'

因為返回的那個wrapper()函數名字就是'wrapper',所以,需要把原始函數的__name__等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執行就會出錯。

不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是干這個事的,所以,一個完整的decorator的寫法如下:

import functoolsdef log(func):@functools.wraps(func)def wrapper(*args, **kw):print('call %s():' % func.__name__)return func(*args, **kw)return wrapper

或者針對帶參數的decorator:

import functoolsdef log(text):def decorator(func):@functools.wraps(func)def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)return wrapperreturn decorator

上面的東西看的一知半解的,看完下面的這一段應該會好點:

def check_is_admin(f):def wrapper(*args,**kwargs):if kwargs.get('username') != 'admin':raise Exception("This user is not allowed to get food")return f(*args, **kwargs)return wrapperclass Storage(object):@check_is_admindef get_food(self, username,food):return Storage.get(food)@check_is_admindef put_food(self,username,food):return Storage.put(food)

?

參考:

(1).https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584(廖雪峰官方文檔)

(2).https://www.zhihu.com/question/21408921/answer/129036707?(怎樣才能寫出 Pythonic 的代碼?)

(3).https://www.zhihu.com/question/26930016/answer/99243411? ?(如何理解Python裝飾器?)

?

?

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

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

相關文章

[轉帖]好技術領導,差技術領導

團隊合作一個優秀的技術領導必然是團隊的一份子,他們認為當整個團隊成功時自己才稱得上成功。他們不僅要做好繁雜和不討好的本職工作,還要清除項目中的障礙,從而讓整個團隊能夠以100%的效率運轉起來。一個好的技術領導會努力拓寬團隊在技術上…

python有哪些常用的庫

參考: (1).https://www.zhihu.com/question/20501628/answer/19542741(Python 常用的標準庫以及第三方庫有哪些?)

C#打開文件對話框和文件夾對話框

打開文件對話框OpenFileDialog OpenFileDialog ofd new OpenFileDialog();ofd.Filter "Excel文件(*.xls;*.xlsx)|*.xls;*.xlsx|所有文件|*.*";ofd.ValidateNames true;ofd.CheckPathExists true;ofd.CheckFileExists true;if (ofd.ShowDialog() DialogResult.O…

debian安裝flash插件

$ sudo apt-get install flashplugin-nonfree 轉載于:https://www.cnblogs.com/vipzrx/p/3554839.html

python中的拷貝

1.賦值: 只是復制了新對象的引用,不會開辟新的內存空間。 2.淺拷貝: 創建新對象,其內容是原對象的引用。 淺拷貝有三種形式:切片操作,工廠函數,copy模塊中的copy函數。 如: lst [1,2,3,[4,5]] …

ZOJ 2112 Dynamic Rankings

這里是題目地址 其實就是帶修改的區間第K大。 寫了一下BIT套主席樹,內存飛起,似乎需要特別的優化技巧 所以還是寫了一下線段樹套平衡樹,跑了1s左右。 其實線段樹套平衡樹就是歸并樹的自然擴展而已。 歸并樹是把歸并排序的過程建成一顆線段樹…

python3[進階]8.對象引用、可變性和垃圾回收

文章目錄8.1變量不是盒子8.2 標識,相等性和別名8.2.1 在和is之間選擇8.2.2 元組的相對不可變性8.3 默認做淺復制(拓展)為任意對象做深復制和淺復制深拷貝和淺拷貝有什么具體的區別呢?8.4 函數的參數作為引用時8.4.1 不要使用可變類型作為參數…

openfire修改服務器名稱方法

1.登陸openfire管理頁面,在主頁面下方選擇編輯屬性,修改服務器名稱為當前主機名稱,點擊保存屬性,按頁面提示重啟服務器。 2.重啟后,主頁的服務器屬性下的服務器名稱出現一個嘆號,鼠標放上去顯示Found RSA c…

python (第八章)補充-可迭代對象(補充高階函數,以及常用的高階函數)

文章目錄可迭代對象迭代器什么是迭代器什么是生成器生成器的作用生成器的注意事項總結:高階函數什么是高階函數?map()函數filter()函數reduce()函數參考可迭代對象 我們已經知道,可以直接作用于for循環的數據類型有以下幾種: 一類…

網絡閱讀開篇

網絡閱讀也符合馬太效應,投入的時間越多,獲取的有效信息卻越來越少,因此做出以下規定: 1、限制網絡閱讀時間; 2、每次閱讀做總結。 本來想的挺簡單的,隨便搜了一下,居然一部小心拜讀了兩位大神的…

python (第二章)數據結構

文章目錄2.5 對序列使用 和 建立由列表組成的列表2.6序列的增量賦值(和)關于 的謎題補充:extend()方法和有什么區別呢?2.7 list.sort方法和內置函數sorted(排序)2.8 用bisect來管理已排序的序列2.8.2用bisect.insort插入元素2.9 當…

數據庫 CURD測試題【簡單】

文章目錄1.組合兩個表基本信息要求答案2.第二高的薪水基本信息要求答案3.查找重復的電子郵箱基本信息要求答案4.超過經理收入的員工基本信息要求答案:5.超過5名學生的課信息:要求答案6.有趣的電影信息要求答案7.交換工資(updeta,條件判斷&…

JAVA學習資料整理

今天偶然間發現之前一個群里發過的一篇關于JAVA學習資料的東西。本著服務大眾的精神,搬來了博客園: 《JAVA編程思想》第四版(英文原版)下載地址:http://115.com/file/e7fzi0fm《JAVA開發實戰經典》下載地址&#xff1a…

mysql快速了解

文章目錄數據庫了解:快速操作:安裝mysql啟動,關閉,重啟mysql服務連接mysql的root用戶創建數據庫刪除數據庫選擇數據庫mysql 數據類型MySQL 創建數據表MySQL 刪除數據表MySQL 插入數據MySQL 查詢數據MySQL WHERE 子句BINARY 關鍵字MySQL UPDATE 更新批量更…

javascript編程風格(粗略筆記)

1、空格 緊湊型&#xff1a;    project.MyClass function(arg1, arg2){  松散型&#xff1a;    for( i 0; i < length; i ){ 2、代碼行長度  最多80個字符 3、命名: 采用駝峰式方法命名(開始的第一個單詞小寫&#xff0c;之后的所有單詞首字母大寫)  var …

數據結構 面試題

文章目錄1.數組1.1 尋找數組中第二小的元素1.2 找到數組中第一個不重復出現的整數1.3合并兩個有序數組1.4 重新排列數組中的正值和負值2.棧2.1 前綴表達式&#xff0c;中綴表達式&#xff0c;后綴表達式2.1.1 中綴表達式轉化為后綴表達式2.1.2 中綴表達式轉化為前綴表達式2.2使…

WPF之無法觸發KeyDown或者KeyUp鍵盤事件

有時候我們可能在Panel(StackPanel、Canvas、Grid)上或者是在一些默認不支持Focus的控件上添加了KeyDown或者KeyUp&#xff0c;可是殘酷的現實告訴我們&#xff0c;這是無法觸發的&#xff0c;怎么辦呢&#xff0c;很簡單&#xff0c;只需一句代碼。 private void MouseLeftBut…

宣布在日本地區正式發布 Windows Azure

&#xfeff;&#xfeff;昨天&#xff0c;我與 Microsoft 日本的集團副總裁 Yasuyuki Higuchi 一同站在臺上&#xff0c;宣布在兩個新地區正式發布 Windows Azure&#xff1a;日本東部和日本西部。能夠親自見證 Microsoft 對日本市場的持續承諾&#xff0c;對我來說是莫大的榮…

運行cmd狀態下MySQL導入導出.sql文件

MySQL導入導出.sql文件步驟如下&#xff1a; 一.MySQL的命令行模式的設置&#xff1a; 桌面->我的電腦->屬性->環境變量->新建-> PATH“&#xff1b;path\mysql\bin;”其中path為MySQL的安裝路徑。 二.簡單的介紹一下命令行進入MySQL的方法&#xff1a; 1.C:\&g…