python的装饰器
装饰器 简单的来说就是函数的函数,在执行目标函数时,先执行装饰器函数,很像HOOk
简单的装饰器
实现一个打印日志的装饰器
def myLogger(func):
def wrapper(*args, **kw):
print('开始执行:{} 函数了:'.format(func.__name__))
result = func(*args, **kw)
print('{}执行完啦。'.format(func.__name__))
return result
return wrapper
myLogger是装饰器, 参数func是目标函数,返回值也是个函数,所以说装饰器是函数的函数
@myLogger
def add(a,b):
return a + b
print(add(1,2))
开始执行:add 函数了:
add执行完啦。
3
@myLogger
def sub(a,b):
return a - b
sub(2,1)
开始执行:sub 函数了:
sub执行完啦。
1
装饰器很灵活,只要加个装饰器,就实现了每个函数的打印日志的功能
带参数的装饰器
实现一个函数运行失败时,自动重新运行的装饰器,这在爬虫中经常看到。我们传给装饰器自动尝试运行的次数的参数
def myTry(try_nums):
def myfunc(func):
def wrapper(*args, **kw):
i = 0
while i < try_nums:
print('开始发起{}次请求'.format(i+1))
try:
result = func(*args, **kw)
return result
except Exception as e:# 这个地方要专门捕获 超时,连接中断等错误
error = e
i += 1
return error
return wrapper
return myfunc
这里不使用爬虫程序,使用其他函数看看效果。
@myTry(3)
def add(a,b):
return a+b
add(1,2)
开始发起1次请求
3
add('1',2)
开始发起1次请求
开始发起2次请求
开始发起3次请求
TypeError('must be str, not int')
不带参数的类装饰器
上面都是基于函数实现的装饰器,其实类也能当作装饰器
基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。 init :接收被装饰函数, call :实现装饰逻辑。
实现一个收集错误信息的类装饰器
class MyExcept():
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('{} 函数开始执行'.format(self.func.__name__))
data = {'status':1, 'msg':'', 'result': ''}
try:
result = self.func(*args, **kwargs)
data['result'] = result
except Exception as e:
data['status'], data['msg'] = 0, e
return data
@MyExcept
def add(a,b):
return a+b
add(1,1)
add 函数开始执行
{'status': 1, 'msg': '', 'result': 2}
add('1',1)
add 函数开始执行
{'status': 0, 'msg': TypeError('must be str, not int'), 'result': ''}
带参数的类装饰器
带参数和不带参数的类装饰器还是有一定的差别
实现一个自动尝试运行和收集错误信息的类装饰器
class MyExcept():
def __init__(self, try_nums):
self.try_nums = try_nums
def __call__(self, func):
def wrapper(*args, **kwargs):
i = 0
while i < self.try_nums:
print('{} 函数开始第{}次执行'.format(func.__name__,i+1))
try:
result = func(*args, **kwargs)
data = {'status':1, 'msg':'', 'result': result}
break
# except (timeout): #要捕获的错误允许重新尝试
# i += 1
# data = {'status':0, 'msg':'超时', 'result': ''}
except Exception as e: # #其他错误不允许重新尝试
data = {'status':0, 'msg':e, 'result': ''}
i += 1
return data
return wrapper
@MyExcept(3)
def add(a,b):
return a+b
add(1,1)
add 函数开始第1次执行
{'status': 1, 'msg': '', 'result': 2}
add('a',1)
add 函数开始第1次执行
add 函数开始第2次执行
add 函数开始第3次执行
{'status': 0, 'msg': TypeError('must be str, not int'), 'result': ''}
通过上面几个装饰器的实现,是否已经明白装饰器的本质了。
对于带参数的装饰器,它是在不带参数的装饰器的基础上,在外面又封装了一层函数,用于接收参数,然后在装饰器内部使用这些参数,相当于带参数的装饰器执行完之后返回了一个装饰器。
对于装饰器,本质就是要把目标函数当作参数传入装饰器。 事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。 所以类要实现call函数
add
<function __main__.MyExcept.__call__.<locals>.wrapper(*args, **kwargs)>
add.__name__
'wrapper'
add 经过装饰器之后,实际上内部已经经过某些处理,使得add函数变成了wrapper函数,所以表面上看我们在调用了add函数,实际上调用的wrapper函数。
经过哪些处理实际上也很好理解,还是上面的例子,添加一些提示。
from functools import wraps
class MyExcept():
def __init__(self, try_nums):
print('类开始初始化')
self.try_nums = try_nums
def __call__(self, func):
print('开始调用函数')
@wraps(func)
def wrapper(*args, **kwargs):
i = 0
while i < self.try_nums:
print('{} 函数开始第{}次执行'.format(func.__name__,i+1))
try:
result = func(*args, **kwargs)
data = {'status':1, 'msg':'', 'result': result}
break
# except (timeout): #要捕获的错误允许重新尝试
# i += 1
# data = {'status':0, 'msg':'超时', 'result': ''}
except Exception as e: # #其他错误不允许重新尝试
data = {'status':0, 'msg':e, 'result': ''}
i += 1
return data
print('返回函数wrapper')
return wrapper
@MyExcept(3)
def add(a,b):
return a+b
类开始初始化
开始调用函数
返回函数wrapper
在我们还没有调用函数时,函数已经开始调用,并返回了一个函数,实际上add 已经指向了内部wrapper函数了
第一步,装饰器执行装饰器的参数,返回一个callable类型的对象,而且这个对象的参数必须是一个函数。
第二步,返回的对象把目标函数当作参数,执行后,返回一个函数赋值给目标函数(目标函数指向返回函数)。
第三步,等待正常调用。
add
<function __main__.add(a, b)>
add.__name__
'add'
这里使用 functools .wraps 装饰器,它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉。
既然装饰器可以装饰函数,那装饰器可以不可以装饰类呢,也是可以的。
装饰类的装饰器
实现单例模式的装饰器
def SingleInstance(cls):
def new(cls,*args,**kwargs):
if not hasattr(cls,'instance'):
cls.instance= object.__new__(cls)
return cls.instance
cls.__new__=new
return cls
@SingleInstance
class User:
_instance = None
def __init__(self, name):
self.name = name
u1 = User('jack')
u1.name
'jack'
u2 = User('tom')
u2.age = 20
u2.name
'tom'
print(u1.name, u1.age)
tom 20
u1 is u2
True