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
请记住,返回函数或一个合适的装饰替换函数是一个好的做法,这样就可以对装饰器进行链式调用。