python装饰器(Decorator)是什么:
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
本质上python装饰器是一个能够返回函数的高阶函数。是将原函数作为参数传递到装饰器方法中,在装饰器中进行其他功能的开发,然后将包装后的方法进行返回。执行起来就是在执行包装后的方法。
举个例子来说,我想在某段代码上新增日志功能,但是原有代码不想做更改,这样的话装饰器就派上用场了,代码如下:
# 日志装饰器
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 原有方法
@log
def now():
print('2015-3-25')
# 调用now方法
now()
# 运行结果:
# call now():
# 2015-3-25
可以明显看到,原函数只是打印一个日期字符串,调用now()函数的原代码也没有任何变化。只是新增了log()函数,将其作为装饰器放到原有业务代码上,调用结果就发生了变化。首先执行了 print('call %s():' % func.__name__) 这句代码,然后才执行now()函数原有的业务逻辑,输出日期字符串。
log()函数是一个接收参数和返回值都是函数的函数,接收的函数就是装饰器要修饰的函数,返回值是修饰后的包装函数。所以表面调用的是now()函数而实际是在调用装饰器中wrapper()函数。
三层嵌套的装饰器:
import functools
# 第一层接收装饰器信息
def log(text):
# 第二层接收目标函数
def decorator(func):
# 将原方法的文档__name__等属性赋值给装饰器
@functools.wraps(func)
# 第三层接收*args, **kw
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
第一层接收的参数是装饰器的参数,上面例子中就是'execute',后两层代码与上面第一个例子相同。
上一段叙述中有一个问题,就是调用now()时实际调用的是wrapper()函数,这样如果打印__name__属性结果是wrapper,而调用的是now(),这样如果有依赖这个函数的__name__属性实现的函数就不能正确执行,为了避免这个问题产生引入了@functools.wraps(func)这句代码,它的作用是将原方法的__name__等属性复制到装饰器中,这样就保证了依赖原函数__name__等属性的方法也可以正常使用。
一个有趣的例子:
用装饰器实现单例模式:
def singleton(cls, *args, **kw):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return _singleton
@singleton
class MyClass4(object):
a = 1
def __init__(self, x=0):
self.x = x
这是我在网上看到的一个例子,感觉这种方式挺有意思,就记录下来了。其中MyClass4这个类并不知道自己是单例的,通过装饰器的修饰,每次创建实例都是通过单例模式获取的。这样就可以肆无忌惮的“创建”对象了~
补充说明:装饰器实现的单例模式目前有几个问题暂时未解决,后续补充
最后附上参考博客:廖雪峰的官方网站,python面试大全