Python裝飾器的另類用法

原文出處: cicaday

之前有比較系統(tǒng)介紹過Python的裝飾器(請查閱《詳解Python裝飾器》)镊逝,本文算是一個補充物臂。今天我們一起探討一下裝飾器的另類用法旺拉。

語法回顧

開始之前我們再將Python裝飾器的語法回顧一下。

@ decorate
def f(...):
    pass

等同于:

def f(...):
    pass
 
f = decorate(f)

@語法的好處在于:

  • 相同的函數(shù)名只出現(xiàn)一次棵磷,避免了f = decorate(f)這樣的語句蛾狗。
  • 可讀性更高,讓讀代碼的人一眼就明白函數(shù)被裝飾了哪些功能仪媒。

@call()裝飾器

假設(shè)你要創(chuàng)建一個整數(shù)平方的列表沉桌,你可以這樣寫:

>>> table = [0, 1, 4, 9, 16]
>>> len(table), table[3]
(5, 9)

也可以使用列表表達式,因為我們要實現(xiàn)比較簡單算吩。

>>> table = [i * i for i in range(5)]
>>> len(table), table[3]
(5, 9)

但是假如這個列表的邏輯比較復雜的時候留凭,最好是寫成一個方法,這樣會更好維護偎巢。

>>> def table(n):
...     value = []
...     for i in range(n):
...         value.append(i*i)
...     return value
>>> table = table(5)

注意看最后一句蔼夜,是不是很符合裝飾器的語法規(guī)則?什么情況下你會寫這樣的代碼呢压昼?

  • 你需要把相對復雜業(yè)務寫成一個方法求冷。
  • 這個方法和返回值可以同名瘤运,而且你不希望對外公開此方法,只公開結(jié)果匠题。
  • 你想盡量使用裝飾器拯坟。(無厘頭的理由)

那么這時候@call()裝飾器就登場了。

def call(*args, **kwargs):
    def call_fn(fn):
        return fn(*args, **kwargs)
    return call_fn

這個裝飾器會把你傳入的參數(shù)送給目標函數(shù)然后直接執(zhí)行韭山。

@call(5)
def table(n):
    value = []
    for i in range(n):
        value.append(i*i)
    return value
 
print len(table), table[3]  # 5 9

@call()裝飾器適用于任何函數(shù)郁季,你傳入的參數(shù)會被直接使用然后結(jié)果賦值給同名函數(shù)。這樣避免了你重新定義一個變量來存儲結(jié)果钱磅。

@list 裝飾器

假如你有一個這樣一個生成器函數(shù)梦裂。

def table(n):
    for i in range(n):
        yield i

當你要生成n=5的序列時,可以直接調(diào)用续搀。

table = table(5)
print table  # <generator object table at 0x027DAC10>

使用上節(jié)提到的@call()裝飾器塞琼,也能得到一樣的結(jié)果。

@call(5)
def table(n):
    for i in range(n):
        yield i
 
print table # <generator object table at 0x0340AC10>

你還可以直接將其轉(zhuǎn)換成列表禁舷。(使用list(generator_object)函數(shù))

<a >@list</a>
@call(5)
def table(n):
    for i in range(n):
        yield i
 
print table  # [0, 1, 2, 3, 4]

相信不少同學第一次看到這個用法應該是懵逼的彪杉。這等同于列表表達式,但是可讀性也許差了不少牵咙。例子本身只是演示了裝飾器的一種用法派近,但不是推薦你就這樣使用裝飾器。你這樣用也許會被其他同事拖到墻角里打死洁桌。

類裝飾器

在Python 2.6以前渴丸,還不支持類裝飾器。也就是說另凌,你不能使用這樣的寫法谱轨。

@ decorator
class MyClass(object):
    pass

你必須這樣寫:

class MyClass(object):
    pass
 
MyClass = decorator(MyClass)

也就是說,@語法對類是做了特殊處理的吠谢,類不一定是一個callable對象(盡管它有構(gòu)造函數(shù))土童,但是也允許使用裝飾器。那么基于以上語法工坊,你覺得類裝飾器能實現(xiàn)什么功能呢献汗?

