1. 程式人生 > >python黑魔法---裝飾器(decorator)

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中的