设为首页 收藏本站
查看: 792|回复: 0

[经验分享] ZMAN的学习笔记之Python篇:装饰器

[复制链接]

尚未签到

发表于 2015-11-29 08:39:06 | 显示全部楼层 |阅读模式
  
  ZMAN的学习笔记之Python篇:
    1.装饰器
    2.函数“可变长参数”
    3.命令行解析
    
  注:本文全原创,作者:ZMAN  (http://www.cnblogs.com/zmanone/)
  
    年前工作事务比较繁琐,我只能用零碎的时间继续学习Python,决定开一个系列的博文,作为自己深入学习Python的记录吧。名字也取好了,就叫《ZMAN的学习笔记之Python篇》~开篇是关于装饰器的,春节假期码的字哈哈~就让我们开始吧!
    本文的例子都是自己想的,如果不是很合适,请大家提出宝贵意见哈~谢谢啦!
  
  一、为什么要用“装饰器”
    比如我们写了如下一段代码:



# 打印0~99
def func():
for i in range(100):
print(i)
  
    我们想要监测执行这个函数花费了多少时间,于是我们将这个函数改成了这样:
  



import time

# 打印0~99
def func():
start = time.time()
for i in range(100):
print(i)
end = time.time()
print("耗时:%.4f" % (end - start))
  
    虽然达到了目的,但是我们却改变了原有的函数,而且如果有几十个不同的函数,都这样改动一下,工作量也是非常大的。
    使用了“装饰器”后,就能在不修改原函数的前提下,达到相同的功能。
  
  二、什么是“装饰器”
    在Python中,函数是“对象”,而装饰器是函数,它的作用就是对已经存在的函数进行装饰。Python中的“装饰器”可以很好地解决那些有“面向切面编程”需求的问题。
    请看例子:



def deco(ex):
print('func函数被调用前')
ex()
print('func函数被调用后')
return ex
def func():
print('func函数被调用')
func = deco(func)

>>>
func函数被调用前
func函数被调用
func函数被调用后
  
    我写了两个函数,将函数func作为参数传入deco函数,并将返回值赋给func变量。我们可以看成是func函数经过了deco的装饰~
    而这就是装饰器的概念了:装饰器可以说是封装器,让我们在被装饰的函数之前或之后执行一些代码,而不必修改函数本身。利用这点,我们可以做出许多酷炫的功能~
  
  三、写第一个“装饰器”
    刚才我介绍了装饰器的概念,但这是一个“手工”的装饰器,并没有用到Python的装饰器语法,实际上,装饰器语法非常简单,看例子:



def deco(ex):
def _deco():
print('func函数被调用前')
ex()
print('func函数被调用后')
return _deco

@deco
def func():
print('func函数被调用')
#return('OK')

func()

>>>
func函数被调用前
func函数被调用
func函数被调用后
  
  大家可以看到,我在定义函数func的上一行,加了一句“@deco”,这就是装饰器语法了,这样写了之后,能确保每次调用func函数都被deco函数装饰,是不是非常简单呀~~
  
  四、让被装饰函数带上确定的参数
    如果被装饰函数带可以确定的参数,需要像下面这样对装饰器函数进行修改:



def deco(ex):
def _deco(a, b):
print('%s函数被调用前' % ex.__name__)
c = ex(a, b)
print('%s函数被调用后,结果为:%s' % (ex.__name__, c))
return _deco

@deco
def func(a, b):
print('func函数被调用,传入%s,%s' % (a, b))
return a+b

func(1, 2)

>>>
func函数被调用前
func函数被调用,传入1,2
func函数被调用后,结果为:3
  
  这个例子的装饰器实现了:打印传入函数的名字、打印两个数字相加结果的功能。我们在原先的deco函数内又定义了一个函数_deco用来接收func函数中的参数。
  
  五、让被装饰函数带上不确定的参数



def deco(ex):
def _deco(*args, **kwargs):
print('%s函数被调用前' % ex.__name__)
c = ex(*args, **kwargs)
print('%s函数被调用后,结果为%s' % (ex.__name__, c))
return _deco

@deco
def func(a, b):
print('func函数被调用,传入%s,%s' % (a, b))
return a+b

@deco
def func2(a, b, c):
print('func2函数被调用,传入%s,%s,%s' % (a, b, c))
return a+b+c              

func(1, 2)
func2(1, 2, 3)

>>>
func函数被调用前
func函数被调用,传入1,2
func函数被调用后,结果为3
func2函数被调用前
func2函数被调用,传入1,2,3
func2函数被调用后,结果为6
  
    简单修改我们的代码,使用*args, **kwargs来捕捉不定量的传参,便实现了多个参数的求和。
  
  六、让装饰器带上参数



def deco(ex):
def _deco(func):
def _deco2():
print('%s函数被调用前,传入参数为:%s' % (func.__name__, ex))
func()
print('%s函数被调用后' % func.__name__)
return _deco2
return _deco

@deco('parameter1')
def func():
print('func函数被调用')

func()

>>>
func函数被调用前,传入参数为:parameter1
func函数被调用
func函数被调用后
  
    如果要让装饰器带上参数,我们要在装饰器函数内部再多定义一层函数,用来接收装饰器的参数~大家可不要搞混了装饰器参数和函数的参数哟~
  
  七、来个任性的:装饰器和被装饰函数都带参数



def deco(ex):
def _deco(func):
def _deco2(c, d):
print('%s函数被调用前,装饰器参数为:%s' % (func.__name__, ex))
x = func(c, d)
if x > 3:
x = x-ex
else:
x = x+ex
print('%s函数被调用后,计算结果为:%d\n' % (func.__name__, x))
return _deco2
return _deco

@deco(3)
def func(a, b):
print('func函数执行结果为:%s' % int(a+b))
return(a+b)

func(3, 4)
func(1, 2)

>>>
func函数被调用前,装饰器参数为:3
func函数执行结果为:7
func函数被调用后,计算结果为:4
func函数被调用前,装饰器参数为:3
func函数执行结果为:3
func函数被调用后,计算结果为:6
  
    最初的func函数只是实现两个数字的相加,经过装饰后实现了对func返回的和的大小进行了分支处理:如果“两数的和大于3”,最后结果为“两数的和减去3”,否则最后结果为“两数的和加上3”。我在这个例子中使用的是“确定”的参数,大家可以自己更改哦~
  
  八、同时使用多个装饰器
    之前的例子都是只用了一个装饰器,我们当然可以装饰多次啦~



def deco1(ex):
def _deco1(string):
print('deco1被调用前')
string = ex(string)
if 'hello' in string:
string = "You are my old friend."
else:
string = "You are my new friend."
print('deco1被调用后,%s\n' % string)
return string
return _deco1

def deco2(ex):
def _deco2(string):
print('deco2被调用前')
string = ex(string)
if 'ZMAN' in string:
string = 'hello, ' + string
else:
string = 'Is your name ' + string + '?'
print('deco2被调用后,%s' % string)
return string
return _deco2
@deco1
@deco2
def func(string):
print('func函数被调用')
return string
func('ZMAN')
deco1(deco2(func('John')))

>>>
deco1被调用前
deco2被调用前
func函数被调用
deco2被调用后,hello, ZMAN
deco1被调用后,You are my old friend.
deco1被调用前
deco2被调用前
func函数被调用
deco2被调用后,Is your name John?
deco1被调用后,You are my new friend.
  
  在这个例子中,我们主要要关注装饰器调用的先后顺序,此时func('ZMAN')和deco1(deco2(func('ZMAN')))是等同的,这个调用顺序大家一看就明白了吧~
  
  九、实际应用
    最后来个实际应用~好吧,我实在是绞尽脑汁了,写代码的时候正好在吃苹果,那就来个跟水果有关的实例吧(别打我 - -!)



#综合运用:简单地检测函数传参是否合法
def deco(ex):
def _deco(*args, **kwargs):
print('***%s函数被调用前***' % ex.__name__)
if args:
if not isinstance(args[0], str):
print('★店名参数错误:%s' % args[0])
if not(isinstance(args[1], int) and args[1]>0):
print('★员工参数错误:%s' % args[1])
else:
print('★未传入店名和员工数信息!')
if kwargs:
for i in kwargs:
if not ((isinstance(kwargs, int)
or isinstance(kwargs, float))
and kwargs>0):
print('★水果单价参数错误:%s:%r' % (i, kwargs))
else:
print('★未传入水果单价信息!')
a = ex(*args, **kwargs)
print('***%s函数被调用后***\n' % ex.__name__)
return _deco

@deco
# 假设传入几家水果店的名称、员工数以及水果单价。店名为字符,员工数为正整数,单价为正数
def func(*args, **kwargs):
print('***函数func被调用***')
brief = args
detail = kwargs
return(brief, detail)

func('水果之家', -4, apple=3.5, strawberry=6, orange=3, cherry=8.5,)
func(123, 8, apple=3, orange=2,)
func('天然果园', 0.2, )
func()

>>>
***func函数被调用前***
★员工参数错误:-4
***函数func被调用***
***func函数被调用后***
***func函数被调用前***
★店名参数错误:123
***函数func被调用***
***func函数被调用后***
***func函数被调用前***
★员工参数错误:0.2
★未传入水果单价信息!
***函数func被调用***
***func函数被调用后***
***func函数被调用前***
★未传入店名和员工数信息!
★未传入水果单价信息!
***函数func被调用***
***func函数被调用后***
  
  代码有点长,但是只要大家耐心看,其实还是挺简单的,没有什么花里胡哨的东西。这个装饰器用来检测传参是否合法~
  
  十、小结
    第一篇洋洋洒洒那么多个例子,终于写完了!利用“装饰器”,我们无须改写原函数,就能对它进行功能扩充,比如计时、检测传参、记录日志等等。就比如我们有一把枪,我们可以给它加上消音器,又或者是刺刀…不用的时候就拿掉,还是原来的枪~~
    (本文难免有写错或不足的地方,希望大家不吝赐教哦~谢谢!)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-144769-1-1.html 上篇帖子: 《Python爬虫学习系列教程》学习笔记 下篇帖子: [python]闭包到底是什么鬼?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表