装饰器是Python的一个重要的组成部分,是修改的其他函数的功能函数的功能函数,有助于缩短代码。xi下面介绍如何使用装饰器类.
1,函数装饰器
如果我们想给函数曾江额外的操作,但是不更改函数的内部结构,可以通过给函数添加装饰器来实现。
1.1函数添加装饰器
def time_use(func):
# Decorator that reports the execution time
# parame func:
# return :
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'func name:{func.__name__},time used:{end-start}')
return result
return wrapper
@time_use
def count_down(n):
# Counts down
# :param n:
# :return:
while n > 0:
n-=1
count_down(100000)
count_down(1000000)
输出为:
func name:count_down,time used:0.02700519561767578
func name:count_down,time used:0.26958799362182617
观察上述例子发现,time_use(func)表示对应的修饰器,func表示被修饰的函数。
装饰器使用了*args, **kwargs来接收任意参数的函数
1.1装饰器中保留的函数元信息
在实际的应用中会发现,我们把装饰器作用在某个函数后发现该函数逇重要的元信息的比如,名字,文档字符串,注解和参数签名都丢失了但是我们希望保留的这些信息。
故,在定义装饰器的时候,我们应该使用functool库中的@wraps装饰器来注解底层包装函数。
from functools import wraps
import time
def time_use(func):
# Decorator that reports the execution time
# parame func:
# return :
# 保留原函数的元信息
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'func name:{func.__name__},time used:{end-start}')
return result
return wrapper
# 使用被包装后的函数,检测对应的元信息
@time_use
def count_down(n):
# Counts down
# :param n:
# :return:
while n > 0:
n-=1
count_down(100000)
print(f'func name:{count_down.__name__}')
print(f'func name:{count_down.__doc__}')
输出为:
func name:count_down,time used:0.02752208709716797
func name:count_down
func name:None
1.3 解除装饰器
对于已经使用了装饰器的函数,我们有时又会期望回到原始的,没有被装饰的状态。
from functools import wraps
import time
def time_use(func):
# Decorator that reports the execution time
# parame func:
# return :
# 保留原函数的元信息
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'func name:{func.__name__},time use:{end-start}')
return result
return wrapper
@time_use
def add(x,y):
# Counts down
# :param n:
# :return:
return x+y
print(add(1,2))
add1 = add.__wrapped__
print(add1(1,2))
输出结果为:
func name:add,time use:9.059906005859375e-06
3
@wraps一个重要的特征是,他能通过属性的__wrapped__直接访问被包装的函数。
1.4 带参数的装饰器
在实际应用中,我们有时候希望装饰器可以带参数,或者说定义一个可以带参数的装饰器。
from functools import wraps
import time
import logging
def logged(level,name=None,message=None):
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level,logmsg)
return func(*args, **kwargs)
return wrapper
return decorate
@logged(logging.DEBUG)
def add(x,y):
return x + y
@logged(logging.CRITICAL,"examples")
def spam():
print("spam")
print(f"func name:{add(2,3)}")
print(f"func name:{spam()}")
输出的结果为:
func name:5
spam
spam
func name:None
2,类中定义装饰器
类中定义装饰器很简单,首先要确认它的使用方式。到底是作为一个实例方法还是类方法。
from functools import wraps
class A:
#Decorators as a instance method
def decorator_1(self,func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Decorator - 1 ")
return func(*args, **kwargs)
return wrapper
def decorator_2(self,func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Decorator - 2 ")
return func(*args, **kwargs)
return wrapper
a = A()
# 实例调用
@a.decorator_1
def spam():
print(f"Spam=1")
# 类调用
@A.decorator_1
def spam1():
print(f"Spam=2")
通过观察可以发现,装饰器的调用,一个是实例调用,一个是类调用。
2.1 装饰器定义为类
在实际的应用中,我们需要使用一个装饰器取包装函数,但是希望返回的一个可以调用的实例,并且的需要装饰器的可以同时工作在的类定义的内部和外部
为了将装饰器定义为一个实例,我们需要的确保的它实现了__call__()方法和__get__()方法。
from functools import wraps
import types
class Profile:
def __init__(self,func):
wraps(func)(self)
self.ncalls = 0;
def __call__(self,*args,**kwargs):
self.ncalls += 1
print(f"555")
return self.__wrapped__(*args,**kwargs)
def __get__(self,instance,cls):
if instance is None:
return self
else :
return types.MethodType(self, instance)
@Profile
def add(x,y):
return x+y
class Spam:
@Profile
def bar(self,x):
print(f"object:{self},param is {x}")
print(f"number add result:{add(5,2)}")
print(f"number add result:{add(5,8)}")