通过“函数式编程基础(四)”的学习,我们已经知道:装饰器可以在函数的外部扩展一个函数的行为,而不用修改函数内部的调用。本片将补充讲解一些函数式编程的基础以及向装饰器传递参数的方法。
注:本篇博客参考了:https://blog.csdn.net/qq_15552763/article/details/52031855 转载的内容
一. 关于函数的进一步探索
在python中函数是一等公民,可以像基本数据类型一样,赋值给另一个变量:
设置可以删除原来的引用,但是通过“statement”任然可以访问原来的函数:
python中,函数中可以定义另一个函数(注意:内部定义的函数引用属于局部变量,在函数外部不能访问)
函数引用还可以作为另一个函数的返回值:
因此,函数引用也可以作为参数传递:
综合上面的知识,我们可以写出一个装饰器,再被封装函数之前和之后分别执行一段代码,而不去修改函数本身
上述代码实现了一个最简单的装饰器:我们将statement函数传递给装饰器,装饰器将动态地将其包装在任何你想执行的代码中,然后返回一个新的函数。调用新函数,可以看到装饰器的效果。可是上述代码的问题是:每次调用statement函数都需要修改为调用func_wrapper函数,为此,我们重新赋值statement即可:
以上过程,python为我们提供了一个语法糖简化操作:
@my_decorator的作用:用statement绑定装饰器函数my_decorator的返回值。即被装饰的函数引用将被重新绑定到装饰器函数的返回值。
此外,装饰器可以叠加,但是先后顺序非常重要:
二. 向装饰器函数传递参数
当我们调用装饰器返回的函数时,实际上是调用包装函数,因此给包装函数传递参数即可将参数传递给装饰器:
在装饰方法时,必须考虑方法的首个参数,即指向当前对象的引用(self):
或者你也可以利用*args和**kwargs,定义一个更加通用的装饰器,可以用在任何函数或对象方法上,而不必关系其参数:
修饰函数,传参方式为位置传参:
修饰函数,传参方式为位置传参和关键字传参:
修饰方法:
三. 向装饰器本身传递参数
由于装饰器必须使用函数作为参数,因此不能直接传递参数给装饰器本身,向装饰器本身传递参数时,我们必须另辟蹊径,声明一个用于创建装饰器的函数。
1)定义装饰器创建函数,并创建一个装饰器:
2)使用装饰器装饰一个函数:
3)调用被装饰的函数:
跳过中间变量new_decorator做同样的事:
再次调用被装饰的函数:
那么重点来了:上述,我们使用@调用一个函数,那么,向装饰器本身传递参数就变得简单了。因此我们可以通过函数去创建装饰器,并传递参数给这个装饰器。
1)定义一个包含参数的装饰器创建函数,并使用此装饰器传递参数
2)调用被装饰的函数:
注意:装饰器仅在Python代码被导入时执行一次,之后不能再动态地改变参数。
四. @wraps装饰器
1)定义一个函数,打印其__name__和__doc__属性:
2)给该函数添加二中定义的装饰器:
可见我们此时无法获取原函数的__name__和__doc__属性,取而代之的是wrapper函数的相关属性,python2.5之后,允许我们使用functools包中的wraps来消除此副作用:
3)定义装饰器时使用@wraps装饰wrapper函数:
4)再次运行2)中的程序:
五. 装饰为什么那么有用
因为使用装饰器可以在函数的外部扩展一个函数的行为,而不用修改内部的调用,下面分别使用装饰器来打印一个函数的执行时间、记录函数日志、记录一个函数的运行次数。
1)记录函数执行时间
2)记录函数日志
3)记录函数执行次数
运行结果: