python基础-第五篇-5.3装饰器

  小白发呆的看着窗外,同事们陆陆续续的地来到公司,想起算法,小白就飘飘然了。突然后面传来一声呼唤,原来是小刘!

  小刘:不好意思啊!堵车了,就来晚了点,不耽误你的时间,咱们就开启的今天的培训内容吧!

  小白连忙说:没事,可以开始啦!

函数内存与执行函数

  小刘:那我给你看一段代码,你看看会得到什么结果

def f1():
return 'F1' x = f1()
print(x) x2 = f1
print(x2)

  小白看了看,很快说出了x的输出值为‘F1’,但是看到x2这里,小白就有点想不通,小白就执行了一遍

  python基础-第五篇-5.3装饰器

  小白还是看不懂,就问小刘这x2到底打印的是什么玩意啊?

  小刘笑了笑:这可神奇了,叫做函数内存地址,看着哈,我给这这玩意加点东西

def f1():
return 'F1' x = f1()
print(x)
x2 = f1
print(x2)
ret = x2()
print(ret)

  小刘:我只是把f1赋值后的x2加了个括号,最后的结果和f1函数执行的结果一样,神奇吧?

   小刘:这是我画的原理图,你看看

  python基础-第五篇-5.3装饰器

  小白若有所悟的点点头,那我这样这样做,结果那是一样一样的?

def f1():
return 'F1' x = f1()
print(x)
x2 = f1
ret = x2()
print(ret)
x3 = x2
ret2 = x3()
print(ret2)
x4 = f1
ret3 = x4()
print(ret3)

   小刘:是的,你只要记住,函数名加括号就是执行函数,没加就是函数内存地址就可以了

  小白点点头

当函数名也来做函数参数时

  小刘:既然函数的内存地址都能赋给多个变量,那它做可以做为函数参数传入到函数体中去么??

  小白含糊其辞地回来了个:应该可以吧

  小刘:先给看个例子吧,你先想一下结果什么?

def login(func):
print('passed user verification...')
return func def tv():
print('welcome [%s] to tv page') tv = login(tv)

  小白手停不下来,又开始画起来了

  python基础-第五篇-5.3装饰器

  小白连忙答道:执行了login函数,打印了‘passed..ver...’,tv这个函数名指向的内存地址没有变并且tv函数不执行。

  小白回答到这,又不由说到:走这么一个大弯,回来还是原样子,这个做难道有很大用处??

  小刘微微一笑:你有这样的疑问!很正常,那我直接告诉你它的用处吧,在不修改源码的情况,可以增加额外的功能,比如说在基本功能上我要加个用户权限管理,login函数就是对这个进行验证的,那问你--如果上面要执行tv函数,你怎么做?

  小白:简单,在下面加个调用函数--tv()

  小刘点点头,那我们回到刚才那个案例上来,假如你是那基层平台的管理者,你觉得这样添加功能,你满意吗?

  小白:满意!不修改源码下实现了额外的功能!

  小刘:如果是我,我还是不满意,因为我要为其他的业务部门人员使用平台而考虑,以前他们只要调用tv()就行,现在要先写tv = login(tv),再写tv()才达到自己的目的,你不觉得这样添加了他们的负担吗?

  小白:是啊,我怎么没想到呢?

  小刘:好!冲着这点,我给到你这个方案,你看看

def login(func):
print('passed user verification...')
return func @login
def tv():
print('welcome [%s] to tv page') tv()

  小刘:那我也明确告诉你,@login的作用等同于tv=login(tv),你看最终会得出什么结果?

  小白:既然@有这种效果,那打印passed user verification...  welcome [%s] to tv page这两句

  小刘:嗯!而且我告诉你这玩意叫做装饰器,作用就是把装饰器下面的函数--函数名当参数传给装饰器函数,不过刚才我给你这个方案还一个弊端,你把执行tv()去掉试试看?

  小白对得出结果也表示惊奇,连摇头表示不懂。

  小刘给了一张图给小白看

  python基础-第五篇-5.3装饰器

  小白看到图后,才想到装饰器等同就是执行了login函数

  小白:那我们应该怎么处理这种弊端呢?

  小刘:那我问问你,python解释器遇定义函数是什么情况?

  小白:把函数刷到内存里,不执行

  小刘:好!那我们就利用这一点,给那个装饰器函数里在嵌套一个函数!  

  小白好像有些恍然大悟,但还是不知道怎么做

  小刘:小白!我给你看这个例子

def login(func):
def inner():
print('passed user verification...')
return func
return inner @login
def tv():
print('welcome [%s] to tv page')

  小刘;你执行一下,看达到我们的效果没有?

  小白欢喜的回到:我们做到了

  小刘:别告诉的太早了,还有问题,你执行一下看看,加个tv()

  小白加了tv()执行后惊讶地发现,真正tv的功能根本没有执行,小白一脸疑惑!

  小刘:小白!你来看看这张图

python基础-第五篇-5.3装饰器

  小刘:你看,我们应该怎么改进,让其执行原来tv函数呢?

  小白看着图想了想,不应该return tv内存地址,应该在那写成tv()执行函数

  小刘:就按你说的,试一下。

  python基础-第五篇-5.3装饰器

def login(func):  #tv
def inner():
print('passed user verification...')
func()
return inner @login #tv = login(tv)
def tv():
print('welcome [%s] to tv page') tv()

  小刘:不错哦!实现了哦!

给装饰器加参数

  小刘:业务部门调用函数是不是会给装饰器传参数?那如果要加参数应该加到哪呢?

  小白想了想

  python基础-第五篇-5.3装饰器

  小刘看着小白画的图,哈哈大笑!悟性高!

def login(func):  #tv
def inner(arg1,arg2):
print('passed user verification...')
func(arg1,arg2)
return inner @login #tv = login(tv)
def tv(arg1,arg2):
print('welcome [%s] to [%s] tv page'%(arg1,arg2)) tv('alex','江西卫视')

  小刘:小白!是不是有不同的业务用我们的平台,那他们是不是有可能往装饰器里传的参数个数都有可能不同?

  小白:嗯嗯!

  小刘:那怎么解决这个需求呢?

  小白想了又想,支支吾吾的回答了个:难道要用函数里的动态参数??

  小刘:没错!那我们来看看怎么改进吧

def login(func):  #tv
def inner(*args,**kwargs):
print('passed user verification...')
func(*args,**kwargs)
return inner @login #movie = login(movie)
def movie(arg1):
print('welcome [%s] to movie page'%arg1) @login #tv = login(tv)
def tv(arg1,arg2):
print('welcome [%s] to [%s] tv page'%(arg1,arg2)) tv('alex','江西卫视')
print('*'*25)
movie('eric') 结果为:
passed user verification...
welcome [alex] to [江西卫视] tv page
*************************
passed user verification...
welcome [eric] to movie page

  小白:实现了!

  小白乐地得都合不上嘴了!

  小刘:好了!装饰器大部分内容给你讲完了,最后剩下那点内容就算你回家的任务吧,就是多个装饰器运行流程,以你的领悟能力,应该没问题。

  小刘:另外就多个装饰器,我总结了两句话,在执行过程中,你结合这两句话去理解:

                          1.多个装饰器在调用时,从外向里

                    2.装饰器的封装是从里向外的

  小白:好!没问题!小刘!你辛苦了

  小刘:应该的!那回去吧!

欢迎大家对我的博客内容提出质疑和提问!谢谢

                                                                             笔者:拍省先生

上一篇:Winform控件重写


下一篇:JS继承方式详解