1. 装饰者模式
装饰者模式是常用的软件设计模式之一。通过此设计模式,我们能够在不修改任何底层代码情况下,给已有对象赋予新的职责。python中可以用装饰器简单地实现装饰者模式。
1.1 将函数作为参数传递
在C/C++中,函数指针可以将函数作为参数传递给另一函数。而在python中,函数也是对象的一种,函数可以被引用,也可直接作为参数传入函数,以及作为容器对象的元素。python中可以采用如下方法实现装饰者模式:
#!/usr/bin/env python3.6# -*- coding: utf-8 -*-def add(x, y): result = x+y return resultdef log(func): def wrapper(*args, **kwargs): result = func(*args) print(func.__name__,'has been called\n') return result return wrapperif __name__ == '__main__': print(log(add)(1,2))
上述代码中,log
函数以需要被装饰的函数作为参数,并返回函数对象。被返回的函数的参数为可变参数*args
与**kwargs
(*args
参数会被封装成tuple
,**kwargs
参数则会被封装成字典对象),以适应不同函数的不同参数,保证通用性。
1.2 装饰器
上面的实现方法有些繁杂,所有调用被装饰的函数之处的代码,都要进行相应修改,自然不符合python简洁易读的特性。因此python中给出相应语法糖来增加可读性和易用性,那便是“装饰器”。
#!/usr/bin/env python3.6# -*- coding: utf-8 -*-from functools import wrapsdef log(func): #@wraps(func) def wrapper(*args, **kwargs): result = func(*args) print(func.__name__,'has been called') return result return wrapper#等价于add = log(add)@logdef add(x, y): result = x+y return resultif __name__ == '__main__': print(add(1,2)) print(add.__name__)
运行情况如下:
>>print(add(1,2)) add has been called3>>print(add.__name__) wrapper
但上述方法亦有缺陷,原函数add的元数据(比如名字、文档字符串、注解和参数签名)会丢失。为避免缺陷,任何时候你定义装饰器的时候,都应该使用functools
库中的@wraps
装饰器来注解底层包装函数(代码中注释部分)。@wraps
有一个重要特征是它能让你通过属性 __wrapped__
直接访问被包装函数。
改进后运行情况:
>>print(add(1,2)) add has been called3>>print(add.__name__) add
1.3 解除装饰器
当装饰器已经作用于某函数,而你想撤销它,那么可以访问 __wrapped__
属性来访问原始函数
orig_add = add.__wrapped__ orig_add(1,2)
但若使用了多个装饰器, __wrapped__
属性会变得不可控,应尽量避免使用。
若有如下代码:
#!/usr/bin/env python3.6# -*- coding: utf-8 -*-import functoolsimport timedef metric(func): @functools.wraps(func) def wrapper(*args,**kv): print('Decorator1') f = func(*args,**kv) return f return wrapperdef logging(func): @functools.wraps(func) def wrapper(*args,**kv): print('Decorator2') f = func(*args,**kv) return f return wrapper@metric@loggingdef normalize(name): sName = name[0:1].upper() + name[1:].lower() print(sName)if __name__ == '__main__': normalize('heLlO') normalize.__wrapper__('')
运行情况如下:
>>normalize('helLo') Decorator1 Decorator2 Hello>>normalize.__wrapped__('world') Decorator2 World
1.4 定义带参数的装饰器
from functools import wrapsdef log(text): def decorator(func): @wraps(func) def wrappering(*args,**kv): print('%s %s():'%(text,func.__name__)) return func(*args,**kv) return wrappering return decorator@log('run')def normalize(name): sName = name[0:1].upper() + name[1:].lower() print(sName)
装饰器函数可以带参数,最外层的函数会将参数传给内层的装饰器函数,即wrappering
函数是可以使用log
的传入参数的。
装饰器处理过程与下面是等价的:
normalize = log('run')(normalize)
作者:lethe_zh
链接:https://www.jianshu.com/p/d307e8ac9e2a
共同学习,写下你的评论
评论加载中...
作者其他优质文章