python-裝飾器

前言

行為:裝飾器(decorator)可以對一個函數(shù)焦辅、方法或者類進行“加工”,相當(dāng)于在封裝婚惫。
目的:抽象化代碼氛赐,利用函數(shù)是一等公民的特性來復(fù)用代碼,人生苦短先舷,趕緊偷懶艰管。
須知:越高抽象速度越慢,畢竟函數(shù)的跳轉(zhuǎn)也需要時間蒋川。


本質(zhì)和語法糖

其本質(zhì)是利用函數(shù)作為 python 中的一等公民的特性牲芋,可以作為變量來使用,這時作為變量時其實傳遞的是其函數(shù)的引用(地址)。

也就是說缸浦,只要是將函數(shù)當(dāng)成一等公民的編程語言夕冲,其實都可以寫裝飾器。

裝飾器:

def deco(func):
    def __funny():
        print("2")
        func() # 核心
        print("3333")
        return None # 注意返回 None
    return __funny

def foo():
    print("foo")

f = deco(foo) # 核心
n = foo()
print(n)
image.png

很直白的裂逐,首先將 foo 的引用傳遞給 deco 函數(shù)歹鱼,然后 deco 函數(shù)將會返回一個 __funny() 的引用,所以最后 f 其實獲取了 “__funny() 的引用”卜高,也就是說 f() 就是 __funny()弥姻,在之后 n = f() 的調(diào)用將會得到 f() 的返回值,這里我返回了一個 None 值掺涛。

利用語法糖 @:

def deco(func):
    def __funny():
        print("2")
        func() # 核心
        print("3333")
        return None # 注意返回 None
    return __funny

@deco
def foo():
    print("foo")

n = f()
print(n)
image.png

語法糖其實就是進行了 f = deco(foo) 的操作庭敦,一句話就是懶。

注意裝飾器內(nèi)部的 self 用于指明這是哪一個類的實例薪缆,所以j即使寫在類外部也不能省略秧廉,因為實際運行會被暴露出來啦。


與默認(rèn)參數(shù)和關(guān)鍵字參數(shù)的友好會晤

與默認(rèn)參數(shù)和關(guān)鍵參數(shù)的友好結(jié)合拣帽,將會大大的提高靈活性疼电,能夠變得更加的懶惰。

def deco(func):
    def inner(self, *argv, **kwargv):
        print("2")
        r = func(self, *argv, **kwargv)
        print("3333")
        return None
    return inner
class  Something():
    @deco
    def foo(self):
        print("foo")

a = Something()
print(a.foo())

image.png

來實際抽象一波

我曾經(jīng)封裝過一些 python 中的 SQL 方法诞外,其中有一些非常無聊的操作:

import MySQLdb

class MySqlSearch():
    def __init__(self):
        pass

    def get_conn(self):
        self.conn = MySQLdb.connect(
            # ...
        )

    def conn_close(self):
        if self.conn:
            self.conn.close()

    def get_one(self, order='id'):
        self.get_conn()
        # ...
        self.conn_close()
        return result

    def get_all(self, order='id'):
        self.get_conn()
        # ...
        self.conn_close()
        return result

    def get_by_page(self, page=1, page_size=10, order='id'):
        '''根據(jù)頁面顯示數(shù)據(jù),默認(rèn)第一頁起算灾票,一頁有十行數(shù)據(jù)'''
        self.get_conn()
        # ...
        self.conn_close()
        return result


def main():
    obj = MySqlSearch()
    print(obj.get_one())
    print('-'*50)
    print(obj.get_all())
    print('-'*50)
    print(obj.get_by_page())


if __name__ == '__main__':
    main()

啊峡谊,十分明顯的,為了不長時間占用與數(shù)據(jù)庫的鏈接刊苍,每次我都需要開關(guān)數(shù)據(jù)庫的鏈接既们,太麻煩了。

很明顯正什,可以將開關(guān)數(shù)據(jù)庫操作給封裝掉:

import MySQLdb

def mysql_open_close_decorator(func):
    def __foo(self):
        self.conn = MySQLdb.connect(
                    # ...
        )
        result = func(self) # 實際操作
        if self.conn:
            self.conn.close()
        return result
    return __foo

