Python 程式設計/裝飾器
外觀
在軟體中,重複程式碼被認為是一種不好的做法,主要原因是它需要更多的工作量來維護。如果同一個演算法對不同的資料執行兩次,你可以將演算法放入一個函式中並將資料傳遞給它,以避免重複程式碼。但是,有時你會發現程式碼本身發生了變化,但兩個或多個地方仍然有大量的重複樣板程式碼。一個典型的例子可能是記錄
def multiply(a, b):
result = a * b
log("multiply has been called")
return result
def add(a, b):
result = a + b
log("add has been called")
return result
在這種情況下,如何消除重複並不明顯。我們可以遵循我們之前將公共程式碼移到函式中的模式,但是用不同的資料呼叫函式不足以產生我們想要的不同行為(加法或乘法)。相反,我們必須將一個函式傳遞給公共函式。這涉及一個對函式進行操作的函式,稱為高階函式。
Python 中的裝飾器是高階函式的語法糖。
屬性裝飾器的最小示例
>>> class Foo(object):
... @property
... def bar(self):
... return 'baz'
...
>>> F = Foo()
>>> print(F.bar)
baz
上面的示例實際上只是像這樣的程式碼的語法糖
>>> class Foo(object):
... def bar(self):
... return 'baz'
... bar = property(bar)
...
>>> F = Foo()
>>> print(F.bar)
baz
通用裝飾器的最小示例
>>> def decorator(f):
... def called(*args, **kargs):
... print('A function is called somewhere')
... return f(*args, **kargs)
... return called
...
>>> class Foo(object):
... @decorator
... def bar(self):
... return 'baz'
...
>>> F = Foo()
>>> print(F.bar())
A function is called somewhere
baz
裝飾器的良好用途是允許你重構你的程式碼,以便可以將常見功能移動到裝飾器中。例如,假設你想跟蹤對某些函式的所有呼叫,並打印出每次呼叫時所有函式引數的值。現在你可以在裝飾器中實現它,如下所示
#define the Trace class that will be
#invoked using decorators
class Trace(object):
def __init__(self, f):
self.f =f
def __call__(self, *args, **kwargs):
print("entering function " + self.f.__name__)
i=0
for arg in args:
print("arg {0}: {1}".format(i, arg))
i =i+1
return self.f(*args, **kwargs)
然後,你可以透過以下方式將裝飾器用於你定義的任何函式
@Trace
def sum(a, b):
print "inside sum"
return a + b
執行此程式碼,你會看到類似的輸出
>>> sum(3,2)
entering function sum
arg 0: 3
arg 1: 2
inside sum
或者,你可以使用函式而不是將裝飾器建立為類。
def Trace(f):
def my_f(*args, **kwargs):
print("entering " + f.__name__)
result= f(*args, **kwargs)
print("exiting " + f.__name__)
return result
my_f.__name = f.__name__
my_f.__doc__ = f.__doc__
return my_f
#An example of the trace decorator
@Trace
def sum(a, b):
print("inside sum")
return a + b
#if you run this you should see
>>> sum(3,2)
entering sum
inside sum
exiting sum
5
請記住,返回函式或一個合適的裝飾替換函式是一個好的做法,這樣就可以對裝飾器進行鏈式呼叫。