[譯] Python裝飾器Part II:裝飾器參數(shù)

這是Python裝飾器講解的第二部分,上一篇:Python裝飾器Part I:裝飾器簡介

回顧:不帶參數(shù)的裝飾器

Python裝飾器Part I:裝飾器簡介中,我演示了怎么樣使用無參數(shù)的裝飾器,主要是使用類式裝飾器,因?yàn)檫@樣更容易理解。
如果我們創(chuàng)建了一個(gè)不帶參數(shù)的裝飾器,被裝飾的方法會(huì)傳遞給裝飾器的構(gòu)造器,然后在被裝飾的函數(shù)被調(diào)用的時(shí)候,裝飾器的__call__()方法就會(huì)執(zhí)行。

class decoratorWithoutArguments(object):

    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print "Inside __init__()"
        self.f = f

    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print "Inside __call__()"
        self.f(*args)
        print "After self.f(*args)"

@decoratorWithoutArguments
def sayHello(a1, a2, a3, a4):
    print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"
sayHello("say", "hello", "argument", "list")
print "After first sayHello() call"
sayHello("a", "different", "set of", "arguments")
print "After second sayHello() call"

任何傳遞給被裝飾方法的參數(shù)都將傳遞給__call__(),輸出日志是:

Inside __init__()
After decoration
Preparing to call sayHello()
Inside __call__()
sayHello arguments: say hello argument list
After self.f(*args)
After first sayHello() call
Inside __call__()
sayHello arguments: a different set of arguments
After self.f(*args)
After second sayHello() call

需要注意的是,在“裝飾”階段,只有__init__()會(huì)被調(diào)用;同時(shí)只有在被裝飾方法被調(diào)用的時(shí)候,__call__()才會(huì)被調(diào)用。

帶參數(shù)的裝飾器

現(xiàn)在我們把上面的那個(gè)例子簡單的改動(dòng)一下,看看在添加裝飾器參數(shù)的情況下會(huì)發(fā)生什么情況:

class decoratorWithArguments(object):

    def __init__(self, arg1, arg2, arg3):
        """
        If there are decorator arguments, the function
        to be decorated is not passed to the constructor!
        """
        print "Inside __init__()"
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

    def __call__(self, f):
        """
        If there are decorator arguments, __call__() is only called
        once, as part of the decoration process! You can only give
        it a single argument, which is the function object.
        """
        print "Inside __call__()"
        def wrapped_f(*args):
            print "Inside wrapped_f()"
            print "Decorator arguments:", self.arg1, self.arg2, self.arg3
            f(*args)
            print "After f(*args)"
        return wrapped_f

@decoratorWithArguments("hello", "world", 42)
def sayHello(a1, a2, a3, a4):
    print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"
sayHello("say", "hello", "argument", "list")
print "after first sayHello() call"
sayHello("a", "different", "set of", "arguments")
print "after second sayHello() call"

從輸出結(jié)果來看,運(yùn)行的效果發(fā)生了明顯的變化:

Inside __init__()
Inside __call__()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call

現(xiàn)在,在“裝飾”階段,構(gòu)造器和__call__()都會(huì)被依次調(diào)用,__call__()也只接受一個(gè)函數(shù)對象類型的參數(shù),而且必須返回一個(gè)裝飾方法去替換原有的方法,__call__()只會(huì)在“裝飾”階段被調(diào)用一次,接著返回的裝飾方法會(huì)被實(shí)際用在調(diào)用過程中。
盡管這個(gè)行為很合理,構(gòu)造器現(xiàn)在被用來捕捉裝飾器的參數(shù),而且__call__()不能再被當(dāng)做裝飾方法,相反要利用它來完成裝飾的過程。盡管如此,第一次見到這種與不帶參數(shù)的裝飾器迥然不同的行為還是會(huì)讓人大吃一驚,而且它們的編程范式也有很大的不同。

帶參數(shù)的函數(shù)式裝飾器

最后,讓我們看一下更復(fù)雜的函數(shù)式裝飾器,在這里你不得不一次完成所有的事情:

def decoratorFunctionWithArguments(arg1, arg2, arg3):
    def wrap(f):
        print "Inside wrap()"
        def wrapped_f(*args):
            print "Inside wrapped_f()"
            print "Decorator arguments:", arg1, arg2, arg3
            f(*args)
            print "After f(*args)"
        return wrapped_f
    return wrap

@decoratorFunctionWithArguments("hello", "world", 42)
def sayHello(a1, a2, a3, a4):
    print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"
sayHello("say", "hello", "argument", "list")
print "after first sayHello() call"
sayHello("a", "different", "set of", "arguments")
print "after second sayHello() call"

輸出結(jié)果:

Inside wrap()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call

函數(shù)式裝飾器的返回值必須是一個(gè)函數(shù),能包裝原有被包裝函數(shù)。也就是說,Python會(huì)在裝飾發(fā)生的時(shí)候拿到并且調(diào)用這個(gè)返回的函數(shù)結(jié)果,然后傳遞給被裝飾的函數(shù),這就是為什么我們在裝飾器的實(shí)現(xiàn)里嵌套定義了三層的函數(shù),最里層的那個(gè)函數(shù)是新的替換函數(shù)。
因?yàn)?code>閉包的特性, wrapped_f()在不需要像在類式裝飾器例子中一樣顯示存儲(chǔ)arg1, arg2, arg3這些值的情況下,就能夠訪問這些參數(shù)。不過,這恰巧是我覺得“顯式比隱式更好”的例子。盡管函數(shù)式裝飾器可能更加精簡一點(diǎn),但類式裝飾器會(huì)更加容易理解并因此更容易被修改和維護(hù)。

原文地址:Python Decorators II: Decorator Arguments

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 呵呵!作為一名教python的老師,我發(fā)現(xiàn)學(xué)生們基本上一開始很難搞定python的裝飾器,也許因?yàn)檠b飾器確實(shí)很難懂...
    TypingQuietly閱讀 20,301評論 26 186
  • 假以時(shí)日,我相信裝飾器一定會(huì)成為Python這門編程語言一個(gè)更加強(qiáng)大的功能。到目前為止,我覺得到我所看到的有關(guān)介紹...
    TypingQuietly閱讀 5,545評論 2 70
  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫完項(xiàng)目接著寫寫一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,786評論 1 118
  • 要點(diǎn): 函數(shù)式編程:注意不是“函數(shù)編程”,多了一個(gè)“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍?、屬性?..
    victorsungo閱讀 1,694評論 0 6
  • 通常我們表達(dá)謝意,不會(huì)說謝謝你,我們覺得那樣太見外,太生疏,不符合小城市樸實(shí)和豪爽的基調(diào)。情感的表達(dá),在一飯間,在...
    言心有話說閱讀 551評論 0 3

友情鏈接更多精彩內(nèi)容