1. 程式人生 > >第一次嘗試翻譯《Python裝飾器 II :裝飾器的引數》

第一次嘗試翻譯《Python裝飾器 II :裝飾器的引數》

第一次嘗試翻譯,由於本人水平有限,歡迎指正。
本文也是簡單 12 步理解 Python 裝飾器推薦繼續閱讀的兩篇文章之一。

原文地址:Python Decorators II: Decorator Arguments
原文標題:
Computing Thoughts
Python Decorators II: Decorator Arguments
by Bruce Eckel
October 19, 2008

以下是翻譯內容


摘要:當有引數傳遞給裝飾器時,裝飾器的執行機制是明顯不同於沒有引數傳遞的時候的


回顧:沒有引數傳遞時的裝飾器執行機制
在第一部分,我展示瞭如何使用沒有引數傳遞情況下的裝飾器,之所以我用先用“類”來定義裝飾器,是因為我覺得比較容易被理解。
如果我們生成一個沒有引數傳遞的裝飾器,那個需要被裝飾的函式就會被傳遞給裝飾器,每次裝飾器被觸發的時候(譯者加一句:就是每次被函式呼叫操作符“()”呼叫的時候),call

()方法都會被呼叫一次。

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 #譯者加註 1 def sayHello(a1, a2, a3, a4): print ('sayHello arguments:'
, a1, a2, a3, a4) print ("After decoration") #譯者加註 2 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")

任何被傳遞給裝飾器的引數都會被指派給__call__(),因此,輸出結果:

Inside __init__()		#譯者加註 1的位置被呼叫,注意,沒有引數傳遞給裝飾器
After decoration		#譯者加註 2的位置被呼叫
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

請注意,在裝飾器被構建的時候(譯者注 1的位置),只有__init__()方法被呼叫,而__call__()方法是每次通過函式呼叫操作符“()”呼叫sayhello函式的時候才被呼叫。


有引數的裝飾器
現在修改上面的例子,看看有引數傳遞的裝飾器的工作機制。

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)	#譯者加註 1
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")

從輸出可以看出明顯的不同。

Inside __init__()	#譯者加註 1 處呼叫執行
Inside __call__()	#譯者加註 1 處呼叫執行
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

裝飾器的呼叫流程發生了明顯的變化,當構建裝飾器的時候,首先呼叫構造器__init__(),然後立即呼叫__call__(),call()方法接收輸入的引數(即被裝飾函式sayHello),然後返回真正的裝飾函式wrapped_f。注意,call()方法只被呼叫這一次,而後,每次對裝飾器的呼叫,實際上都是呼叫那個裝飾函式warpped_f。
雖然看起來很容易理解,但還是要注意不同情況下編寫裝飾器類的區別。

帶引數的裝飾器函式
最後,學習更復雜的裝飾器函式的實現方法,這種實現方法,一次性的定義所有裝飾器所設計的內容。

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"

輸出結果如下:

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

裝飾器的返回值必須是一個“用於包裝被裝飾函式”的函式,也就是說,在每次“裝飾”的時候,python都會呼叫那個“用於包裝被包裝函式”的函式(wrap),並把“被裝飾函式”傳遞給它,這也正是我們有三層函式的原因:最內層的那個函式才是真正的那個“裝飾函式”。
因為python的“閉包”特性,wrap函式能夠訪問到裝飾器的引數arg1、arg2和arg3,而不必像“類裝飾器”中那樣,將引數儲存在類的屬性中。然而,根據“明確要比模糊強”這個概念,雖然函式版本的裝飾器定義更加簡明扼要,但還是類版本的裝飾器更加容易理解、修改及維護。