装饰器和装饰模式
先给出两者的定义:
- 装饰器:装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
- 装饰模式:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰器是python的高级函数应用的一个技巧,可以在不修改对象的前提下增强对象的功能。这个对象可以是函数,类方法和类属型。看到“装饰”和它的功能,对于设计模式比较熟悉的同学应该会想到装饰模式。如果你现在没有想到装饰器或者对于装饰器没有一个清晰的概念认识,那么就跟我一起来复习一下装饰模式。深入理解装饰模式对于装饰器的理解会很有帮助。
装饰模式示例
这里我们使用java来通过一个简单的场景展示一下装饰模式。
情景:程序猿没日没夜的工作可以赚钱,把钱攒起来之后就可以买房买车,迎娶白富美,走上幸福生活。。。
这里我们定义一个可以存钱的接口:CanSaveMoney,程序猿类Coder。
public interface CanSaveMoney {
public void save(int money);
}
class Coder implements CanSaveMoney {
private int count = 0;
@Override public void save(int money) {
count += money;
}
}
/××
×存钱的方法
××/
public void saveMoney(CanSaveMoney person, int money) {
person.save(money)
}
情景续:过了一段时间,程序猿追到了女神,需要和女神培养感情。可是我们之前只是留了一个存钱的功能没有取钱的功能,没有问题,程序猿都是聪明的,我们存一个负的钱数不就是取钱了麻。就这样程序猿顺利的跟女神培养好了感情。
到了女神管理程序猿的收入的时候了,女神识破了程序猿的小把戏,怎样修复这个漏洞呢?女神是霸道的,女神认为程序猿的一切都是她的,程序猿上缴工资卡,一切都经过女神的手。
class Godness implements CanSaveMoney {
private Coder coder;
public Godness(Coder coder) {
this.coder = coder;
}
@Override public void save(int money) {
if (money <= 0) {
throw new RuntimeException("滚犊子");
} else {
coder.save(money);
}
}
}
上面就是使用了装饰模式,在不改动原有类的情况下增强类的某一功能。
python 装饰器
python中装饰器是对装饰模式的一个更宽泛的应用,不仅仅能够应用于类,也能应用于函数,类方法和类属性。灵活利用装饰器可以大大提高你的python开发效率。
简单的装饰器示例
下面是一些简单的装饰器示例, 展示了你可以在函数调用之前或者之后做一些处理。
import functools
# do something before call
def log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print(‘begin %s()‘ % fun.__name__)
return fun(*args, **kwargs)
return wrapper
# do something before and after call
def log2(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
print(‘begin call %s()‘ % fun.__name__)
f = fun(*args, **kwargs)
print(‘end call‘)
return f
return wrapper
# decorator with param
def log_tag(tag):
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(‘%s %s()‘ % (tag, func.__name__))
return func(*args, **kwargs)
return wrapper
return log
#装饰器类,适用于需要保存一些多次调用的信息,如函数调用的次数
class LogClass(object):
def __init__(self, f):
self.f = f
self.count = 0
# 复制原函数的属性
for n in set(dir(f)) - set(dir(self)):
setattr(self, n, getattr(f, n))
def __call__(self, *args):
self.count += 1
print(‘called times: %s‘ % self.count)
return self.f(*args)
def __repr__(self):
return self.f
@log_tag("execute")
def print_num(n=0):
print(‘num is %s‘ % n)
if __name__ == ‘__main__‘:
for n in range(1, 4):
print_num(n)
print(‘%s‘ % print_num.__name__)
关于@functools.wraps()
@functools.wraps()
是functools模块中一个非常有用的装饰器,它的作用是把原函数的属性复制到装饰过的函数中。函数也是对象,所以函数的属性这也概念不难理解,例如你可以使用pirnt(‘%s‘ % print_num.__name__)
来打印出函数的名称属性。这里重点解释装饰过的函数并非原函数。
上面的代码片断最后我们打印出了函数的名称属性,如果我们把装饰器中的@functools.wraps()
这一行注释掉之后,再执行打印,打印出的结果不会再是print_numm
了,而是wrapper
。弄清楚原因你就理解了@functools.wraps()
的作用。
为什么装饰过的函数的print_num.__name__
改变了?
上面我们使用@log
的形式来使用装饰器装饰函数,这在python里面叫装饰器语法。不使用装饰器语法的的形式是这样的
print_num = log(print_num)
print_num(n)
这样我们就理解了为什么装饰过后的函数不再是原来的函数了。上面代码段中装饰器类初始化代码中获取设置原函数属性的作用类似于@functools.wraps()
的作用。
版权声明:本文为博主原创文章,未经博主允许不得转载。