装饰器是python中很好用的一个特性,我们看下面的方法装饰器
# 定义一个装饰器,对函数调用增加日志输出功能
def call_log(func):
# 装饰输入的函数
def func_wrapper(*args, **kw):
print('will call func: %s' % func.__name__)
return func(*args, **kw)
# 返回装饰后的函数
return func_wrapper
@call_log
def method1():
print('calling method1')
上述装饰器的定义可以发现它是一个将函数作为参数并且内部定义一个新的函数作为返回值的结构,它是一个典型的高阶函数
装饰器也是可以携带参数的
# 定义一个带参数的装饰器,对函数调用增加日志输出功能
def call_log(param):
# 返回一个装饰器
def func_decorator(func):
# 装饰输入的函数
def func_wrapper(*args, **kw):
print(param)
print('will call func: %s' % func.__name__)
return func(*args, **kw)
# 返回装饰后的函数
return func_wrapper
return func_decorator
@call_log('test')
def method1():
print('calling method1')
在网络上看到一道python题大致意思是【如何实现一个装饰器可以同时适用上面的2种情况】,其实如果我们对装饰器的原理以及高阶函数的应用很熟悉的话,那么这道题将很容易分析出来:
- @call_log这种形式的装饰器是直接调用装饰器方法来修饰待的方法;
- @call_log(x)这种形式的装饰器则是使用call_log(x)方法的返回值作为一个装饰器来修饰待装饰的方法;
所以我们最终定义的这个装饰器方法应该同时具备
接收一个待装饰的方法作为参数
和接收若干个装饰变量作为参数
的能力
按照这个思路该装饰器的最终实现如下:
def call_log(obj):
# 如果参数是函数,则直接装饰该函数并返回
if isinstance(obj, types.FunctionType):
def func_wrapper(*args, **kw):
print('will call %s:' % obj.__name__)
return obj(*args, **kw)
return func_wrapper
else:
# 返回一个装饰器
def func_decorator(func):
def func_wrapper(*args, **kw):
print(obj)
print('will call func: %s' % func.__name__)
return func(*args, **kw)
return func_wrapper
return func_decorator
# 不带参数装饰方法
@call_log
def method1():
print('calling method1')
# 携带带参数装饰方法
@call_log('test')
def method2():
print('calling method2')
method1()
method2()
'''
输出如下
will call method1():
calling method1
test
will call func: method2()
calling method2
'''
上述实现中通过对call_log
方法的参数的类型来判断它的调用出是不带参数的装饰器调用还是带参数的装饰器调用从而动态返回内部的实现。
事实上带参数的装饰器
@call_log('test')
完全可以拆分为两步,它等价于下面的代码:
# 先返回一个装饰器函数
decorator = call_log('test')
# 在用返回的装饰器来装饰method2
@decorator
def method2():
print('calling method2')
总结
- 属性装饰器可用来定义的私有成员变量的setter和getter方法达到封装的内部实现的目的;
- 类装饰器和方法装饰器可以装饰方法,为方法添加额外的代码逻辑,通常用在诸如添加日志,添加时间戳,添加监控。
- 装饰器有点类似于其他语言中AOP的概念;
- 装饰器的本质是对函数式编程中的高阶函数的应用;