小白发呆的看着窗外,同事们陆陆续续的地来到公司,想起算法,小白就飘飘然了。突然后面传来一声呼唤,原来是小刘!
小刘:不好意思啊!堵车了,就来晚了点,不耽误你的时间,咱们就开启的今天的培训内容吧!
小白连忙说:没事,可以开始啦!
函数内存与执行函数
小刘:那我给你看一段代码,你看看会得到什么结果
def f1():
return 'F1' x = f1()
print(x) x2 = f1
print(x2)
小白看了看,很快说出了x的输出值为‘F1’,但是看到x2这里,小白就有点想不通,小白就执行了一遍
小白还是看不懂,就问小刘这x2到底打印的是什么玩意啊?
小刘笑了笑:这可神奇了,叫做函数内存地址,看着哈,我给这这玩意加点东西
def f1():
return 'F1' x = f1()
print(x)
x2 = f1
print(x2)
ret = x2()
print(ret)
小刘:我只是把f1赋值后的x2加了个括号,最后的结果和f1函数执行的结果一样,神奇吧?
小刘:这是我画的原理图,你看看
小白若有所悟的点点头,那我这样这样做,结果那是一样一样的?
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)
小白手停不下来,又开始画起来了
小白连忙答道:执行了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()去掉试试看?
小白对得出结果也表示惊奇,连摇头表示不懂。
小刘给了一张图给小白看
小白看到图后,才想到装饰器等同就是执行了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的功能根本没有执行,小白一脸疑惑!
小刘:小白!你来看看这张图
小刘:你看,我们应该怎么改进,让其执行原来tv函数呢?
小白看着图想了想,不应该return tv内存地址,应该在那写成tv()执行函数
小刘:就按你说的,试一下。
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()
小刘:不错哦!实现了哦!
给装饰器加参数
小刘:业务部门调用函数是不是会给装饰器传参数?那如果要加参数应该加到哪呢?
小白想了想
小刘看着小白画的图,哈哈大笑!悟性高!
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.装饰器的封装是从里向外的
小白:好!没问题!小刘!你辛苦了
小刘:应该的!那回去吧!
欢迎大家对我的博客内容提出质疑和提问!谢谢
笔者:拍省先生