知道python 装饰器这个名字很久了,也在很多代码中看到,但是感觉始终没有很透彻的了解,所以今天花了些时间来弄懂它,我会尝试用通俗易懂的例子来解释。
首先,给一个定义,可能不严谨(根据我自己的理解)。装饰器就是一种方法,能够对新的函数进行装饰(或封装),使他们有相同的特性或功能。
假设你正在开发一个计算器。要求是能够实现加减这些简单的功能,同时在进行计算时输出 ‘hello user’。很简单,只需要如下两个简单的函数。但是这个写法有一个问题,print('hello user') 起相同的作用,但是在不同函数中需要重复写。假设你有100个方法,那么就造成很多重复语句。并且,如果你哪天不想打印 hello user 了,要一个一个改。
#code 1
def plus(a, b):
print('hello user')
result = a+b
return result
def sub(a, b):
print('hello user')
result = a-b
return result
a = 10
b = 5
result_plus = plus(a, b)
result_sub = sub(a, b)
这个问题可以被解决,如下所示。 很简单,只需要定义另一个函数,将相同的功能固定,但是传入不同的计算方法来获得不同的结果即可。但是,这会导致另外一个问题(我猜测的,我也没有被这个解释说服 :)。这看起来不 ‘优雅’。因为第一眼看上去调用的是decorator()这个函数,实际上在很多代码中就是这么用的。需要至少两次跳转才知道具体执行的哪个函数。那么,怎么解决呢。
#code 2
def decorator(fun, a, b):
print('hello user')
result = fun(a, b)
return result
def plus(a, b):
result = a+b
return result
def sub(a, b):
result = a-b
return result
a = 10
b = 5
result = decorator(plus, a, b)
result = decorator(sub, a, b)
最终,developer 们开发了另一个 ‘fancy’ 的用法。具体原理跟如上相似,也是把新定义的函数当作参数传入另一个函数。案例如下。结果跟上面完全相同,但是一眼就知道执行的是哪个函数。
#code 3
def decorator(fun):
def wrapper(a, b):
print('hello user')
result = fun(a, b)
return wrapper
@decorator
def plus(a, b):
result = a+b
return result
@decorator
def sub(a, b):
result = a-b
return result
a = 10
b = 5
result = plus(a, b)
result = sub(a, b)
这里有一个问题我困惑了很久。为什么decorator()里边要再多套一层 wrapper()。同样的功能完全可以实现只使用一个decorator() 函数,只需要加上类似@decorator 的骚操作, 如下所示 (应该无法运行)。我认为最主要的原因是,开发者希望decorator() 返回的是一个函数, 即 wrapper。 只使用一层函数,无法实现这个功能,自己也可以试一下。比如:
def decorator(fun, a, b):
print('hello user')
result = fun(a, b)
return decorator
:)
#code 4. 应该不能运行
def decorator(fun, a, b):
print('hello user')
result = fun(a, b)
return result
@decorator
def plus(a, b):
result = a+b
return result
@decorator
def sub(a, b):
result = a-b
return result
a = 10
b = 5
result = plus(a, b)
result = sub(a, b)
好了,主要原理已经解释完了,最后添加一点补充。基于 #code 3的代码, wrapper() 的需要的参数是 a, b,这是由 fun() 决定的。 很明显,如果你定义的fun() 通常使用不同的参数,那么这样必然会报错。最终,代码可以这样修改来解决这个问题。
#code 5
def decorator(fun):
def wrapper(*args, **kwargs):
print('hello user')
result = fun(*args, **kwargs)
return wrapper
@decorator
def plus(a, b):
result = a+b
return result
@decorator
def sub(a, b):
result = a-b
return result
a = 10
b = 5
result = plus(a, b)
result = sub(a, b)