Python学习笔记-Day60-装饰器
今日内容:
一、函数名的应用
二、闭包
三 、装饰器
四、装饰器进阶
一、函数名的应用
1、函数名可以当作变量赋值给另一个变量
2、函数名可以作为参数
3、函数名可以作为返回值
4、函数名可以作为容器中的元素
二、闭包
1、闭包的灵魂三问
①
def func1(): name = '张三' def func2(arg): print(arg) func2(name) func1()
②
def func1(): name = '张三' def func2(): print(name) # 能够访问外层作用域的变量 func2() func1()
③
def func1(name): # 函数的形参相当于在函数内部定义了一个变量 def func2(): print(name) # 能够访问到外层作用域的变量 func2() func1('张三')
2、什么是闭包?
闭包就是内层函数调用外层函数的(非全局的)变量,这个内层函数就是一个闭包;在Python中,可以用__closure__来检测一个函数是否是闭包
3、闭包的作用
如果Python中的一个函数,引用了外层函数的变量,那么这个变量不会随着外层函数的结束而被销毁,而是会被保留在内存中。
也就是说,闭包可以保留变量的使用
4、如何在函数的外部调用内层函数?
将内层函数名作为外层函数的返回值
def wrapper(): name='张三'
def inner(): print(name) return inner#把内部函数当作返回值返回
ret=wrapper()#把返回值赋值给变量ret,ret=inner ret()#就可以调用内部函数,相当于inner()
三、装饰器的定义
1、装饰器的来历
软件设计原则:开闭原则(开放封闭原则)
对扩展代码的功能是开放的,但是对修改源代码是封闭的,这样的软件设计能更好的开发和维护我们的代码
2、装饰器的作用
在不改变函数的结构和调用方式的基础上,动态的给函数添加功能
例如:我们自己写的给不改变函数的调用方式,给create_people添加功能
def create_people(): # 原来的函数 print('女娲造人啦!!') def a(func): # 加了功能的函数 def b(): print('加水') func() return b create_people = a(create_people) # 给create_people重新赋值,相当于create_people = b create_people() # 相当于执行了b函数 # 最后的结果是: # 加水 # 女娲造人啦!!
3、装饰器语法糖
利用装饰器,可以简化上面的代码
def wrapper(func): # 装饰器 def inner(): print('加水') func() return inner @wrapper def create_people(): # 原来的函数 print('女娲造人啦!!') create_people() # 装饰之后的函数 # 最终的结果还是: # 加水 # 女娲造人啦!!
@wrapper的作用是:
① 将create_people作为参数传给wrapper函数
② 将inner函数赋值给create_people函数
四、装饰器的进阶
1、装饰带返回值的函数
就是在装饰器的inner函数中获取原函数的返回值,并在inner函数最后将其作为返回值返回
def wrapper(func): def inner(): print('这是新功能') r = func() # 获取func原来的返回值 return r # 返回func原来的返回值 return inner @wrapper def f1(): return '嘿嘿嘿' ret = f1() # 执行了新功能 print(ret) # 返回了原来的f1函数的返回值"嘿嘿嘿"
2、装饰带参数的函数
让装饰器函数中的inner函数带上和被装饰函数一样的参数,通用的写法是inner(*args,**kwargs)
def wrapper(func): def inner(x,y): print('这是新功能') func(x,y) return inner @wrapper() def f1(x,y): print("{}+{}={}".format(x,y,x+y)) # f1(10,20) # 这是新功能 10+20=30
3、带参数的装饰器
有时候,我们需要为我们的装饰器传递参数
我们使用装饰器是直接@wrapper,没有可以加参数的地方;
此时我们可以在原来的装饰器的外层再定义一个函数用来传参,并在最后返回原来的装饰器函数名。
def d(a=None): # d是新添加的最外层函数,为我们原来的装饰器传递参数,a就是要传递的参数 def wrapper(func): # wrapper 就是我们原来的装饰器函数,func是被装饰的函数 def inner(*args,**kwargs): #args和kwargs是被装饰函数的参数 if a: print('欢迎来到{}页面'.format(a)) else: print('欢迎来到首页') func(*args,**kwargs) # 调用被装饰的函数,接收参数 return inner return wrapper @d() # 不给装饰器传参数,使用默认的None参数 def index(name): print('hello{}'.format(name)) @d('电影') # 给装饰器函数传参数 def movie(name): print('hello{}'.format(name)) if __name__ == '__main__': index('张三') # 欢迎来到首页 hello张三 movie('张三') # 欢迎来到电影页面 hello张三
4、多个装饰器装饰同一个函数
同一个函数可以被多个装饰器装饰,此时需要注意装饰器的装饰顺序和函数的执行顺序
def wrapper1(func): print('d1') def inner1(): print('inner1') return '<i>{}</i>'.format(func()) return inner1 def wrapper2(func): print('d2') def inner2(): print('inner2') return '<b>{}</b>'.format(func()) return inner2 @wrapper1 @wrapper2 def f1(): return "Hello Andy" ret = f1() # 结果是 inner1 inner2 print(ret) # 结果是 <i><b>Hello Andy</b></i>
装饰顺序:
① f1先被wrapper2装饰,所以先打印”d2“,此时 f1 = inner2
② 然后被wrapper2装饰,打印”d1“,此时 f1 = wrapper1(inner2) = inner1
函数执行的时候:
① 函数刚开始执行的时候,f1 = inner1,所以先执行inner1函数,所以先打印inner1,此时返回值是<i>{}</i>.format(inner2())
② 执行inner2函数,打印inner返回值2,此时返回值是<b>{}</b>.format(f1())
③ 执行f1函数,返回值是”Hello Andy“,再逐一将结果返回,最终的返回值是<i><b>Hello Andy</b></i>