decorators 参数_Python Decorators(二):Decorator参数
Python Decorators II: Decorator Arguments
October 19, 2008
(本文是(Python3之模式和用法)一書的章節節選第二部分,點擊閱讀第一部分)
回顧:不含參數的decorators
在前文中,我介紹了如何使用不含參數的decorators,并使用類來實現。因為我發現這樣做更容易接受。
如果創建了一個無參decorator,被decorated的函數被傳至構造器,每次調用decorated函數時就會調用__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
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"
decorated函數的所有參數會被傳至__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__()是唯一一個被調用執行decoration的方法,每次調用decorated的sayHello()時就會調用__call__()。
含有參數的decorators
現在讓我們來修改上面的代碼,看看向decorator加入參數后結果是什么。
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"
從輸出結果可以看到,加入參數使程序執行發生了很大變化。
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
現在decoration方法調用構造器,然后就馬上調用__call__(),后者只能包含一個參數(函數對象)且返回替代原有函數的decorated函數對象。注意當前decoration期間__call__()僅被調用一次,此后從__call__()返回的decorated函數就可以在實際調用中使用了。
雖然這種機制有一定合理性—構造器在這里可獲取decorator參數,但__call__()對象不能再作為decorated函數使用了。因此你必須使用__call__()執行decoration—可能第一次遇到這種與無參情況截然不同的方式你會比較吃驚,何況還必須編寫和無參decorator完成不同的代碼。
含decorator參數的decorator函數
最后,讓我們看一個更復雜一點的decorator函數實現,它需要你處理所有細節:
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
decorator函數的返回值必須是一個封裝待decorated函數的函數。也就是說,Python會保存返回函數然后在decoration期間調用,并傳遞待decorated函數。這也是為何有三層函數的原因:里面那個函數才是被替換的。
由于閉包,wrapped_f()有權訪問decorator參數arg1, arg2和arg3,而無需像在class版本中那樣顯式存儲它們。然而,我也是在這里發現了“顯勝于隱(explicit is better than implicit)”。即使該函數版本看起來要更加簡潔緊湊,但我發現還是類版本容易理解,當然也就容易修改和維護。
下一節內容
在下一節中我會給出decorators的一些實例—基于Python開發的build system—然后在最后一節討論類decorators。
總結
以上是生活随笔為你收集整理的decorators 参数_Python Decorators(二):Decorator参数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高德地图定位精度多少米_中美俄卫星定位精
- 下一篇: 关闭日志