摘要:示例中我們編寫的裝飾器只接受一個隱式參數即被裝飾的方法,在使用此裝飾器時本身看上去是并沒有參數的。實際上裝飾器里的后接收一個函數,該函數以被裝飾的函數例子中是為參數,并且返回一個函數。
探究裝飾器參數 編寫傳參的裝飾器
通常我們見到的簡單裝飾器這樣的:
import json import functools def json_output(func): @functools.wraps(decorated) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result) return inner @json_output def f(): return {"status": "done"}
當裝飾器應用于函數 f 上時,它接受 f 作為其參數,返回一個函數 inner ,且將他綁定到變量f上。
示例中我們編寫的裝飾器 json_output 只接受一個隱式參數——即被裝飾的方法,在使用此裝飾器時本身看上去是并沒有參數的。然而有時候需要讓裝飾器自身帶有一些需要的信息,從而使裝飾器可以使用恰當的方式裝飾方法。比如上面的例子中,我們想通過向裝飾器傳入不同的參數來控制輸出結果的縮進(indent)和排序(sort)。我們可以這么做:
import json import functools def json_output(indent=None, sort_keys=False): def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner return actual_decorator @json_output(indent=4) def f(): return {"status": "done"}理解傳參的裝飾器
初次看起來會覺得比較繞人,因為函數里嵌套了兩個函數定義,然而實際上和之前一個版本的區別在于為了接收json序列化的參數多包裝了一層,所以
@json_output(indent=4) def f(): return {"status": "done"} # 相當于 @actual_decorator def f(): return {"status": "done"}
這樣看起來就會明晰很多。
實際上, 裝飾器里的 @ 后接收一個函數,該函數以被裝飾的函數(例子中是f)為參數,并且返回一個函數。當需要在裝飾函數的同時傳入參數的話,那么就需要多包裝一層,先傳入參數(例子中是 indent=4 )返回一個裝飾的函數(例子中是 actual_decorator ), 這個返回的的函數 就跟以前一樣接受被裝飾的函數(f)作為參數并且返回一個函數作為裝飾最后的方法供調用。
傳參和不傳參的兼容然而當我們像上面那樣定義裝飾器時,就不能這樣調用:
import json import functools def json_output(indent=None, sort_keys=False): def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner return actual_decorator @json_output def f(): return {"status": "done"}
在實際的項目過程中,有時會出現這樣的狀況: 一開始寫的裝飾器時不需要使用時傳參數的,后來發現有必要傳參數,改好后原來不傳參的裝飾器不能正常使用了,這是修改原來使用的地方是項痛苦的事情。
這時候就需要對裝飾器做一個兼容,使它在以下情況都可用:
@json_output @json_output() @json_output(indent=4)
具體做法如下:
import json import functools def json_output(decorated_=None, indent=None, sort_keys=False): if decorated_ and (indent or sort_keys): raise def actual_decorator(func): @functools.wraps(func) def inner(*args, **kwargs): result = func(*args, **kwargs) return json.dumps(result, indent=indent, sort_keys=sort_keys) return inner if decorated_: return actual_decorator(decorated_) else: return actual_decorator @json_output(indent=4) def f1(): return {"status": "done"} @json_output def f2(): return {"status": "done"} @json_output() def f3(): return {"status": "done"} print f1() print f2() print f3()
代碼中關鍵的地方在于 json_output 在最后對參數 decorated 進行了判斷,有的話證明是不傳參調用,那么直接返回 actual_decorator 的調用;沒有的話則代表是傳參類型的調用(雖然參數可能不存在),那么返回 actual_decorator 。其中有點需要注意, josn_output 的傳參需要使用關鍵字參數,如果像下面這樣直接傳一個位置參數,那么根據現在的實現會出現錯誤(因為它會被當成 decorated_ )。
@json_output(4) #錯誤的使用方法 def f4(): return {"status": "done"}參考資料
《Python高級編程》 by Luke Sneeriger
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/38291.html
摘要:探究多個裝飾器執行順序裝飾器是用于封裝函數或代碼的工具,網上可以搜到很多文章可以學習,我在這里要討論的是多個裝飾器執行順序的一個迷思。這時候你該知道為什么輸出結果會是那樣,以及對裝飾器執行順序實際發生了什么有一定了解了吧。 探究多個裝飾器執行順序 裝飾器是Python用于封裝函數或代碼的工具,網上可以搜到很多文章可以學習,我在這里要討論的是多個裝飾器執行順序的一個迷思。 疑問 大部...
摘要:最近看到一個關于的題文章其中的一個是裝飾器的順序問題就想寫篇博客回顧下裝飾器首先強烈推薦很久之前看的一篇博文翻譯理解中的裝飾器關于什么是裝飾器看這篇文章就好了這里主要想寫關于多個裝飾器的執行流程裝飾順序示例代碼初始化初始化輸出結果初始化初始 最近看到一個關于Flask的CTF(RealWorld CTF 2018 web題bookhub)文章其中的一個trick是裝飾器的順序問題,就想...
摘要:裝飾器的使用符合了面向對象編程的開放封閉原則。三簡單的裝飾器基于上面的函數執行時間的需求,我們就手寫一個簡單的裝飾器進行實現。函數體就是要實現裝飾器的內容。類裝飾器的實現是調用了類里面的函數。類裝飾器的寫法比我們裝飾器函數的寫法更加簡單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...
摘要:一般情況下,我們使用裝飾器提供的語法糖,來簡化上面的寫法像上面的情況,可以動態修改函數或類功能的函數就是裝飾器。本文標題為會打扮的裝飾器本文鏈接為參考資料修飾器的函數式編程中的裝飾器介紹思誠之道裝飾器入門與提高賴明星 裝飾器 我們知道,在 Python 中,我們可以像使用變量一樣使用函數: 函數可以被賦值給其他變量 函數可以被刪除 可以在函數里面再定義函數 函數可以作為參數傳遞給另外...
摘要:初步認識裝飾器函數裝飾器用于在源代碼中標記函數,以某種方式增強函數的行為。函數裝飾器在導入模塊時立即執行,而被裝飾的函數只在明確調用時運行。只有涉及嵌套函數時才有閉包問題。如果想保留函數原本的屬性,可以使用標準庫中的裝飾器。 《流暢的Python》筆記本篇將從最簡單的裝飾器開始,逐漸深入到閉包的概念,然后實現參數化裝飾器,最后介紹標準庫中常用的裝飾器。 1. 初步認識裝飾器 函數裝飾...
閱讀 1405·2021-11-25 09:43
閱讀 2261·2021-09-27 13:36
閱讀 1114·2021-09-04 16:40
閱讀 1957·2019-08-30 11:12
閱讀 3309·2019-08-29 14:14
閱讀 566·2019-08-28 17:56
閱讀 1320·2019-08-26 13:50
閱讀 1246·2019-08-26 13:29