前言
写过一段时间的python代码或者阅读过python程序的人一定不会陌生“装饰器”,其语法糖为@。今天就来好好看看到底什么是python装饰器,它到底装饰了什么?以及为什么要使用装饰器。
“装饰器”
什么是装饰器
python装饰器是的本质也是函数或者类,它接受另外的函数的作为其逻辑参数,将真正的业务函数包裹在其中。在不影响业务函数执行情况下,对业务函数添加额外的功能。
为什么要用装饰器
在前面已经说过,装饰器的作用是在不影响业务函数执行情况下, 对业务函数添加额外的功能。例如,需要打印函数的执行日志,一种方法是在业务函数中添加打印日志语句;但是,当函数数量很多时,这种方法就显得不那么靠谱,而且程序中会充斥着大量的重复代码,影响程序的可读性。这时,装饰器就能够发挥很大的作用。
详解装饰器
装饰器总的来说分为函数装饰和类装饰器,函数装饰器中又有无参数装饰器和带参数装饰器,所装饰函数也包括无参函数,一个参数或者多个参数函数,甚至是参数个数不固定的函数,以及带有关键字参数的函数。虽然这里说明了装饰的种类比较多,但是其思想完全一样。所以弄懂装饰器工作原理,这些问题也就迎刃而解了。
无参函数的装饰器
无参函数的装饰器也即是所装饰函数不带有参数,具体看下面代码
1 | # 无参数函数的装饰器 |
执行结果:
1 | func is running... |
上面的代码实现了一个简单的装饰器,在业务函数执行之前打印正在即将执行函数名称。可以看到真正的业务函数是func()
,其被包裹在函数wrapper
之中。函数record_logging
即是装饰器,它的返回值是一个函数wrapper
,然后语句func = record_logging(func)
在调用装饰器。这样就能够达到我们所需要的目的。在python中,使用@来告诉python,该函数需要被装饰器所装饰,这样就可以省略掉赋值语句func = record_logging(func)
,具体如下
1 | def record_logging(func): |
执行结果:
1 | func is running... |
可以看出,其执行结果与赋值调用装饰器结果一样。而且使用@使代码更加简洁美观易读,也更加的pythonic。
一个参数函数的装饰器
当所装饰的业务函数带有逻辑参数时,又该如何实现装饰器呢?
1 | # 一个参数函数的装饰器 |
执行结果:
1 | func is running... |
其逻辑思想与无参数函数装饰器一致,其区别仅仅在于wrapper
函数接受业务函数参数(仔细对比无参数函数装饰器的写法)。
多个参数甚至不固定参数个数函数的装饰器
这个时候我们就需要用到*args
来代替业务函数的逻辑参数。具体使用如下:
1 | def record_logging(func): |
执行结果:
1 | func is running... |
当业务函数参数较多或不固定时,只需要将函数wrapper
的参数设置为*args
即可。当业务函数中包含关键字参数如None
时,设置函数wrapper
接收**kwargs
即可。
装饰器带有参数
带有参数的装饰器灵活度更高,其相当于与将装饰器再封装了成函数。例如,在查询函数执行日志时,不同的ID有不同的查询权限,具体如下的例子:
1 | #带参数的装饰器 |
执行结果:
1 | 权限太低,没法儿查询 |
函数装饰器就介绍完了。前面我们说过还有一种装饰器是类装饰器,它是通过类的__call__
函数实现的,其具体见如下代码
1 | class Decorator(object): |
执行结果:
1 | A class decorator is running... |
这是一个简单的类装饰器,可以看到装饰器的本质还是体现在函数__call__
上面(包括参数的传递等),可仔细体会其与函数装饰器之间的异同。
python中内置装饰器
在python中常见的内置装饰器有
- @property 把方法变成属性调用
- @staticmethod 静态方法,用来修饰类中的方法,可以使类方法像字段属性一样调用。
- @classmethod 类方法,与@staticmethod类似,@classmethod需要将class传入被修饰的方法中。
具体的使用方法可以查看官方文档。
总结
本文仔细梳理了python中的装饰器工作原理,以及不同装饰器的实现方法,简单介绍了类装饰器。本文作为python装饰器学习之后的一个笔记,记录在此,以备后查!