Python 装饰器(Decorator)
引入
如果你学过Java的UML设计模式,那么你一定对Decorator Pattern和你熟悉,Decorator Pattern即装饰器模式(也译修饰器模式),是著名的四人帮(Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. 设计模式:可复用面向对象软件的基础. 北京: 机械工业出版社)书中介绍的23种设计模式之一。
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.[1] The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.[2] The decorator pattern is structurally identical to the chain of responsibility pattern, the difference being that in a chain of responsibility, exactly one of the classes handles the request, while for the decorator, all classes handle the request.
提示:在面向对象的概念中,行为是抽象的说法,体现在代码上的就是方法(函数)。 简述:在面向对象编程中,装饰器模式是一种允许动态或者静态向一个对象添加新的行为而不会影响到其他对象的一种设计模式。
Python 装饰器(Decorator)
Python includes a more natural way of decorating a function by using an annotation on the function that is decorated. https://en.wikipedia.org/wiki/Decorator_pattern 译:Python提供了一种更为自然的方式来装饰一个函数,这种方法通过在被装饰的函数前加一个注释来时实现。
Python提供了装饰器(Decorator)来更为简便的实现Java的装饰模式(Decorator Pattern),其不需要在类的层面就可以实现对一个函数进行装饰,你可以将装饰理解为添加新的功能。 装饰器充分体现了Python的简洁性,因为在Java是一种设计模式,而在Python这里就只是简单的一句注释。
#下面是来自维基百科的一个例子
def benchmark(func): #定义了一个装饰器
"""
Print the seconds that a function takes to execute.
"""
from time import time
def wrapper(*args, **kwargs):
t0 = time()
res = func(*args, **kwargs)
print("function @{0} took {1:0.3f} seconds".format(func.__name__, time() - t0))
return res
return wrapper
@benchmark #声明使用装饰器
def wait_some_seconds(num_seconds = 1):
from time import sleep
sleep(num_seconds)
wait_some_seconds(1.11)
# function @wait_some_seconds took 1.11 seconds
wait_some_seconds(5)
# function @wait_some_seconds took 5.000 seconds
#结果
function @wait_some_seconds took 1.111 seconds
function @wait_some_seconds took 5.001 seconds
如何来理解:
一个装饰器就是一个以被装饰函数为参数,以wrapper函数为闭包,返回值为wrapper函数的一个函数,其特点是wrapper采用了(*args, **kwargs)即万用参数表作为参数,可以接收任何类型的参数,使用时在被装饰函数前加上@装饰器名
作为使用装饰器的声明。
被装饰的函数依然采用自己的函数名进行调用,但是一旦一个函数前有@装饰器
名这个注释,就表示其添加了装饰器中wrapper函数所具有的功能,相当于装饰器函数(被装饰函数)
即装饰器函数调用了被装饰的函数,被装饰函数作为参数传进去,所以装饰器函数可以使用被装饰函数的功能,同时又可以使用自身的功能,从而实现装饰效果。
#我们在上面的代码中加入这一条语句:__name__是python所有函数都自动封装的一个对象的属性 即函数的名字
print(wait_some_seconds.__name__)
#结果
wrapper
#这个结果也说明了Python中装饰器的实现正如上面作者个人的理解
但是这并不是我们所期望的,因为所有被装饰的函数的名字都是wrapper,我们只是想给函数添加新功能,而不想做多余的更改。
Python中封装了functools.wraps()这个方法来实现对被装饰函数的__name__
属性进行重命名,它其实也是一个装饰器,这个装饰器包含的功能是将被装饰函数的名字赋值给装饰器的wrapper的__name__
属性,从而实现了被装饰函数名字的正常显示,虽然这种正常显示是人为进行更改的。
#完全体装饰器 对维基上的Pyhton装饰器例子进行了更改
import functools #@functools.wraps()方法位置
def benchmark(func): #定义了一个装饰器
"""
Print the seconds that a function takes to execute.
"""
from time import time
@functools.wraps(func) #声明使用functools.wraps()装饰器
def wrapper(*args, **kwargs):
t0 = time()
res = func(*args, **kwargs)
print("function @{0} took {1:0.3f} seconds".format(func.__name__, time() - t0))
return res
return wrapper
@benchmark #声明使用装饰器
def wait_some_seconds(num_seconds = 1):
from time import sleep
sleep(num_seconds)
wait_some_seconds(1.11)
# function @wait_some_seconds took 1.11 seconds
wait_some_seconds(5)
# function @wait_some_seconds took 5.000 seconds
print(wait_some_seconds.__name__)
#结果
function @wait_some_seconds took 1.111 seconds
function @wait_some_seconds took 5.000 seconds
wait_some_seconds
装饰器也可以自带参数,为了把这个参数传进去,我们需要在再加一层函数嵌套,把该参数错作为最外层函数的参数。
import functools
def sample(msg):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s函数名:%s():' % (msg, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@sample('this is a sample:n')
def hello():
print('Hello, World')
hello()
#结果
this is a sample: #换行得以正确实现是因为%s格式化输出字符串 正确识别了换行符
函数名:hello():
Hello, World
参考资料(这篇廖神其实写的有点难以理解): https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000
- 基于计算机视觉和OpenCV:创建一个能够计算道路交通流量的应用
- 这或许是对小白最友好的python入门了吧——20,定义函数简单应用
- 数据库结构版本控制
- Extjs4---Cannot read property 'addCls' of null 或者 el is null 关于tab关闭后再打开不显示或者报错
- 【干货】什么?Python3.X不能输出中文?原来是编辑器geany的锅?!
- Shell 历史记录异地留痕审计与监控
- [译]WebAPI下的如何实现参数绑定
- 这或许是对小白最友好的python入门了吧——10,元组
- Extjs 在项目中碰到问题
- 网站防刷方案
- 如何正确的猜拳:反事实遗憾最小化算法
- 使用python中的Numpy进行t检验
- 实操 Web Cache
- 怎样制作RPM包
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- AndroidQ(10)黑暗模式适配的实现
- Android仿微信录音功能
- android实现截图并动画消失效果的思路详解
- 位运算判断奇偶数
- Kotlin之在Gradle中无参(no-arg)编译器插件的使用详解
- android实现常驻通知栏遇到的问题及解决办法
- Android仿微信键盘切换效果
- Android实现WIFI和GPRS网络的切换
- 在VS2010里快要疯掉的hello world
- gh0st源码分析与远控的编写(三)
- Android自定义View实现五星好评效果
- Android通过ViewModel保存数据实现多页面的数据共享功能
- Android自定义View实现炫酷进度条
- OpenSSL的VC编程 - MD5
- 详解AndroidStudio中代码重构菜单Refactor功能