如何理解 Python 裝飾器
裝飾器(decorator)是一種高級(jí)Python語(yǔ)法。可以對(duì)一個(gè)函數(shù)、方法或者類進(jìn)行加工。在Python中,我們有多種方法對(duì)函數(shù)和類進(jìn)行加工,相對(duì)于其它方式,裝飾器語(yǔ)法簡(jiǎn)單,代碼可讀性高。因此,裝飾器在Python項(xiàng)目中有廣泛的應(yīng)用。修飾器經(jīng)常被用于有切面需求的場(chǎng)景,較為經(jīng)典的有插入日志、性能測(cè)試、事務(wù)處理, Web權(quán)限校驗(yàn), Cache等。

裝飾器的優(yōu)點(diǎn)是能夠抽離出大量函數(shù)中與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。即,可以將函數(shù)“修飾”為完全不同的行為,可以有效的將業(yè)務(wù)邏輯正交分解。概括的講,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。例如記錄日志,需要對(duì)某些函數(shù)進(jìn)行記錄。笨的辦法,每個(gè)函數(shù)加入代碼,如果代碼變了,就悲催了。裝飾器的辦法,定義一個(gè)專門(mén)日志記錄的裝飾器,對(duì)需要的函數(shù)進(jìn)行裝飾。

Python 的 Decorator在使用上和Java/C#的Annotation很相似,都是在方法名前面加一個(gè)@XXX注解來(lái)為這個(gè)方法裝飾一些東西。但是,Java/C#的Annotation也很讓人望而卻步,在使用它之前你需要了解一堆Annotation的類庫(kù)文檔,讓人感覺(jué)就是在學(xué)另外一門(mén)語(yǔ)言。而Python使用了一種相對(duì)于Decorator Pattern和Annotation來(lái)說(shuō)非常優(yōu)雅的方法,這種方法不需要你去掌握什么復(fù)雜的OO模型或是Annotation的各種類庫(kù)規(guī)定,完全就是語(yǔ)言層面的玩法:一種函數(shù)式編程的技巧。
裝飾器背后的原理
在Python中,裝飾器實(shí)現(xiàn)是十分方便。原因是:函數(shù)可以被扔來(lái)扔去。
Python的函數(shù)就是對(duì)象
要理解裝飾器,就必須先知道,在Python里,函數(shù)也是對(duì)象(functions are objects)。明白這一點(diǎn)非常重要,讓我們通過(guò)一個(gè)例子來(lái)看看為什么。
def?shout(word="yes"):
????**return**?word.capitalize()?+?"!"
print(shout())
\#?outputs?:?'Yes!'
\#?作為一個(gè)對(duì)象,你可以像其他對(duì)象一樣把函數(shù)賦值給其他變量
scream?=?shout
\#?注意我們沒(méi)有用括號(hào):我們不是在調(diào)用函數(shù),
\#?而是把函數(shù)'shout'的值綁定到'scream'這個(gè)變量上
\#?這也意味著你可以通過(guò)'scream'這個(gè)變量來(lái)調(diào)用'shout'函數(shù)
print(scream())
\#?outputs?:?'Yes!'
\#?不僅如此,這也還意味著你可以把原來(lái)的名字'shout'刪掉,
\#?而這個(gè)函數(shù)仍然可以通過(guò)'scream'來(lái)訪問(wèn)
del?shout
**try**:
????print(shout())
**except**?NameError?as?e:
????print(e)
????\#?outputs:?"name?'shout'?is?not?defined"
print(scream())
\#?outputs:?'Yes!'
Python 函數(shù)的另一個(gè)有趣的特性是,它們可以在另一個(gè)函數(shù)體內(nèi)定義。
def?talk():
????\#?你可以在?'talk'?里動(dòng)態(tài)的(on?the?fly)定義一個(gè)函數(shù)...
????**def**?whisper(word="yes"):
????????**return**?word.lower()?+?"..."
????\# ... 然后馬上調(diào)用它!
????print(whisper())
\#?每當(dāng)調(diào)用'talk',都會(huì)定義一次'whisper',然后'whisper'在'talk'里被調(diào)用
talk()
\#?outputs:
\#?"yes..."
\#?但是"whisper"?在?"talk"外并不存在:
**try**:
????print(whisper())
**except**?NameError?as?e:
????print(e)
????\#?outputs?:?"name?'whisper'?is?not?defined"
函數(shù)引用(Functions references)
你剛剛已經(jīng)知道了,Python的函數(shù)也是對(duì)象,因此:
可以被賦值給變量 可以在另一個(gè)函數(shù)體內(nèi)定義
那么,這樣就意味著一個(gè)函數(shù)可以返回另一個(gè)函數(shù):
def?get_talk(type="shout"):
????\#?我們先動(dòng)態(tài)定義一些函數(shù)
????**def**?shout(word="yes"):
????????**return**?word.capitalize()?+?"!"
????**def**?whisper(word="yes"):
????????**return**?word.lower()?+?"..."
????\#?然后返回其中一個(gè)
????**if**?type?==?"shout":
????????\#?注意:我們是在返回函數(shù)對(duì)象,而不是調(diào)用函數(shù),所以不要用到括號(hào)?"()"
????????**return**?shout
????**else**:
????????**return**?whisper
\#?那你改如何使用d呢?
\#?先把函數(shù)賦值給一個(gè)變量
talk?=?get_talk()
\#?你可以發(fā)現(xiàn)?"talk"?其實(shí)是一個(gè)函數(shù)對(duì)象:
print(talk)
\#?outputs?:?
\#?這個(gè)對(duì)象就是?get_talk?函數(shù)返回的:
print(talk())
\#?outputs?:?Yes!
\#?你甚至還可以直接這樣使用:
print(get_talk("whisper")())
\#?outputs?:?yes...
既然可以返回一個(gè)函數(shù),那么也就可以像參數(shù)一樣傳遞:
def?shout(word="yes"):
????**return**?word.capitalize()?+?"!"
scream?=?shout
**def**?do_something_before(func):
????print("I?do?something?before?then?I?call?the?function?you?gave?me")
????print(func())
do_something_before(scream)
\#?outputs:
\#?I?do?something?before?then?I?call?the?function?you?gave?me
\#?Yes!
裝飾器實(shí)戰(zhàn)
現(xiàn)在已經(jīng)具備了理解裝飾器的所有基礎(chǔ)知識(shí)了。裝飾器也就是一種包裝材料,它們可以讓你在執(zhí)行被裝飾的函數(shù)之前或之后執(zhí)行其他代碼,而且不需要修改函數(shù)本身。
手工制作的裝飾器
\#?一個(gè)裝飾器是一個(gè)需要另一個(gè)函數(shù)作為參數(shù)的函數(shù)
**def**?my_shiny_new_decorator(a_function_to_decorate):
????\#?在裝飾器內(nèi)部動(dòng)態(tài)定義一個(gè)函數(shù):wrapper(原意:包裝紙).
????\#?這個(gè)函數(shù)將被包裝在原始函數(shù)的四周
????\#?因此就可以在原始函數(shù)之前和之后執(zhí)行一些代碼.
????**def**?the_wrapper_around_the_original_function():
????????\#?把想要在調(diào)用原始函數(shù)前運(yùn)行的代碼放這里
????????print("Before?the?function?runs")
????????\#?調(diào)用原始函數(shù)(需要帶括號(hào))
????????a_function_to_decorate()
????????\#?把想要在調(diào)用原始函數(shù)后運(yùn)行的代碼放這里
????????print("After?the?function?runs")
????\#?直到現(xiàn)在,"a_function_to_decorate"還沒(méi)有執(zhí)行過(guò)?(HAS?NEVER?BEEN?EXECUTED).
????\#?我們把剛剛創(chuàng)建的?wrapper?函數(shù)返回.
????\#?wrapper?函數(shù)包含了這個(gè)函數(shù),還有一些需要提前后之后執(zhí)行的代碼,
????\#?可以直接使用了(It's?ready?to?use!)
????**return**?the_wrapper_around_the_original_function
\#?Now?imagine?you?create?a?function?you?don't?want?to?ever?touch?again.
**def**?a_stand_alone_function():
????print("I?am?a?stand?alone?function,?don't?you?dare?modify?me")
a_stand_alone_function()
\#?outputs:?I?am?a?stand?alone?function,?don't?you?dare?modify?me
\#?現(xiàn)在,你可以裝飾一下來(lái)修改它的行為.
\#?只要簡(jiǎn)單的把它傳遞給裝飾器,后者能用任何你想要的代碼動(dòng)態(tài)的包裝
\#?而且返回一個(gè)可以直接使用的新函數(shù):
a_stand_alone_function_decorated?=?my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
\#?outputs:
\#?Before?the?function?runs
\#?I?am?a?stand?alone?function,?don't?you?dare?modify?me
\#?After?the?function?runs
裝飾器的語(yǔ)法糖
我們用裝飾器的語(yǔ)法來(lái)重寫(xiě)一下前面的例子:
\#?一個(gè)裝飾器是一個(gè)需要另一個(gè)函數(shù)作為參數(shù)的函數(shù)
**def**?my_shiny_new_decorator(a_function_to_decorate):
????\#?在裝飾器內(nèi)部動(dòng)態(tài)定義一個(gè)函數(shù):wrapper(原意:包裝紙).
????\#?這個(gè)函數(shù)將被包裝在原始函數(shù)的四周
????\#?因此就可以在原始函數(shù)之前和之后執(zhí)行一些代碼.
????**def**?the_wrapper_around_the_original_function():
????????\#?把想要在調(diào)用原始函數(shù)前運(yùn)行的代碼放這里
????????print("Before?the?function?runs")
????????\#?調(diào)用原始函數(shù)(需要帶括號(hào))
????????a_function_to_decorate()
????????\#?把想要在調(diào)用原始函數(shù)后運(yùn)行的代碼放這里
????????print("After?the?function?runs")
????\#?直到現(xiàn)在,"a_function_to_decorate"還沒(méi)有執(zhí)行過(guò)?(HAS?NEVER?BEEN?EXECUTED).
????\#?我們把剛剛創(chuàng)建的?wrapper?函數(shù)返回.
????\#?wrapper?函數(shù)包含了這個(gè)函數(shù),還有一些需要提前后之后執(zhí)行的代碼,
????\#?可以直接使用了(It's?ready?to?use!)
????**return**?the_wrapper_around_the_original_function
@my_shiny_new_decorator
**def**?another_stand_alone_function():
????print("Leave?me?alone")
another_stand_alone_function()
\#?outputs:
\#?Before?the?function?runs
\#?Leave?me?alone
\#?After?the?function?runs
是的,這就完了,就這么簡(jiǎn)單。@decorator 只是下面這條語(yǔ)句的簡(jiǎn)寫(xiě)(shortcut):
another_stand_alone_function?=?my_shiny_new_decorator(another_stand_alone_function)
裝飾器語(yǔ)法糖其實(shí)就是裝飾器模式的一個(gè)Python化的變體。為了方便開(kāi)發(fā),Python已經(jīng)內(nèi)置了好幾種經(jīng)典的設(shè)計(jì)模式,比如迭代器(iterators)。當(dāng)然,你還可以堆積使用裝飾器:
def?bread(func):
????**def**?wrapper():
????????print("''''''\>")
????????func()
????????print("<\______/>")
????**return**?wrapper
**def**?ingredients(func):
????**def**?wrapper():
????????print("#tomatoes#")
????????func()
????????print("~salad~")
????**return**?wrapper
**def**?sandwich(food="--ham--"):
????print(food)
sandwich()
\#?outputs:?--ham--
sandwich?=?bread(ingredients(sandwich))
sandwich()
\#?outputs:
\#?''''''\>
\#?#tomatoes#
\#?--ham--
\#?~salad~
\#?<\______/>
用Python的裝飾器語(yǔ)法表示:
def?bread(func):
????**def**?wrapper():
????????print("''''''\>")
????????func()
????????print("<\______/>")
????**return**?wrapper
**def**?ingredients(func):
????**def**?wrapper():
????????print("#tomatoes#")
????????func()
????????print("~salad~")
????**return**?wrapper
@bread
@ingredients
**def**?sandwich(food="--ham--"):
????print(food)
sandwich()
\#?outputs:
\#?''''''\>
\#?#tomatoes#
\#?--ham--
\#?~salad~
\#?<\______/>
裝飾器放置的順序也很重要:
def?bread(func):
????**def**?wrapper():
????????print("''''''\>")
????????func()
????????print("<\______/>")
????**return**?wrapper
**def**?ingredients(func):
????**def**?wrapper():
????????print("#tomatoes#")
????????func()
????????print("~salad~")
????**return**?wrapper
@ingredients
@bread
**def**?strange_sandwich(food="--ham--"):
????print(food)
strange_sandwich()
\#?outputs:
\##tomatoes#
\#?''''''\>
\#?--ham--
\#?<\______/>
\#?~salad~
給裝飾器函數(shù)傳參
\#?這不是什么黑色魔法(black?magic),你只是必須讓wrapper傳遞參數(shù):
**def**?a_decorator_passing_arguments(function_to_decorate):
????**def**?a_wrapper_accepting_arguments(arg1,?arg2):
????????print("I?got?args!?Look:",?arg1,?arg2)
????????function_to_decorate(arg1,?arg2)
????**return**?a_wrapper_accepting_arguments
\#?當(dāng)你調(diào)用裝飾器返回的函數(shù)式,你就在調(diào)用wrapper,而給wrapper的
\#?參數(shù)傳遞將會(huì)讓它把參數(shù)傳遞給要裝飾的函數(shù)
@a_decorator_passing_arguments
**def**?print_full_name(first_name,?last_name):
????print("My?name?is",?first_name,?last_name)
print_full_name("Peter",?"Venkman")
\#?outputs:
\#?I?got?args!?Look:?Peter?Venkman
\#?My?name?is?Peter?Venkman
含參數(shù)的裝飾器
在上面的裝飾器調(diào)用中,比如@decorator,該裝飾器默認(rèn)它后面的函數(shù)是唯一的參數(shù)。裝飾器的語(yǔ)法允許我們調(diào)用decorator時(shí),提供其它參數(shù),比如@decorator(a)。這樣,就為裝飾器的編寫(xiě)和使用提供了更大的靈活性。
\#?a?new?wrapper?layer
**def**?pre_str(pre=''):
????\#?old?decorator
????**def**?decorator(F):
????????**def**?new_F(a,?b):
????????????print(pre?+?"?input",?a,?b)
????????????**return**?F(a,?b)
????????**return**?new_F
????**return**?decorator
\#?get?square?sum
@pre_str('^_^')
**def**?square_sum(a,?b):
????**return**?a?**?2?+?b?**?2
\#?get?square?diff
@pre_str('T_T')
**def**?square_diff(a,?b):
????**return**?a?**?2?-?b?**?2
print(square_sum(3,?4))
print(square_diff(3,?4))
\#?outputs:
\#?('^_^?input',?3,?4)
\#?25
\#?('T_T?input',?3,?4)
\#?-7
上面的pre_str是允許參數(shù)的裝飾器。它實(shí)際上是對(duì)原有裝飾器的一個(gè)函數(shù)封裝,并返回一個(gè)裝飾器。我們可以將它理解為一個(gè)含有環(huán)境參量的閉包。當(dāng)我們使用@pre_str(‘^_^’)調(diào)用的時(shí)候,Python能夠發(fā)現(xiàn)這一層的封裝,并把參數(shù)傳遞到裝飾器的環(huán)境中。該調(diào)用相當(dāng)于:
square_sum?=?pre_str('^_^')?(square_sum)
裝飾“類中的方法”
Python的一個(gè)偉大之處在于:方法和函數(shù)幾乎是一樣的(methods and functions are really the same),除了方法的第一個(gè)參數(shù)應(yīng)該是當(dāng)前對(duì)象的引用(也就是 self)。這也就意味著只要記住把 self 考慮在內(nèi),你就可以用同樣的方法給方法創(chuàng)建裝飾器:
def?method_friendly_decorator(method_to_decorate):
????**def**?wrapper(self,?lie):
????????lie?=?lie?-?3??#?very?friendly,?decrease?age?even?more?:-)
????????**return**?method_to_decorate(self,?lie)
????**return**?wrapper
**class**?Lucy(object):
????**def**?__init__(self):
????????self.age?=?32
????@method_friendly_decorator
????**def**?say_your_age(self,?lie):
????????print("I?am?%s,?what?did?you?think?"?%?(self.age?+?lie))
l?=?Lucy()
l.say_your_age(-3)
\#?outputs:?I?am?26,?what?did?you?think?
當(dāng)然,如果你想編寫(xiě)一個(gè)非常通用的裝飾器,可以用來(lái)裝飾任意函數(shù)和方法,你就可以無(wú)視具體參數(shù)了,直接使用 *args, **kwargs 就行:
def?a_decorator_passing_arbitrary_arguments(function_to_decorate):
????\#?The?wrapper?accepts?any?arguments
????**def**?a_wrapper_accepting_arbitrary_arguments(*args,?**kwargs):
????????print("Do?I?have?args?:")
????????print(args)
????????print(kwargs)
????????\#?Then?you?unpack?the?arguments,?here?*args,?**kwargs
????????\#?If?you?are?not?familiar?with?unpacking,?check:
????????\#?http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
????????function_to_decorate(*args,?**kwargs)
????**return**?a_wrapper_accepting_arbitrary_arguments
@a_decorator_passing_arbitrary_arguments
**def**?function_with_no_argument():
????print("Python?is?cool,?no?argument?here.")
function_with_no_argument()
\#?outputs
\#?Do?I?have?args?:
\#?()
\#?{}
\#?Python?is?cool,?no?argument?here.
@a_decorator_passing_arbitrary_arguments
**def**?function_with_arguments(a,?b,?c):
????print(a,?b,?c)
function_with_arguments(1,?2,?3)
\#?outputs
\#?Do?I?have?args?:
\#?(1,?2,?3)
\#?{}
\#?1?2?3
@a_decorator_passing_arbitrary_arguments
**def**?function_with_named_arguments(a,?b,?c,?platypus="Why?not??"):
????print("Do?%s,?%s?and?%s?like?platypus??%s"?%?(a,?b,?c,?platypus))
function_with_named_arguments("Bill",?"Linus",?"Steve",?platypus="Indeed!")
\#?outputs
\#?Do?I?have?args???:
\#?('Bill',?'Linus',?'Steve')
\#?{'platypus':?'Indeed!'}
\#?Do?Bill,?Linus?and?Steve?like?platypus??Indeed!
**class**?Mary(object):
????**def**?__init__(self):
????????self.age?=?31
????@a_decorator_passing_arbitrary_arguments
????**def**?say_your_age(self,?lie=-3):??#?You?can?now?add?a?default?value
????????print("I?am?%s,?what?did?you?think??"?%?(self.age?+?lie))
m?=?Mary()
m.say_your_age()
\#?outputs
\#?Do?I?have?args?:
\#?(<__main__.Mary?object?at?0xb7d303ac>,)
\#?{}
\#?I?am?28,?what?did?you?think?
裝飾類
在上面的例子中,裝飾器接收一個(gè)函數(shù),并返回一個(gè)函數(shù),從而起到加工函數(shù)的效果。在Python 2.6以后,裝飾器被拓展到類。一個(gè)裝飾器可以接收一個(gè)類,并返回一個(gè)類,從而起到加工類的效果。
def?decorator(aClass):
????**class**?newClass:
????????**def**?__init__(self,?age):
????????????self.total_display?=?0
????????????self.wrapped?=?aClass(age)
????????**def**?display(self):
????????????self.total_display?+=?1
????????????print("total?display",?self.total_display)
????????????self.wrapped.display()
????**return**?newClass
@decorator
**class**?Bird:
????**def**?__init__(self,?age):
????????self.age?=?age
????**def**?display(self):
????????print("My?age?is",?self.age)
eagleLord?=?Bird(5)
**for**?i?**in**?range(3):
????eagleLord.display()
在decorator中,我們返回了一個(gè)新類newClass。在新類中,我們記錄了原來(lái)類生成的對(duì)象(self.wrapped),并附加了新的屬性total_display,用于記錄調(diào)用display的次數(shù)。我們也同時(shí)更改了display方法。通過(guò)修改,我們的Bird類可以顯示調(diào)用display的次數(shù)了。
內(nèi)置裝飾器
Python中有三種我們經(jīng)常會(huì)用到的裝飾器, property、 staticmethod、 classmethod,他們有個(gè)共同點(diǎn),都是作用于類方法之上。
property 裝飾器
property 裝飾器用于類中的函數(shù),使得我們可以像訪問(wèn)屬性一樣來(lái)獲取一個(gè)函數(shù)的返回值。
class?XiaoMing:
????first_name?=?'明'
????last_name?=?'小'
????@property
????**def**?full_name(self):
????????**return**?self.last_name?+?self.first_name
xiaoming?=?XiaoMing()
print(xiaoming.full_name)
例子中我們像獲取屬性一樣獲取 full_name 方法的返回值,這就是用 property 裝飾器的意義,既能像屬性一樣獲取值,又可以在獲取值的時(shí)候做一些操作。
staticmethod 裝飾器
staticmethod 裝飾器同樣是用于類中的方法,這表示這個(gè)方法將會(huì)是一個(gè)靜態(tài)方法,意味著該方法可以直接被調(diào)用無(wú)需實(shí)例化,但同樣意味著它沒(méi)有 self 參數(shù),也無(wú)法訪問(wèn)實(shí)例化后的對(duì)象。
class?XiaoMing:
????@staticmethod
????**def**?say_hello():
????????print('同學(xué)你好')
XiaoMing.say_hello()
\#?實(shí)例化調(diào)用也是同樣的效果
\#?有點(diǎn)多此一舉
xiaoming?=?XiaoMing()
xiaoming.say_hello()
classmethod 裝飾器
classmethod 依舊是用于類中的方法,這表示這個(gè)方法將會(huì)是一個(gè)類方法,意味著該方法可以直接被調(diào)用無(wú)需實(shí)例化,但同樣意味著它沒(méi)有 self 參數(shù),也無(wú)法訪問(wèn)實(shí)例化后的對(duì)象。相對(duì)于 staticmethod 的區(qū)別在于它會(huì)接收一個(gè)指向類本身的 cls 參數(shù)。
class?XiaoMing:
????name?=?'小明'
????@classmethod
????**def**?say_hello(cls):
????????print('同學(xué)你好,?我是'?+?cls.name)
????????print(cls)
XiaoMing.say_hello()
wraps 裝飾器
一個(gè)函數(shù)不止有他的執(zhí)行語(yǔ)句,還有著 name(函數(shù)名),doc (說(shuō)明文檔)等屬性,我們之前的例子會(huì)導(dǎo)致這些屬性改變。
def?decorator(func):
????**def**?wrapper(*args,?**kwargs):
????????"""doc?of?wrapper"""
????????print('123')
????????**return**?func(*args,?**kwargs)
????**return**?wrapper
@decorator
**def**?say_hello():
????"""doc?of?say?hello"""
????print('同學(xué)你好')
print(say_hello.__name__)
print(say_hello.__doc__)
由于裝飾器返回了 wrapper 函數(shù)替換掉了之前的 say_hello 函數(shù),導(dǎo)致函數(shù)名,幫助文檔變成了 wrapper 函數(shù)的了。解決這一問(wèn)題的辦法是通過(guò) functools 模塊下的 wraps 裝飾器。
from?functools?import?wraps
**def**?decorator(func):
????@wraps(func)
????**def**?wrapper(*args,?**kwargs):
????????"""doc?of?wrapper"""
????????print('123')
????????**return**?func(*args,?**kwargs)
????**return**?wrapper
@decorator
**def**?say_hello():
????"""doc?of?say?hello"""
????print('同學(xué)你好')
print(say_hello.__name__)
print(say_hello.__doc__)
裝飾器總結(jié)
裝飾器的核心作用是name binding。這種語(yǔ)法是Python多編程范式的又一個(gè)體現(xiàn)。大部分Python用戶都不怎么需要定義裝飾器,但有可能會(huì)使用裝飾器。鑒于裝飾器在Python項(xiàng)目中的廣泛使用,了解這一語(yǔ)法是非常有益的。
常見(jiàn)錯(cuò)誤:“裝飾器”=“裝飾器模式”
設(shè)計(jì)模式是一個(gè)在計(jì)算機(jī)世界里鼎鼎大名的詞。假如你是一名 Java 程序員,而你一點(diǎn)設(shè)計(jì)模式都不懂,那么我打賭你找工作的面試過(guò)程一定會(huì)度過(guò)的相當(dāng)艱難。
但寫(xiě) Python 時(shí),我們極少談起“設(shè)計(jì)模式”。雖然 Python 也是一門(mén)支持面向?qū)ο蟮木幊陶Z(yǔ)言,但它的鴨子類型設(shè)計(jì)以及出色的動(dòng)態(tài)特性決定了,大部分設(shè)計(jì)模式對(duì)我們來(lái)說(shuō)并不是必需品。所以,很多 Python 程序員在工作很長(zhǎng)一段時(shí)間后,可能并沒(méi)有真正應(yīng)用過(guò)幾種設(shè)計(jì)模式。
不過(guò)裝飾器模式(Decorator Pattern)是個(gè)例外。因?yàn)?Python 的“裝飾器”和“裝飾器模式”有著一模一樣的名字,我不止一次聽(tīng)到有人把它們倆當(dāng)成一回事,認(rèn)為使用“裝飾器”就是在實(shí)踐“裝飾器模式”。但事實(shí)上,它們是兩個(gè)完全不同的東西。
“裝飾器模式”是一個(gè)完全基于“面向?qū)ο蟆毖苌龅木幊淌址āK鼡碛袔讉€(gè)關(guān)鍵組成:一個(gè)統(tǒng)一的接口定義、若干個(gè)遵循該接口的類、類與類之間一層一層的包裝。最終由它們共同形成一種“裝飾”的效果。
而 Python 里的“裝飾器”和“面向?qū)ο蟆睕](méi)有任何直接聯(lián)系,**它完全可以只是發(fā)生在函數(shù)和函數(shù)間的把戲。事實(shí)上,“裝飾器”并沒(méi)有提供某種無(wú)法替代的功能,它僅僅就是一顆“語(yǔ)法糖”而已。下面這段使用了裝飾器的代碼:
@log_time
@cache_result
**def**?foo():?pass
基本完全等同于:
def?foo():?pass
foo?=?log_time(cache_result(foo))
裝飾器最大的功勞,在于讓我們?cè)谀承┨囟▓?chǎng)景時(shí),可以寫(xiě)出更符合直覺(jué)、易于閱讀的代碼。它只是一顆“糖”,并不是某個(gè)面向?qū)ο箢I(lǐng)域的復(fù)雜編程模式。
參考鏈接:
Primer on Python Decorators [Decorator Basics:Python’s functions are objects]
作者:錢魏Way
https://baidubiaodianfu.com/python-decorator.html
- EOF -
回復(fù)關(guān)鍵字“簡(jiǎn)明python ”,立即獲取入門(mén)必備書(shū)籍《簡(jiǎn)明python教程》電子版
回復(fù)關(guān)鍵字“爬蟲(chóng)”,立即獲取爬蟲(chóng)學(xué)習(xí)資料

推薦
