Python 装饰器(Decorator)
装饰器的语法为 @dec_name ,置于函数定义之前。如:
import atexit @atexit.register
def goodbye():
print('Goodbye!') print('Script end here')
atexit.register 是一个装饰器,它的作用是将被装饰的函数注册为在程序结束时执行。函数 goodbye 是被装饰的函数。
程序的运行结果是:
Script end here
Goodbye!
可见函数 goodbye 在程序结束后被自动调用。
另一个常见的例子是 @property ,装饰类的成员函数,将其转换为一个描述符。
class Foo:
@property
def attr(self):
print('attr called')
return 'attr value' foo = Foo()
等价语法
语句块
@atexit.register
def goodbye():
print('Goodbye!')
等价于
def goodbye():
print('Goodbye!')
goodbye = atexit.register(goodbye)
这两种写法在作用上完全等价。
从第二种写法,更容易看出装饰器的原理。装饰器实际上是一个函数(或callable),其输入、返回值为:
| 说明 | 示例中的对应 | |
|---|---|---|
| 输入 | 被装饰的函数 | goodbye |
| 返回值 | 变换后的函数或任意对象 |
返回值会被赋值给原来指向输入函数的变量,如示例中的 goodbye 。此时变量 goodbye 将指向装饰器的返回值,而不是原来的函数定义。返回值一般为一个函数,这个函数是在输入参数函数添加了一些额外操作的版本。
如下面这个装饰器对原始函数添加了一个操作:每次调用这个函数时,打印函数的输入参数及返回值。
def trace(func):
def wrapper(*args, **kwargs): 1
print('Enter. Args: %s, kwargs: %s' % (args, kwargs)) 2
rv = func(*args, **kwargs) 3
print('Exit. Return value: %s' % (rv)) 4
return rv return wrapper @trace
def area(height, width):
print('area called')
return height * width area(2, 3) 5
- 1 :定义一个新函数,这个函数将作为装饰器的返回值,来替换原函数
- 2, 4 : 打印输入参数、返回值。这是这个装饰器所定义的操作
- 3 :调用原函数
- 5 :此时
area实际上是 1 处定义的wrapper函数
程序的运行结果为:
Enter. Args: (2, 3), kwargs: {}
area called
Exit. Return value: 6
如果不使用装饰器,则必须将以上打印输入参数及返回值的语句直接写在 area 函数里,如:
def area(height, width):
print('Enter. Args: %s, %s' % (height, width))
print('area called')
rv = height * width
print('Exit. Return value: %s' % (rv))
return rv area(2, 3)
程序的运行结果与使用装饰器时相同。但使用装饰器的好处为:
- 打印输入参数及返回值这个操作可被重用
如对于一个新的函数
foo,装饰器trace可以直接拿来使用,而无须在函数内部重复写两条print语句。@trace
def foo(val):
return 'return value'一个装饰器实际上定义了一种可重复使用的操作
- 函数的功能更单纯
area函数的功能是计算面积,而调试语句与其功能无关。使用装饰器可以将与函数功能无关的语句提取出来。 因此函数可以写地更小。使用装饰器,相当于将两个小函数组合起来,组成功能更强大的函数
修正名称
以上例子中有一个缺陷,函数 area 被 trace 装饰后,其名称变为 wrapper ,而非 area 。 print(area) 的结果为:
<function wrapper at 0x10df45668>
wrapper 这个名称来源于 trace 中定义的 wrapper 函数。
可以通过 functools.wraps 来修正这个问题。
from functools import wraps def trace(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('Enter. Args: %s, kwargs: %s' % (args, kwargs))
rv = func(*args, **kwargs)
print('Exit. Return value: %s' % (rv))
return rv return wrapper @trace
def area(height, width):
print('area called')
return height * width
即使用 functools.wraps 来装饰 wrapper 。此时 print(area) 的结果为:
<function area at 0x10e8371b8>
函数的名称能够正确显示。
接收参数
以上例子中 trace 这个装饰器在使用时不接受参数。如果想传入参数,如传入被装饰函数的名称,可以这么做:
from functools import wraps def trace(name):
def wrapper(func):
@wraps(func)
def wrapped(*args, **kwargs):
print('Enter %s. Args: %s, kwargs: %s' % (name, args, kwargs))
rv = func(*args, **kwargs)
print('Exit %s. Return value: %s' % (name, rv))
return rv return wrapped
return wrapper @trace('area')
def area(height, width):
print('area called')
return height * width area(2, 3)
程序的运行结果为:
Enter area. Args: (2, 3), kwargs: {}
area called
Exit area. Return value: 6
将函数名称传入后,在日志同时打印出函数名,日志更加清晰。
@trace('area') 是如何工作的?
这里其实包含了两个步骤。 @trace('area') 等价于:
dec = trace('area')
@dec
def area(height, width): ...
即先触发函数调用 trace('area') ,得到一个返回值,这个返回值为 wrapper 函数。 而这个函数才是真正的装饰器,然后使用这个装饰器装饰函数。
多重装饰器
装饰器可以叠加使用,如:
@dec1
@dec2
def foo():pass
等价的代码为:
def foo():pass
foo = dec2(foo)
foo = dec1(foo)
即装饰器依次装饰函数,靠近函数定义的装饰器优先。相当于串联起来。
如果你一路读到这里,我相信你已经掌握了关于Python 装饰器80%的知识,并能够应用到工作学习中。你知道了装饰器的工作原理,以及如何自己编写一个装饰器,及避免常见的编写错误。
如果你仍然有疑问,欢迎留言讨论!(如果你觉得这篇文章有用,请点下方推荐按钮:p)
Python 装饰器(Decorator)的更多相关文章
- python 装饰器(decorator)
装饰器(decorator) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语 ...
- Python装饰器--decorator
装饰器 装饰器实质是一个函数,其作用就是在不改动其它函数代码的情况下,增加一些功能.如果我们需要打印函数调用前后日志,可以这么做 def log(func): print('%s is running ...
- Python 装饰器Decorator(一)
(一) 装饰器基础知识 什么是Python装饰器?Python里装饰器是一个可调用的对象(函数),其参数是另一个函数(被装饰的函数) 假如有一个名字为somedecorator的装饰器,target是 ...
- Python装饰器(Decorator)简介
Python有许多出色的语言特性,装饰器(Decorator)便是其中一朵奇葩.先来看看一段代码: def deco1(f): print 'decorate 1' return f def deco ...
- Python 装饰器Decorator(二)
对于上一篇“”Python闭包“”随笔中提到的make_averager()函数的如下实现,我们把历史值保存在列表里,每次计算平均值都需要重新求和,当历史值较多时,需要占用比较多的空间并且效率也不高. ...
- python 装饰器学习(decorator)
最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...
- python语法32[装饰器decorator](转)
一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...
- python 语法之 装饰器decorator
装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...
- python函数编程-装饰器decorator
函数是个对象,并且可以赋值给一个变量,通过变量也能调用该函数: >>> def now(): ... print('2017-12-28') ... >>> l = ...
- Python——装饰器(Decorator)
1.什么是装饰器? 装饰器放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上.和这个函数绑定在一起.在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶 ...
随机推荐
- 敏捷测试(2)--ATDD概念
什么是验收测试驱动开发 在准备实施一个功能或特性之前,首先团队需要定义出期望的质量标准和验收细则,以明确而且达成共识的验收测试计划(包含一系列测试场景)来驱动开发人员的TDD实践和测试人员的测试脚本开 ...
- Android进程通信之一:两种序列化方式
2月下旬辞职了,去海南度假到现在,领略了一把三亚风情也算任性和 然而这样任性带来的后果就是..不行了我必须吐槽一句.. 没毕业的找工作就这么难嘛!投了57家一家面试机会都没有,好歹给个面试机会啊!!本 ...
- python发送post请求
urllib2.urlopen() urlib2是使用各种协议完成打开url的一个扩展包.最简单的使用方式是调用urlopen方法,比如 def urlopen(url, data=None, tim ...
- (转载)SQL Server2008附加数据库之后显示为只读时解决方法
SQL Server2008附加数据库之后显示为只读时解决方法 啰嗦的话就不多说了,直入主题吧! 方案一: 碰到这中情况一般是使用的sa账户登录的,只要改为Windows身份验证,再附加数据库即可搞定 ...
- Oracle EBS 重新编译无效对象 invalid object
1. 查看数据库中的无效对象 check oracle object SQL> select count(*) from dba_objects where status= ...
- node.js 抓取
http://blog.csdn.net/youyudehexie/article/details/11910465 http://www.tuicool.com/articles/z2YbAr ht ...
- Gradle 1.12用户指南翻译——第二十七章. Ear 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- 【Qt编程】基于QWT的曲线绘制及图例显示操作
在<QWT在QtCreator中的安装与使用>一文中,我们完成了QWT的安装,这篇文章我们讲讲基础曲线的绘制功能. 首先,我们新建一个Qt应用程序,然后一路默认即可.这时,你会发现总共有: ...
- Android Studio使用Lint进行代码检查
Android Studio目前已经更新到1.4版本,它作为Google官方推荐的IDE,功能非常强大,其中提供了一套静态代码分析工具,它可以帮助我们检查项目中存在的问题,让我们更有规范性的开发App ...
- ANDROID 中设计模式的采用--创建型模式
所谓模式就是在某一情景下解决某个问题的固定解决方案. 所有的创建型模式都是用作对象的创建或实例化的解决方案. 1 简单工厂模式 创建对象的最简单方法是使用new来创建一个对象,如果只创建一种固定 ...