一、装饰器
装饰器背后的主要动机源自 python 面向对象编程。装饰器是在函数调用之上的修饰。这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。
装饰器的语法以 @
开头,接着是装饰器函数的名字和可选的参数。紧跟着装饰器声明的是被修饰的函数,和装饰函数的可选参数。
有参数和无参数的装饰器
没有参数的装饰器:
带参数的装饰器 :
现在我们知道装饰器实际就是函数。我们也知道他们接受函数对象。一般说来,当你包装一个函数的时候,你可以在包装的环境下在合适的时机调用这个函数。我们在执行函数之前,可以运行些预备代码,如 post-morrem 分析,也可以在执行代码之后做些清理工作。
你可以考虑在装饰器中置入通用功能的代码来降低程序复杂度。例如,可以用装饰器来:
- 引入日志
- 增加计时逻辑来检测性能
- 给函数加入事务的能力
对于用 python 创建企业级应用,支持装饰器的特性是非常重要的。
二、装饰器的原理探析
我们可以跟着下面的例子更深层次的理解装饰器的原理。
当你要在一个函数A之前或者之后增加一些操作的话,我们可以定义一个新的函数B,在函数B中定义这些操作,并在指定的位置调用函数A。这样我们就等到了一个新的函数对象,他封装的对函数A执行的额外的操作。并且可以把B函数名赋值给其他变量。那怎样将func和B绑定到一个作用域呢?让他们绑定到一起成为一个新的函数对象返回。要知道上面的例子中b和b2都是指向了统一个对象B,所以他们的调用结果才都是一样的。
这时我们可以利用嵌套函数的一个特点,就是 在嵌套函数中,将内层函数对象作为返回值返回的话,内层函数对象可以保留其所在的作用域(外层函数的作用域),也就是外层函数中定义的一切变量都可以跟随内层函数得到保留。 利用这个特性,因为形参也处于函数作用域中,所以我们让外层函数来接受参数,当外层函数调用结束后,返回的内层函数还处于一个封闭的作用域中,并可以使用外层函数的参数。
按照上面的思路,我们可以在函数B的外层再封装一层函数C,让外层的函数C来接受参数,而函数B不用自己传入参数,直接使用外层的函数C就可以了。这样我们可以在外层的函数C中直接将使用了外层函数C参数的函数B对象返回。这样我们把A对象作为参数传递给外层函数C,将其调用,但可以接受到一个绑定了形参func(这里就是A)的新函数B对象。注意这里的函数对象B和单纯定义的函数B是不一样的,因为他和参数func绑定在了一起,是一个新的函数对象。所以上面b和b2是两个各自独立的函数对象。
装饰器就是为了解决上面的问题而存在的,我们使用@符号为函数加上装饰器。当然我们函数A本身也是可以有参数的,那么在函数C中我们最后返回的函数B对象也应该定义对应的参数才行,那么为了为了通用性,我们可以使用可扩展参数,也就是在B函数中只定义 *args 和 kwargs, args用来接收所有的位置参数,kwargs用来节后所有的关键字参数。在B的代码中,我们再将其解包以args 和 **kwargs 的形式传给函数func 的调用。
在函数C外面再嵌套一层函数的方法, 不仅是因为我们上面说的将B需要的参数和func 变量的作用域隔离开来。这里,我们再分析一下装饰器的语法,装饰器是在需要改造的函数的上面加一个@符号后面跟一个我们定义的改造函数名。这个@加函数名的语法其实跟小括号一样,会直接调用这个函数,并把下面装饰的函数作为参数传入。那么我们要给装饰器传入参数时,需要使用小括号,那么小括号也是执行函数的表达式。所以这个装饰器函数在匹配上小括号和@符号时要被执行两次。而且是小括号先执行。所以我们必须在装饰器函数中进行两次嵌套。这样小括号表达式执行了函数后将要返回一个函数对象C,C函数对象和@符号匹配将下面装饰的函数A作为参数传入,再次执行并返回一个新的函数对象B,并赋值给下面函数同名的变量名A。
注意@符号和() 都是函数调用语法。给一个函数加上装饰器,Python解释器会直接执行装饰器函数。最后生成一个新的函数对象,也就是上面例子中的A。
《Python基础手册》系列:
Python基础手册 1 —— Python语言介绍
Python基础手册 2 —— Python 环境搭建(Linux)
Python基础手册 3 —— Python解释器
Python基础手册 4 —— 文本结构
Python基础手册 5 —— 标识符和关键字
Python基础手册 6 —— 操作符
Python基础手册 7 —— 内建函数
Python基础手册 8 —— Python对象
Python基础手册 9 —— 数字类型
Python基础手册10 —— 序列(字符串)
Python基础手册11 —— 序列(元组&列表)
Python基础手册12 —— 序列(类型操作)
Python基础手册13 —— 映射(字典)
Python基础手册14 —— 集合
Python基础手册15 —— 解析
Python基础手册16 —— 文件
Python基础手册17 —— 简单语句
Python基础手册18 —— 复合语句(流程控制语句)
Python基础手册19 —— 迭代器
Python基础手册20 —— 生成器
Python基础手册21 —— 函数的定义
Python基础手册22 —— 函数的参数
Python基础手册23 —— 函数的调用
Python基础手册24 —— 函数中变量的作用域
Python基础手册25 —— 装饰器
Python基础手册26 —— 错误 & 异常
Python基础手册27 —— 模块
Python基础手册28 —— 模块的高级概念
Python基础手册29 —— 包