舉一個例子,ptest中的@TestClass()用于聲明一個測試類王污,其源代碼大致如此罢吃。

def TestClass(enabled=True, run_mode="singleline"):
    def tracer(cls):
        cls.__pd_type__ ='test'
        cls.__enabled__ = enabled
        cls.__run_mode__ = run_mode.lower()
        return cls
    return tracer

當我們在寫一個測試類時,發(fā)生了什么昭齐?

@TestClass()
class TestCases(object):
    # your test case ...
 
print TestCases.__dict__  # {'__module__': '__main__', '__enabled__': True, '__pd_type__': 'test', '__run_mode__': 'singleline', ...}

居然裝飾器的參數(shù)全都變成了變成這個類的屬性尿招,好神奇!我們把語法糖一一展開。

class TestCases(object):
    pass
 
decorator = TestClass()
print decorator  # <function tracer at 0x033128F0>
 
TestCases = decorator(TestCases)
print TestCases  # <class '__main__.TestCases'>
 
print TestCases.__dict__  # {'__module__': '__main__', '__enabled__': True, '__pd_type__': 'test', '__run_mode__': 'singleline', ...}

當裝飾器在被使用時泊业,TestClass()函數(shù)會馬上被執(zhí)行并返回一個裝飾器函數(shù)把沼,這個函數(shù)是一個閉包函數(shù),保存了enabled和run_mode兩個變量吁伺。另外它還接受一個類作為參數(shù),并使用之前保存的變量為這個類添加屬性租谈,最后返回篮奄。所以經(jīng)過@TestClass()裝飾過的類都會帶上__enabled____pd_type__以及__run_mode__的屬性割去。

由此可見窟却,類裝飾器可以完成和Java類似的注解功能,而且要比注解強大的多呻逆。

后記

裝飾器就是一個語法糖夸赫,當你看不懂一個裝飾器時,可以考慮將其依次展開咖城,分別帶入茬腿。這個語法糖給了我們不少方便,但是也要慎用宜雀。畢竟可維護的代碼才是高質(zhì)量的代碼切平。


PyChina將聯(lián)合JetBrain(出品PyCharm的公司)一起在北京舉辦一次Python沙龍活動。

時間:11月26日晚上19:00-21:00

地點:科技寺北新橋 北京市東城區(qū)東四北大街107號科林大廈B座107室(近北新橋地鐵站)

歡迎大家報名參加本次活動辐董,特別需要志愿者來幫忙組織本次活動悴品。

詳情請點擊此處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市简烘,隨后出現(xiàn)的幾起案子苔严,更是在濱河造成了極大的恐慌,老刑警劉巖孤澎,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件届氢,死亡現(xiàn)場離奇詭異,居然都是意外死亡亥至,警方通過查閱死者的電腦和手機悼沈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姐扮,“玉大人絮供,你說我怎么就攤上這事〔杳簦” “怎么了壤靶?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惊搏。 經(jīng)常有香客問我贮乳,道長忧换,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任向拆,我火速辦了婚禮亚茬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浓恳。我一直安慰自己刹缝,他們只是感情好,可當我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布颈将。 她就那樣靜靜地躺著梢夯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晴圾。 梳的紋絲不亂的頭發(fā)上颂砸,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機與錄音死姚,去河邊找鬼人乓。 笑死,一個胖子當著我的面吹牛知允,可吹牛的內(nèi)容都是我干的撒蟀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼温鸽,長吁一口氣:“原來是場噩夢啊……” “哼保屯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涤垫,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤姑尺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蝠猬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體切蟋,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年榆芦,在試婚紗的時候發(fā)現(xiàn)自己被綠了柄粹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡匆绣,死狀恐怖驻右,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情崎淳,我是刑警寧澤堪夭,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響森爽,放射性物質(zhì)發(fā)生泄漏恨豁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一爬迟、第九天 我趴在偏房一處隱蔽的房頂上張望橘蜜。 院中可真熱鬧,春花似錦雕旨、人聲如沸扮匠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疹蛉,卻和暖如春活箕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背可款。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工育韩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闺鲸。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓筋讨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親摸恍。 傳聞我的和親對象是個殘疾皇子悉罕,可洞房花燭夜當晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容