装饰器
[TOC]
Python高级编程 第一部分 函数
第一章 装饰器
装饰器是一个用于封装函数或类代码的工具. 它显式地将封装器应用到函数或类上, 从而使它们选择加入到装饰器的功能中.
本质上说, 装饰器是一个接受可调用函数的可调用函数, 并返回一个可调用函数.
1.1 用处
在函数运行前处理常见前置条件(例如: 确认授权)
函数运行后的清理工作(例如: 输出清理或异常处理)
对于处理已经被装饰的函数或类本身, 装饰器也很有用(例如: 装饰器可以将函数注册到信号系统, 或者注册到Web应用程序的URI注册表中)
1.2 理解装饰器
究其根本, 装饰器就是一个可以接受调用也可以返回调用的调用. 装饰器无非就是一个函数(或调用, 如_call_method_方法的对象), 该函数接受被装饰的函数作为其位置参数. 装饰器通过使用该参数来执行某些操作, 然后返回原始参数或一些其他的调用(大概以这种方式与装饰器交互).
由于函数在Python中是一级对象, 因此他们能够像其他对象一样被传递到另一个函数. 装饰器就是接受另一个函数作为参数, 应用其完成一些操作的函数.
1.2.1 示例
定义
# 为被装饰的调用点字符串附加了一个字符串
# func.__doc__ 指的是函数func的docstring, 即函数注释(也并非全部)
def decorated_by(func):
func.__doc__ += '\nDecorated by decorated_by'
return func应用装饰器
查看效果
运行输出
装饰器decorated_by修改了函数add(x, y)的__doc__属性, 然后返回原来的函数对象.
1.3 装饰器语法
Python2.5为装饰器引入了特殊的语法, 在被修饰函数(不包含隐式装饰器的方法签名)声明前加上@decorate_function_name来使用相应装饰器, 示例如下.
1.3.1 装饰器应用顺序
使用装饰器语法(
@)时, 在创建被装饰的可调用函数后, 会立刻应用装饰器, 示例如下.
def decoratedby(func): func._doc += "\nDecorated by decorated_by" return func pass
@decorated_by def add(x, y): """返回x和y的和""" pass
help(add)
使用多个装饰器
通过
@语法使用多个装饰器, 应用顺序是自底而上, 示例如下.
def decoratedby(func): func._doc += "\nDecorated by decorated_by" return func pass
def alsodecoratedby(func): func.__doc += "\nDecorated by also_decorated_by" return func
@also_decorated_by @decorated_by def add(x, y): """返回x和y的和""" pass
help(add)
当装饰器应用到装饰函数时(而不是调用装饰器时), 会执行装饰代码本身.
1.4 装饰器示例
1.4.1 函数注册表
示例一
输出
示例二
示例二输出
1.4.2 执行时封装代码
1.4.2.1 一个简单的类型检查示例
下面是 一个简单的装饰器, 确保函数接受的所有参数都是整型, 否则报错.
代码
输出
说明
装饰器自身是require_ints, 它接受一个参数decorated, 即被装饰的函数, 装饰器唯一做的事情是返回一个新的可调用函数, 即本地函数inner, 该函数替代了被装饰的方法.
1.4.2 functools.wraps
Python中使用装饰器对在运行期对函数进行一些外部功能的扩展。但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,Python 通过 functool.wraps 为我们解决了这个问题:在编写装饰器时,在实现前加入 @functools.wraps(func) 可以保证装饰器不会对被装饰函数造成影响( 点击产看原文)。
Python实现了一个名为@functools.wraps的装饰器, 将一个函数的重要内部因素复制到另一个函数(将原函数对象的指定属性复制给包装函数对象, 默认有 module、name、doc,或者通过参数选择(点击查看原文)).
简单示例
代码
输出
注意和上例输出对比.
1.4.3 验证用户
用户验证是装饰器常见用例(在运行被装饰方法之前执行某种正确性检查).
代码
输出
注
1.4.4 格式化输出
示例一: 格式化输出为JSON格式
代码
输出
示例二: 格式化输出为JSON格式并且捕获异常并以输出指定的JSON格式数据
代码
输出
1.4.5 日志管理
代码
输出
注
实例中, 装饰器并不更改函数实际返回值.
1.5 装饰器参数
一个有参数的装饰器代码.
代码
输出
注
装饰器有一个隐式参数 --- 被装饰的方法.
装饰器参数和函数参数: 传递给装饰器的参数只在函数声明并被装饰时处理一次, 传递给函数的参数在该函数被调用时处理.
在封装装饰器代码的示例中, 这些封装的装饰器在局部作用域声明一个内部方法后返回. 该内部方法是实际的装饰器.
接受参数的装饰器额外增加了一层封装, 该接受参数的装饰器并不是实际的装饰器, 而是一个返回装饰器的函数.
函数能被当作装饰器使用的原因
函数调用的结果被应用到装饰器上.
首先, 解析对
json_output函数的调用然后, Python解析器对函数调用
json_output(indent=4)进行解析, 结果是返回actual_decorator, 然后返回值被应用@:这样, 就应用了真正的装饰器
actual_decorator装饰器函数和装饰器的区别
一个灵活的装饰器函数
注
装饰器函数
json_output支持下面的装饰调用如果设置了
decorated_, 将作为一个没有方法签名的纯装饰器调用, 应用最终的装饰器并返回inner函数; 首选调用并解析actual_decorator(decorated_)函数, 然后以inner作为唯一参数调用该函数的返回结果.如果没有设置
decorated_, 那么这就是带有参数关键字的调用, 并且函数返回一个实际的装饰器, 该装饰器接受被装饰的方法并返回inner函数.
1.6 装饰类
用途
与被装饰类的属性交互
添加属性或将属性参数化
修改一个类的API, 从而使被声明的方式与实例被使用的方式不同
代码
输出
说明
被该装饰类装饰的类, 可以按照实例创建时间先后排序
首先, 保存了累的原始方法
__inIT__的副本然后, 创建一个将会被赋值给
__init__的新方法
1.7 类型转换
装饰器并没有要求返回 返回类型相同的可调用函数
装饰器装饰一个函数可以返回一个类
示例
代码
输出
改进
改进代码以便使用foo()代替foo().run()来实现想要的效果.
代码
输出
说明
定义
__call__方法, 使得该类的实例可以向函数一样被调用.
最后更新于
这有帮助吗?