python黑魔法---裝飾器(decorator)
python 是一門優雅的語言,有些使用方法就像魔法一樣。裝飾器(decorator)就是一種化腐朽性為神奇的技巧。最近一直都在使用 Tornado 框架,一直還是念念不忘 Flask 。Flask 是我最喜歡的 Python 框架,最早被它吸引也是源自它使用裝飾器這個語法糖(Syntactic sugar)來做 Router,讓程式碼看上去就感覺甜甜的。
Tornado 中的 Router 略顯平淡,懷念 Flask 的味道,於是很好奇的想知道 Flask 是如何使用這個魔法。通過閱讀 Flask 的原始碼,我們也可以為 Tornado 實現了一個裝飾器 Router。
當然對於剛接觸 Python 的人,也許很容易理解裝飾器本質是設計模式中的裝飾器模式。可是 Python 通過@
一切都是物件
Python 裡一切都是物件,當然這不代表一切都是女朋友。函式也是物件,因而可以當成引數傳遞,例如:
1234567891011121314 | def say_english():print'hello'def say_chinese():print'你好'say_english()# hellosay_chinese()# 你好def greet(say):say()greet(say_english)# hellogreet(say_chinese)# 你好 |
我們的 greet 函式的引數,也是一個函式物件。可以傳遞這個引數物件。我們呼叫greet的時候,greet 內部進行函式引數的呼叫。
裝飾模式
裝飾模式,顧名思義,就是在呼叫目標函式之前,對這個函式物件進行裝飾。比如一個對資料庫操作的方法,我們在查詢資料之前,需要連線一下資料庫,當查詢結束之後,需要再把連線斷開關閉。正常的邏輯如下:
12345678910111213141516 | def connect_db():print'connect db'def close_db():print'close db'def query_user():connect_db()print'query the user'close_db()query_user()# connect db# query the user# close db |
我們把 連線資料庫(connect_db) 和 關閉連線 (close_db)都封裝成了函式。 query_data 方法執行我們查詢的具體邏輯。這樣需要不同的查詢方法,只需要把查詢的邏輯也封裝成一個方法就Okla
12345678910 | def query_user():print'query some user'def query_data(query):connect_db()query()close_db()query_data(query_user) |
把查詢的函式物件傳進來,符合開篇說的一切都是物件。裝飾器完成啦。對,就這麼簡單,query_data 就是對 query_user 的裝飾,當然你還可以寫出 query_blog 等方法。
等等,設想一種情況,在我們使用裝飾函式之前,專案的程式碼已經有了大量的 query_user方法的呼叫。如果使用了query_data 包裝。我們就不得不把之前 query_user() 的地方統統替換成 query_data(query_user)。怎麼樣才能減少對程式碼的改動呢?
我們的出發點是為了保持之前的 query_user() 不改動,現在實際情況是呼叫 query_data(query_user)。如果 query_data 呼叫的時候,返回一個函式呢?例如下面的程式碼:
1234567891011121314 | def query_user():print'query some user'def query_data(query):""" 定義裝飾器,返回一個函式,對query進行wrapper包裝 """def wrapper():connect_db()query()close_db()returnwrapper# 這裡呼叫query_data進行實際裝飾(注意裝飾是動詞)query_user=query_data(query_user)# 呼叫被裝飾後的函式query_userquery_user() |
這樣一個完整的裝飾器就完成了,比起前面的版本,我們不需要改動之前寫好的 query_user 程式碼。一個關鍵點在於query_data 呼叫的時候,返回了一個 wrapper 函式,而這個wrapper 函式執行 query 函式呼叫前後的一些邏輯。另外一個關鍵就是呼叫裝飾器 query_data 裝飾函式。
語法糖@
前面的程式碼,可以使用 python的裝飾器語法糖@,如下:
1234567891011121314 | def query_data(query):def wrapper():connect_db()query()close_db()returnwrapper# 使用 @ 呼叫裝飾器進行裝飾@query_datadef query_user():print'query some user'query_user() |
前面的 裝飾器 呼叫進行裝飾的時候,python 有一個語法糖。
如果給裝飾器函式前面加一個@
,我們可以理解為呼叫了一些裝飾器函式,即 @query_data
等於 query_data()
。當實際上,並不是這麼使用,而是這麼一個整體:
123 | @query_datadef query_user():print'query some user' |
等價於
1 | query_user=query_data(query_user) |
被裝飾函式引數
我們被裝飾的函式,往往帶有引數,因此通過裝飾器如何傳遞引數呢?回想一下,裝飾器函式針對被裝飾的函式進行裝飾,使用的是返回一個 wrapper 函式。其實這個函式可以等同於被裝飾的函式,只不過 wrapper 還做了更多的事情。被裝飾的函式引數可以通過 wrapper 傳遞。如下:
123456789101112131415 | def query_data(query):def wrapper(count):connect_db()query(count)close_db()returnwrapper@query_datadef query_user(count):print'query some user limit {count}'.format(count=count)query_user(count=100)# connect db# query some user limit 100# close db |
這樣就實現了被裝飾的函式傳遞引數。當然,位置引數和關鍵字引數,可變引數都可以。
裝飾器引數
在 flask 中,對檢視函式的裝飾是裝飾器中傳遞 url 正則,即在裝飾器中傳遞引數,和被裝飾器的引數還不一樣。
123 | @app.router('/user')def user_page():return'user page' |
我們如何定義router這個裝飾器呢?其實只要在原先的裝飾器外面再包裹一層,也就是針對裝飾器進行裝飾。
12345678910111213141516171819202122 | def router(url):print'router invoke url',urldef query_data(query):print'query_data invoke url',urldef wrapper(count):connect_db()query(count)close_db()returnwrapperreturnquery_data@router('/user')# 首先呼叫了router函式, 輸出 router invoke url /user, 進行@裝飾,輸出 'query_data invoke url', urldef query_user(count):print'query some user limit {count}'.format(count=count)query_user(count=100)# connect db# query some user limit 100# close db |
@router() 這個語法糖看上去讓人迷惑,其實也很好理解。這裡可以看成兩個步驟
第一步是呼叫 router 這個函式:
1 | query_data=router('/user') |
第二步則進行裝飾:
123 | @query_datadef query_user():pass |
連起來的效果就是
1 | query_user=router()(query_data(query_user)) |
現在回想,@
這個語法糖很甜吧。並且和python一樣很好理解,也十分常用。
當然,我們使用 裝飾器是為了實現一些需要包裝的方法,例如前面提到的 flask 的 router
Enjoy~
相關推薦
python黑魔法---裝飾器(decorator)
python 是一門優雅的語言,有些使用方法就像魔法一樣。裝飾器(decorator)就是一種化腐朽性為神奇的技巧。最近一直都在使用 Tornado 框架,一直還是念念不忘 Flask 。Flask 是我最喜歡的 Python 框架,最早被它吸引也是源自它使用裝飾器這個語法糖(Syntactic sugar
Python 裝飾器(Decorator)
highlight () 註冊 內部 AI 面積 return 使用 工作 裝飾器的語法為 @dec_name ,置於函數定義之前。如: import atexit @atexit.register def goodbye(): print(‘Goodbye
(轉)python裏為什麽需要使用裝飾器(decorator)
上線 深入 add 指導 對象 引用 是否 寫代碼 學習 出處 為什麽需要使用裝飾器呢?其實很多人學習python之後都會問這個問題。這一次,我來深入地學習一下什麽是裝飾器,以及為什麽需要它。其實這個裝飾器就是我們這樣的程序員太“懶”了,基本上什麽
深入理解Python 裝飾器(decorator)
返璞歸真, 看山還是山 剛看到Python裝飾器時, 覺得很神奇。簡單實驗下,發現也就那麼回事。但是慢慢的看到越來越多的裝飾器。很多時候又不瞭解到底是怎麼回事了。 最後還是決定好好研究下。 先看看一些例項, 然後再來分析下原理 假設我們有如下
Selenium2+python-unittest之裝飾器(@classmethod)
selenium self selenium2 def tex 驗證 drive sts ttr 原文地址:http://www.cnblogs.com/yoyoketang/p/6685416.html 前言 前面講到unittest裏面setUp可以在每次執行用例前執行
設計模式:裝飾器(Decorator)模式
讓我 分享圖片 底部 .com 一件事 輸出 PE 新的 int 設計模式:裝飾器(Decorator)模式 一、前言 裝飾器模式也是一種非常重要的模式,在Java以及程序設計中占據著重要的地位。比如Java的數據流處理,我們可能看到數據流經過不同的類的包裝和包裹,最
python黑魔法——裝飾器
目錄 目錄 一、簡單的例子 二、修飾帶引數和存在返回值的函式 三、帶引數的裝飾器 四、裝飾器呼叫順序 五、python內建的裝飾器
設計模式(7)—— 結構型 —— 裝飾器(Decorator )
介紹 定義:在不改變原有物件基礎之上,將功能附加到物件上 說明:在擴充套件原有物件功能方面,提供了比繼承更有彈性的替代方案 型別:結構型 適用場景: 擴充套件一個類的功能或給一個類新增附加職責 動態地給一個
修飾符@,裝飾器(decorator),迭代器(iterator),生成器(Generator)的一些見解
這三個方法,在Python裡的使用並不多,然而,各大網際網路公司還特別喜歡考這一方面的。以前因為不常用,有些忽視了這幾個方法,碰了幾次壁後,決心好好整理一番這三個方法(以下程式碼均在python3.6的環境下實驗):  
Python黑魔法 --- 非同步IO( asyncio) 協程
python asyncio 網路模型有很多中,為了實現高併發也有很多方案,多執行緒,多程序。無論多執行緒和多程序,IO的排程更多取決於系統,而協程的方式,排程來自使用者,使用者可以在函式中yield一個狀態。使用協程可以實現高效的併發任務。Python的在3.4中引入了協程的概念,可是這個還是以生成器物件
裝飾器(Decorator)
1. 函式也是物件 在python中,函式也是物件,它有自己的方法,它可以傳遞下去。 函式傳遞 >>> def brown(): print 'brown' >>> new_brown = brow
python閉包和裝飾器(轉)
lee type ade 機制 並且 change -1 pri neu 一、python閉包 1、內嵌函數 >>> def func1(): ... print (‘func1 running...‘) ... def func2(
Python不歸路_裝飾器(一)
意思 地址 函數返回值 right 輸出結果 運用 logs pan ext 裝飾器 裝飾器 什麽是裝飾器?給現有函數添加新功能的函數,不更改現有函數源代碼,現有函數調用方式。裝飾器是由高階函數和嵌套函數組成。 概括上面這句話的意思:1、裝飾器 - - - >
python裝飾器(2)
裝飾器 () 方式 cti 調用 st2 rgs 顯示 clas 1.以下代碼,bar作為參數被test2調用。bar的原代碼沒變,但調用方式從bar()變成test2(bar) 不符合裝飾器定義 1 __author__ = "csy" 2 3 def ba
python裝飾器(3)
urn python裝飾器 裝飾 int 裝飾器 func 實現 ret test 另一種實現方式: 1 __author__ = "csy" 2 3 def test2(func): 4 def test1(): 5 func()
Selenium2+python自動化55-unittest之裝飾器(@classmethod)【轉載】
con 關閉 ext 元素 實現 bdr nav expec 裏的 本篇轉自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/unittest/ 前言 前面講到unittest裏面setUp可以在每次執行用例前執行,這樣
Selenium2+python自動化55-unittest之裝飾器(@classmethod)
1.0 ... 區別 就會 pytho 想是 col http -c 前言 前面講到unittest裏面setUp可以在每次執行用例前執行,這樣有效的減少了代碼量,但是有個弊端,比如打開瀏覽器操作,每次執行用例時候都會重新打開,這樣就會浪費很多時間。 於是就想是不是可以只打
python之內置裝飾器(property/staticmethod/classmethod)
類方法 nbsp 編譯器 簡單 用法 是把 test 打印 brush python內置了property、staticmethod、classmethod三個裝飾器,有時候我們也會用到,這裏簡單說明下 1、property 作用:顧名思義把函數裝飾成屬性 一般我們
python高階語法-裝飾器(語法糖)
1.高階函式 在python語言中,引數含有函式名的函式稱之為高階函式。(此時呼叫的函式不加引數,也就是說沒有括號緊跟) 2.裝飾器是什麼 在不改變原始碼的基礎上擴充套件函式需要的新需求,這就是裝飾器。(不改變函式原始碼,這也是裝飾器最基本的原則) 裝飾器,它本身也就
詳解python裝飾器(二)
上一篇文章介紹了裝飾器的基本概念和語法,並且實現了一個簡單的裝飾器。但這些僅僅是學習裝飾器的第一步,本文介紹如何實現一個更好的裝飾器。 保留函式屬性 上一篇文章已經提到在python中函式也是一個物件,並且使用了它的__name__屬性。事實上,python中的