函数装饰器
python的装饰器是个非常强大的功能,下面的部分代码借鉴stackoverflow上面的代码,是对我学习装饰器的一个梳理。
函数装饰器(function decorator)
什么是装饰器(decorator)?
装饰器就是任何把其他函数或者方法当做参数并且返回一个增强函数的函数被当做装饰器
装饰器的概念可以从字面意义上面理解,就是其他函数通过@语法糖把某些装饰器的功能借用过来。
#这是一个非常简单的装饰器,它什么也没有做,但是这个就是装饰器的原型,也可以理解为语法规则。
# -*- coding:utf-8 -*-
def first_decorator(func):
return func
@first_decorator #等同于my_functions = first_decorator(my_function)
def my_function():
print("a simple test decorator")
if __name__ == '__main__':
my_function()
#output:
#a simple test decorator
如何才能理解一个装饰器呢?很简单,让我们看看如果破坏这种‘语法糖’的话会发生什么?
# -*- coding:utf-8 -*-
def second_decorator(func):
pass #装饰器没有返回一个函数
@second_decorator
def my_function():
print("what will happend?")
if __name__ == '__main__':
my_function()
#output
#TypeError: 'NoneType' object is not callable #返回一个错误
那么我们一般是如何使用一个装饰器的呢?使用一个两层嵌套,看看下面这个例子:
# -*- coding:utf-8 -*-
def logger(func):
def wrapper(*args, **kwargs):
print("calling {0}".format(func.__name__)) #__name__方法是输出函数的名字
return func(*args, **kwargs) #最后输出的是原始函数
return wrapper #返回后代替logger成为装饰器
@logger #装饰器的使用
def get_args(*args, **kwargs):
print("*args={0}, **kwargs={1}".format(*args,**kwargs))
if __name__ == '__main__':
get_args('hello','world')
#output
#calling get_args
#*args=hello, **kwargs=world
上面我们看到的装饰器的参数都是函数,这个是默认的装饰器的特性,那么如果我想在装饰器中加入其他的参数怎么办?我们需要使用一个三层嵌套。
#这个装饰器没有什么特别的含义,只是展示一下装饰器工作的过程,我们可以自己想装饰器中输入参数而不需要借助被装饰函数操作
# -*- coding:utf-8 -*-
def logger(text):
def decorator(func):
def wrapper(*args,**kwargs):
print("*args={0}, **kwargs={1}".format(*args, **kwargs))
print("{1} calling {0}".format(func.__name__, text))
return func(*args,**kwargs)
return wrapper
return decorator
@logger("sys")
def get_args(*args, **kwargs):
print("*args={0}, **kwargs={1}".format(*args,**kwargs))
if __name__ == '__main__':
get_args("chuan","cong")
print("func.name = {0}".format(get_args.__name__))
#output
#*args=chuan, **kwargs=cong
#sys calling get_args
#*args=chuan, **kwargs=cong
#func.name = wrapper #这里的输出说明函数经过装饰器装饰之后函数吗已经变成了最后装饰它的那个函数的名称,这样是很容易出问题的,在一些需要函数前面的代码中,我们应该解决这个问题。
如何制止装饰器修改被装饰函数的函数名呢?只需要functools模块即可
# -*- coding:utf-8 -*-
import functools
def logger(text):
def decorator(func):
@functools.wraps(func) #我们只需要在装饰器最后一个函数的前面加上这个装饰器即可,这个是模块自带的
def wrapper(*args,**kwargs):
print("*args={0}, **kwargs={1}".format(*args, **kwargs))
print("{1} calling {0}".format(func.__name__, text))
return func(*args,**kwargs)
return wrapper
return decorator
@logger("sys")
def get_args(*args, **kwargs):
print("*args={0}, **kwargs={1}".format(*args,**kwargs))
if __name__ == '__main__':
get_args("chuan","cong")
print("func.name = {0}".format(get_args.__name__))
#output
#*args=chuan, **kwargs=cong
#sys calling get_args
#*args=chuan, **kwargs=cong
#func.name = get_args #看,已经被修改过来了
类装饰器
什么是类装饰器?函数装饰器是形如:my_function=decorator(my_function),那么类呢?形式和函数装饰器差不多,但是实现过程由细微的差别。
class Decorator(object):#类装饰器的构建
def __init__(self, func): #初始化是把被装饰函数当做参数,赋予对象
self.func = func
def __call__(self, *args, **kwargs):
print('Before the function call.')
res = self.func(*args, **kwargs) #调用函数
print('After the function call.')
return res
@Decorator
def testfunc():
print('Inside the function.')
print('xue')
testfunc()
print(type(testfunc))
#output
#Before the function call.
#Inside the function.#函数调用输出
#xue
#After the function call.
#<class 'dec_test2.Decorator'> #被装饰函数类型改变
函数装饰器和类装饰器由什么不同呢?
类装饰器中被装饰函数只有被调用时才会执行,否则不会执行,但是函数装饰器在装饰完成后会在最后执行。
同样,类装饰器在装饰函数之后,被装饰的函数类型会发生改变,变成了类。