拓展原来函数功能的一种函数,它的返回值也是一个函数
在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出
Python程序是从上往下顺序执行的,碰到函数的定义代码块不会立即执行,只有等到该函数被调用时,才会执行其内部的代码块
对同一个函数定义了两次,后面的定义会覆盖前面的定义
def outer(func):
def inner():
print("内层函数!")
return inner
def foo():
print("原始函数!")
outer(foo)
outer(foo())
函数名: foo、outer、inner
函数体:函数的整个代码结构
返回值: return后面的表达式
函数的内存地址:id(foo)、id(outer)等等
函数名加括号:对函数进行调用,比如foo()、outer(foo)
函数名作为参数: outer(foo)中的foo本身是个函数,作为参数被传递给了outer函数
函数名加括号被当做参数:先调用函数,再将它的返回值当做别的函数的参数,例如outer(foo())
返回函数名:return inner
返回函数名加括号:return inner(),先执行inner函数,再将其返回值作为别的函数的返回值。
def funA(fn): #fnA 作为装饰器函数
fn() # 执行传入的fn参数
return '...'
@funA
def funB():
使用函数装饰器 funA() 去装饰另一个函数 funB()
其底层将 funB 作为参数传给 funA() 函数;
将 funA() 函数执行完成的返回值反馈回 funB()
def funA(fn):
fn() # 执行传入的fn参数
return '...'
def funB():
funB = funA(funB)
def funA(fn): #funA 作为装饰器函数
print("youchanwill")
fn() # 执行传入的fn参数
print("20")
return "装饰器函数的返回值"
@funA
def funB():
print("you")
print(funB)
youchanwill
you
20
装饰器函数的返回值
被“@函数”修饰的函数不再是原来的函数,而是一个新的东西(取决于装饰器的返回值)
如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名
如果装饰器返回是一个函数的名称,那么被修饰的函数名依然表示一个函数
def f1():
print("基础函数")
def f1():
#加入函数调用的安全认证
print("基础函数")
def login(): #定义认证函数
print("认证函数")
def f1():
login() #定义认证函数,在其他函数中调用
print("基础函数")
代码的开放封闭原则,已经实现的功能代码内部不允许被修改,但外部可以被扩展,就是装饰器的作用
def outer(func):
def inner():
print("认证函数")
result = func()
print("添加日志")
return result
return inner
@outer #@outer和@outer()有区别,没有括号时,outer函数依然会被执行
def f1():
print("基础函数")
1. 程序读到def outer(func):时,是把函数体加载到内存里
2. 到@outer时,知道这是装饰器,于是开始运行@后面outer所定义的函数
3. 返回到outer函数,执行装饰器的语法规则
规则
被装饰的函数的名字会被当作参数传递给装饰函数
装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数
原来的f1()函数被当做参数传递给了func,f1这个函数名之后会指向inner函数
*函数名f1(不是被调用后)当做参数传递给装饰函数outer,func = f1,@outer = outer(f1),传递了f1()的函数体,而不是执行f1()后的返回值
*outer函数return的是inner这个函数名,而不是inner()被调用后的返回值
4.程序开始执行outer函数内部的内容,到函数inner时,inner函数定义块不会立刻执行,而是读入内存中(默认规则)
5.到return inner,返回值是函数名,并且这个函数名会被赋值给f1这个被装饰的函数,也就是f1 = inner
6.f1函数被新的函数inner覆盖了(f1这个函数名更改成指向inner这个函数名指向的函数体内存地址),再调用f1的时候将执行inner函数内的代码,而不是先前的函数体
7.原来的f1()函数被当做参数传递给了func,func这个变量保存了老的函数在内存中的地址,在inner函数里的result = func()可以执行老的函数体
8.当通过f1()的方式调用f1函数时,执行的就不再是旧的f1函数的代码,而是inner函数的代码
9.在本例中,首先会print“认证成功”,然后执行func函数并将返回值赋值给变量result,func函数就是旧的f1函数
10.接着print“日志保存”,返回result这个变量,可以用r = f1()的方式接受result的值
11.在函数调用前进行认证,调用后写入日志
def outer(func):
def inner():
print("认证函数")
result = func()
print("添加日志")
return result
return inner
@outer #@outer和@outer()有区别,没有括号时,outer函数依然会被执行
def f1():
print("基础函数")
pass
r = f1() #调用函数
认证函数
基础函数
添加日志
1.执行到装饰器,将装饰器下方函数名传入装饰器作为参数,func = f1
2.inner()写入内存,return result返回inner,将inner赋值给f1,f1 = inner
3.r = f1(),这时f1() = inner(),那么执行def inner():,输出print("认证函数")
4.执行result = func(),fun = f1,那么这里执行def f1():,输出print("基础函数")
5.执行print("添加日志"),执行return result返回result原来的结果
def outer(func):
print("认证函数")
result = func()
print("添加日志")
return result
@outer
def f1():
print("基础函数")
认证函数
基础函数
添加日志
在一层函数下,当执行到装饰器的时候就会立刻执行outer函数中的内容,如果不封装,在函数还未调用的时候,就执行了
如果被修饰的函数本身带有参数,那么在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同
def funA(fn): # 定义嵌套函数
def say(name):
print("you",name)
return say
@funA
def funB(name):
print("funB():", a)
funB("chanwill")
you chanwill
通过funB()函数被装饰器funA()修饰,funB 被赋值为 say,虽然调用的是 funB() 函数,但实际执行的是say()函数
def funA(fn): # 定义一个嵌套函数
def say(name):
print("you",name)
return say
def funB(name):
print("funB():", a)
funB = funA(funB)
funB("chanwill")
当前程序中,有多个函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等
可用 *args 和 **kwargs 作为装饰器内部嵌套函数的参数
*args 和 **kwargs 表示接受任意数量和类型的参数
def funA(fn): # 定义一个嵌套函数
def say(*args,**kwargs):
fn(*args,**kwargs)
return say
@funA
def funB(name):
print("you",name)
@funA
def funC(age,name):
print(age,name)
funB("chanwill")
funC("20","CHANWILL")
you chanwill
20 CHANWILL
Python支持多个装饰器
@funA
@funB
@funC
def fun():
#...
程序的执行顺序是从里到外,等效于下面这行代码
fun = funA( funB ( funC (fun) ) )