未加星标

深入理解Python装饰器Python

字体大小 | |
[大数据技术 所属分类 大数据技术 | 发布者 店小二03 | 时间 | 作者 红领巾 ] 0人收藏点击收藏

深入理解Python装饰器Python
管理
深入理解Python装饰器Python
python
深入理解Python装饰器Python
安全
深入理解Python装饰器Python
编程
深入理解Python装饰器Python
自动化
深入理解Python装饰器Python
函数

作者简介

曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器、漏洞管理平台、安全SaaS平台等。


Python是一门追求优雅编程的语言,它很容易上手,也很容易写出意大利式的代码。本文将介绍如何使用Python进阶编程之装饰器,来帮助您写出更加精炼可读的代码。


全文主要分为四个部分:

第一部分:尝鲜,通过讲解一个简单的装饰器例子,让您对装饰器的用法和作用有一个初步的感性认识;


第二部分:揭开面纱,将介绍装饰器抛开语法糖的使用方法,帮助您理解装饰器的本质原理;


第三部分:趁热打铁,将介绍装饰器在工作当中的实践用法,对应介绍的retry装饰器您可直接应用到项目代码中;


第四部分:更进一步,将介绍装饰器更多的高级用法,帮助您全方位掌握装饰器。


尝鲜

我们先来看一个简单的装饰器例子。首先定义一个装饰器log:


def log(f):

def wrapper():

print "before"

f()

print "after"

return wrapper


使用装饰器log来装饰greeting函数,并调用之:

@log

def greeting():

print "Hello, World!"

greeting()


输出结果:

before

Hello, World!

after

可以看到,使用装饰器我们实现了在函数greeting前后打印调试日志。


揭开面纱

装饰器是什么?从字面意思我们大致可以推测出来,它的作用是用来装饰的。日常生活中,大家都见过很多装饰器,比如装饰在圣诞树上的彩纸,或者套在iPhone外面的保护壳。保护壳的存在,并不会改变iPhone内部的功能,它存在的意义,在于增强了iPhone的抗摔性能。Python中的装饰器也是一样的道理,它并不会改变被装饰对象的内部逻辑,而是通过一种无侵入的方式,让它获得一些额外的能力,比如日志记录、权限认证、失败重试等等。


Python装饰器看起来高深莫测,实际上它的实现原理非常简单。我们知道,在Python中一切皆对象,函数作为一个特殊的对象,可以作为参数传递给另外一个函数,装饰器的工作原理就是基于这一特性。装饰器的默认语法是使用@来调用,这实际上仅仅是一种语法糖。下面我们看看,不利用语法糖来怎么调用装饰器:


def greeting():

print "Hello, World!"

greeting = log(greeting)

把函数greeting作为参数传递给装饰器函数log就行了!对装饰器log来说,它接收一个函数作为入参,然后返回一个新的函数,最后再赋值给greeting标识符。这样便得到了一个增强功能的函数,而它的名字又和之前的保持一样。


趁热打铁

装饰器是一个编程利器,只需一处修改,任何被装饰的对象就可以获得额外的功能。撸起袖子,让我们来看看装饰器在编程实践中的具体应用。


我们知道,程序跑起来后,有一些因素往往是不可控的,比如网络的连通性。为了容错,我们可能会加入try-except语句来捕获异常;考虑到请求失败是有一定概率的,我们或许可以通过多次重试的策略,以达到提高成功率的目的。我们先来模拟一个non_steady函数:


import random

def non_steady():

if random.random() <= 0.5:

# 失败的概率是 0.5

raise Exception("died")

else:

# 成功的概率是 0.5

return "survived"


这个函数成功返回的概率是0.5。显然,单次调用的成功率太低,如果重试10次呢?计算一下:1 - (0.5) ^ 10,即成功的概率将提升到0.9990,相比单次调用的0.5,重试的成功率大大地提升了。


按照上面的描述,我们先通过for循环来提升调用non_steady的成功率:


def non_steady_with_retry(times=10):

for i in xrange(times):

try:

return non_steady()

except Exception as e:

if (i + 1) < times:

# 尚未达到较大重试次数,默默吞掉异常

pass

else:

# 连续重试,达到较大次数时还是发生异常,则抛出异常

raise e


提升成功率的效果达到了,但是这种实现存在几个问题:

不支持无缝升级。假如函数non_steady在代码中被调用了n次,那么这意味着你需要同时修改n个地方(将调用non_steady修改为调用non_steady_with_retry);

不支持代码复用。如果你有第二个函数non_steady1,也需要升级一下重试机制,那么这意味着同样的重试代码,你需要再重写一遍。


再试试用装饰器来提升调用non_steady的成功率。定义一个retry装饰器:


def retry(times=10):

def outer(f):

def inner(*args, **kwargs):

for i in xrange(times):

try:

return f(*args, **kwargs)

except Exception as e:

if (i + 1) < times:

pass

else:

raise e

return inner

return outer


试用一下:

import random

@retry(10)

def non_steady():

if random.random() <= 0.5:

# 失败的概率是 0.5

raise Exception("died")

else:

# 成功的概率是 0.5

return "survived"

可以看到,只要函数前面加一行代码@retry(10),即可为其升级重试机制。一处更改即可,无需处处担忧。同时,对于其他想要升级的函数,也只需要更改一个地方,同样的代码就无需重写多遍了。


更进一步

一个函数可以同时应用多个装饰器,比如下面使用两个装饰器来装饰greeting函数:

@log

@retry(10)

def greeting():

print "Hello, World!"

这段代码等价于:


def greeting():

print "Hello, World!"

temp = retry(10)(greeting)

greeting = log(temp)

可以看到,叠加的装饰器生效的顺序是从内往外的。这一点在使用的时候需要特别注意。


Java中的注解,语法和Python中的装饰器很相似,它注解的顺序,没有Python中装饰器这么严格。使用时注意区分下。

除了函数,也可以用类来定义一个装饰器:

class Log(object):

def __init__(self, f):

self.f = f

def __call__(self, *args, **kwargs):

print "before"

self.f()

print "after"


类装饰器主要是通过它的__call__方法来实现的。相比函数装饰器,类装饰器具有面向对象编程所支援的一系列特点,比如高内聚、封装性和灵活度大等优点。使用类装饰器来装饰函数:

@Log

def greeting():

print "Hello, World!"

greeting()


输出结果和使用函数装饰器一样:

before

Hello, World!

after

实际上,Python中任何callable的对象都可以用来定义装饰器。


结语

使用Python装饰器,可以让你的代码更易维护,可读性也有一定提升。相信大家在日常工作中也有碰到过很多使用装饰器的场景,欢迎留言分享!人生苦短,我用Python。


欢迎加入本站公开兴趣群

软件开发技术群

兴趣范围包括:Java,C/C++,Pythonphp,Ruby,shell等各种语言开发经验交流,各种框架使用,外包项目机会,学习、培训、跳槽等交流

QQ群:204132433


Hadoop源代码研究群

兴趣范围包括:Hadoop源代码解读,改进,优化,分布式系统场景定制,与Hadoop有关的各种开源项目,总之就是玩转Hadoop

QQ群:204050420

tags: #160,装饰,greeting,Python,def,函数,non,steady,print,retry,重试,log,return,World
分页:12
转载请注明
本文标题:深入理解Python装饰器Python
本站链接:http://www.codesec.net/view/570371.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 大数据技术 | 评论(0) | 阅读(92)