class MySqlSearch():
    def __init__(self):
        pass

    @mysql_open_close_decorator
    def get_one(self, order='id'):
        # ...
        return result

    @mysql_open_close_decorator
    def get_all(self, order='id'):
        # ...
        return result

    @mysql_open_close_decorator
    def get_by_page(self, page=1, page_size=10, order='id'):
        '''根據(jù)頁面顯示數(shù)據(jù)啥纸,默認(rèn)第一頁起算,一頁有十行數(shù)據(jù)'''
        # ...
        return result


def main():
    obj = MySqlSearch()
    print(obj.get_one())
    print('-'*50)
    print(obj.get_all())
    print('-'*50)
    print(obj.get_by_page())


if __name__ == '__main__':
    main()

保留被裝飾函數(shù)的元信息

問題:假設(shè)你寫了裝飾器來裝飾一個函數(shù)婴氮,而我們運行時其實運行的是裝飾器并在其中調(diào)用被裝飾的函數(shù)斯棒,所以被裝飾函數(shù)不是被直接調(diào)用的,這樣一來重要的元信息比如函數(shù)名稱主经、文檔字符串荣暮、注解和參數(shù)簽名等等信息都會不會被保留,此時我們能看到只有直接調(diào)用的裝飾器的元信息罩驻。

def deco(func):
     def __funny():
         '''裝飾器的文檔字符串'''
         print("2")
         func()  # 核心
         print("3333")
         return None  # 注意返回 None
     return __funny

@deco
def foo():
    '''被裝飾函數(shù)的文檔字符串'''
    print("foo")
image.png

裝飾一個函數(shù)穗酥,核心在于這個函數(shù)而不是裝飾器,所以我們更希望我們裝飾過的函數(shù)能夠保留所有的原始信息,可以自己寫砾跃,但更推薦使用 functools 庫提供的 @wraps 裝飾器骏啰。

from functools import wraps
def deco(func):
     @wraps(func) # @wraps 裝飾器,注意傳入被裝飾函數(shù)來保留其元信息
     def __funny():
         '''裝飾器的文檔字符串 '''
         print("2")
         func()  # 核心
         print("3333")
         return None  # 注意返回 None
     return __funny

@deco
def foo():
    '''被裝飾函數(shù)的文檔字符串'''
    print("foo")

image.png

@wraps 有一個重要特點是它能讓你通過屬性 wrapped 來直接訪問被包裝函數(shù)抽高,比如上圖中的 foo.wrapped()判耕。

functools 所提供的 wraps 作用于裝飾器,保留被裝飾函數(shù)的元信息和提供一份裝飾器的代碼副本厨内。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祈秕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雏胃,更是在濱河造成了極大的恐慌请毛,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞭亮,死亡現(xiàn)場離奇詭異方仿,居然都是意外死亡,警方通過查閱死者的電腦和手機统翩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門仙蚜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厂汗,你說我怎么就攤上這事委粉。” “怎么了娶桦?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵贾节,是天一觀的道長。 經(jīng)常有香客問我衷畦,道長栗涂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任祈争,我火速辦了婚禮斤程,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菩混。我一直安慰自己忿墅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布沮峡。 她就那樣靜靜地躺著球匕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帖烘。 梳的紋絲不亂的頭發(fā)上亮曹,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天橄杨,我揣著相機與錄音,去河邊找鬼照卦。 笑死式矫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的役耕。 我是一名探鬼主播采转,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瞬痘!你這毒婦竟也來了故慈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤框全,失蹤者是張志新(化名)和其女友劉穎察绷,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體津辩,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡拆撼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了喘沿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闸度。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蚜印,靈堂內(nèi)的尸體忽然破棺而出莺禁,到底是詐尸還是另有隱情,我是刑警寧澤窄赋,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布哟冬,位于F島的核電站,受9級特大地震影響寝凌,放射性物質(zhì)發(fā)生泄漏柒傻。R本人自食惡果不足惜孝赫,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一较木、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧青柄,春花似錦伐债、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至双戳,卻和暖如春虹蒋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工魄衅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留峭竣,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓晃虫,卻偏偏與公主長得像皆撩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哲银,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359