深入理解Python装饰器


Posted in Python onJuly 27, 2016

装饰器简介:

装饰器(decorator)是一种高级Python语法。装饰器可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果。相对于其它方式,装饰器语法简单,代码可读性高。因此,装饰器在Python项目中有广泛的应用。

装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样的可调用对象(callable object,这样的对象定义有__call__方法)。在Python 2.6以及之后的Python版本中,装饰器被进一步用于加工类。

装饰器主要是用来包装函数,对于一些常用的功能,譬如:日志打印,函数计时,身份认证。我们可以使用装饰器来实现,这样可以降低整个程序的复杂度和减少程序的代码量。

它实际上就是函数,不同的是,它把一个函数当做参数,然后返回一个替代版函数。

下面看一个简单的示例:

def add_number(func):
def adder(arg):
return func(arg)+100
return adder
def f(x):
return x
f=add_number(f)
print f(20)

add_number就是一个装饰器函数,它接受一个函数(f)作为参数,然后返回另外一个函数(adder)赋值给原来的函数,这样,原来的函数不用新添加额外的代码量而实现了加法的功能。

这个就是装饰器的原始实现。

But,这种方式还是有点不太方便,毕竟还是绕了一圈,用f=add_number(f)来给原来的函数重新赋值。

其实,Python中可以用下列方式来简化对于装饰器的引用。

def add_number(func):
def adder(arg):
return func(arg)+100
return adder
@add_number
def f(x):
return x
print f(20)

只需一个简单的@add_numbe调用,是不是方便,简单了很多,基本上没侵入原来的代码。

额,大家发现没有,作为装饰器,每次接受的参数无非两种:函数和函数的参数,但书写的格式基本一样,有没有办法来简化这种书写呢?

有,Python提供了一个decorator包,可以大大简化装饰器的书写。

So,第三种实现方式为:

from decorator import decorator
@decorator
def wrapper(func,arg):
return func(arg)+100
@wrapper
def f(x):
return x
print f(20)

喔,果然更加简单了~

以上示例接受的都是一个参数,其实,函数本身是可以接受可变参数的。

如:

@decorator
def wrapper(f,arg1,*args,**kwargs):
print "I am just a wrapper~"
return f(arg1,*args,**kwargs)
@wrapper
def f(arg1,*args,**kwargs):
print arg1
for eacheArg in args:
print 'non-keyword arg:',eacheArg
for eachKw in kwargs.keys():
print 'keyword arg: %s:%d' % (eachKw,kwargs[eachKw])
args=('Joy','Steve')
kwargs={"age":20}
f('China',*args,**kwargs)

输出结果为:

I am just a wrapper~
China
non-keyword arg: Joy
non-keyword arg: Steve
keyword arg: age:20

关于*args,**kwargs的区别,两者都可用于表示可变长度的参数。只不过前者是用元祖表示,没有key值,后者是字典,有key值。两者可用于在同一个函数中,但是,*args必须出现在**kwargs之前。

譬如下例:

def test_var_args_call(arg1, arg2, arg3):
print "arg1:", arg1
print "arg2:", arg2
print "arg3:", arg3
args=(1,2,3)
kwargs ={"arg1":"1","arg3": 3, "arg2": "2"}
test_var_args_call(*args)
print '-----------------'
test_var_args_call(**kwargs)

两者的实现效果一样。

最后来个示例,通过显示函数执行的时间来装饰一个函数

import time
def log(func):
def wrapper(*args, **kw):
print '[%s] %s() was called...' % (time.ctime(),func.__name__)
return func(*args, **kw)
return wrapper
@log
def foo():
pass
for i in range(4):
foo()
time.sleep(1)

输出结果如下:

[Wed Jul 27 09:17:23 2016] foo() was called...
[Wed Jul 27 09:17:24 2016] foo() was called...
[Wed Jul 27 09:17:25 2016] foo() was called...
[Wed Jul 27 09:17:26 2016] foo() was called...

以上所述是小编给大家介绍的深入理解Python装饰器,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
Python中map和列表推导效率比较实例分析
Jun 17 Python
Python爬取网易云音乐上评论火爆的歌曲
Jan 19 Python
Python3实现的画图及加载图片动画效果示例
Jan 19 Python
python放大图片和画方格实现算法
Mar 30 Python
Python使用sort和class实现的多级排序功能示例
Aug 15 Python
Django 登陆验证码和中间件的实现
Aug 17 Python
对python3 中方法各种参数和返回值详解
Dec 15 Python
python实现趣味图片字符化
Apr 30 Python
python爬取盘搜的有效链接实现代码
Jul 20 Python
python 协程中的迭代器,生成器原理及应用实例详解
Oct 28 Python
python带参数打包exe及调用方式
Dec 21 Python
python excel和yaml文件的读取封装
Jan 12 Python
python批量制作雷达图的实现方法
Jul 26 #Python
python 添加用户设置密码并发邮件给root用户
Jul 25 #Python
Python文件夹与文件的相关操作(推荐)
Jul 25 #Python
浅谈python类属性的访问、设置和删除方法
Jul 25 #Python
python直接访问私有属性的简单方法
Jul 25 #Python
python类:class创建、数据方法属性及访问控制详解
Jul 25 #Python
python实现汉诺塔方法汇总
Jul 25 #Python
You might like
《魔兽争霸3:重制版》翻车了?你想要的我们都没有
2019/11/07 魔兽争霸
一个简易需要注册的留言版程序
2006/10/09 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
javascript 模式设计之工厂模式详细说明
2010/05/10 Javascript
JavaScript实现GriwView单列全选(自写代码)
2013/05/13 Javascript
js冒泡法和数组转换成字符串示例代码
2013/08/14 Javascript
分享9点个人认为比较重要的javascript 编程技巧
2015/04/27 Javascript
javascript实现在下拉列表中显示多级树形菜单的方法
2015/08/12 Javascript
基于Jquery easyui 选中特定的tab
2015/11/17 Javascript
基于Vuejs实现购物车功能
2016/08/02 Javascript
简洁实用的BootStrap jQuery手风琴插件
2016/08/31 Javascript
js判断文件格式及大小的简单实例(必看)
2016/10/11 Javascript
详解js中Json的语法与格式
2016/11/22 Javascript
vue.js实现简单轮播图效果
2017/10/10 Javascript
swiper 解决动态加载数据滑动失效的问题
2018/02/26 Javascript
jQuery中复合选择器简单用法示例
2018/03/31 jQuery
详解Vue.js和layui日期控件冲突问题解决办法
2019/07/25 Javascript
Vue axios 跨域请求无法带上cookie的解决
2020/09/08 Javascript
vue将文件/图片批量打包下载zip的教程
2020/10/21 Javascript
手动实现vue2.0的双向数据绑定原理详解
2021/02/06 Vue.js
python中list循环语句用法实例
2014/11/10 Python
编写Python小程序来统计测试脚本的关键字
2016/03/12 Python
python中defaultdict的用法详解
2017/06/07 Python
Python基于回溯法子集树模板解决野人与传教士问题示例
2017/09/11 Python
在Python dataframe中出生日期转化为年龄的实现方法
2018/10/20 Python
如何用python处理excel表格
2020/06/09 Python
Python自定义sorted排序实现方法详解
2020/09/18 Python
HTML最新标准HTML5总结(必看)
2016/06/13 HTML / CSS
日本必酷网络直营店:Biccamera
2019/03/23 全球购物
写一个方法,输入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数
2016/04/13 面试题
教师党员承诺书
2014/03/25 职场文书
杭州西湖英语导游词
2015/02/03 职场文书
教师学习心得体会范文
2016/01/21 职场文书
2019学子的答谢词范本!
2019/07/05 职场文书
MySQL索引失效的典型案例
2021/06/05 MySQL
Python爬虫之用Xpath获取关键标签实现自动评论盖楼抽奖(二)
2021/06/07 Python