装饰器的作用 —— 不想修改函数的调用方式 但是还想在原来的函数前后添加功能
原则: 开放封闭原则
开放 : 对扩展是开放的
封闭 : 对修改是封闭的
当程序使用“@ 函数”(比如函数 A )装饰另一个函数(比如函数 B )时, 实际上完成如下两
步。
看一下实例:
def funA(fn):
print('A')
fn()
return 'asdf'
'''
下面的装饰效果相当于funA(funB)
funB 将会被替换(装饰)成该语句的返回值
由于funA 函数返回fkit ,因此funB 就是asdf
'''
@funA
def funB():
print('B')
print(funB)
上面程序使用@funA 修饰 funB ,这意味着程序要完成两步操作。
① 将 funB 作为 funA()的参数,也就是上面的粗体字代码相当于执行 funA(funB)。
② 将 funB 替换成 ① 第步执行的结果, funA()执行完成后返回 asdf,因此 funB 就不再是函数,而是被替换成一个字符串。
查看运行结果:
A
B
asdf
实例 2:
def wapper(func):
def inner(*args,**kargs):
ret = func(*args,**kargs)
return ret
return inner
@wapper #qqxing = wrapper(qqxing)
def qqxing():
print(123,123)
qqxing() #实际是行inner
这个函数装饰器导致被修饰的函数变成了字符串,那么函数装饰器有什么用?
别忘记了,被修饰的函数总是被替换成@符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由于@符号所引用的函数的返回值决定一一如果@符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数。
下面师范更为复杂的函数装饰器:
def foo(fn):
def bar(*args):
print("1",args)
n = args[0]
print(2,n*(n-1))
print(fn.__name__)
fn(n*(n-1))
print("*"*15)
return fn(n*(n-1))
return bar
'''
下面的装饰效果相当于foo(my_test)
my_test 将会被替换(装饰〉成该语句的返回值
由于foo()函数返回bar 函数,因此funB 就是bar
'''
@foo
def my_test(a):
print("==3==",a)
print(my_test)
# my_test(10)
my_test(5,6)
上面程序定义了一个装饰器函数 foo ,该函数执行完成后并不是返回普通值,而是返回 bar 函数(这是关键〉,这意味着被该 @foo 修饰的函数最终都会被替换成 bar 函数。
上面程序使用@foo 修饰 my_test()函数,因此程序同样会执行 foo(my_test),并将 my_test 替换成 foo()函数的返回值: bar 函数。所以,上面程序代码 print(my_test)
在打印 my_test 函数时,实际上输出的是 bar 函数,这说明 my_test 已经被替换成 bar 函数。接下来程序两次调用 my_test()函数,实际上就是调用 bar()函数。
运行结果:
<function foo.<locals>.bar at 0x000001E2ECA52708>
1 (5, 6)
2 20
my_test
==3== 20
***************
==3== 20
通过@符号来修饰函数是 Python 的一个非常实用的功能,它既可以在被修饰函数的前面添加一些额外的处理逻辑(比如权限检查),也可以在被修饰函数的后面添加-些额外的处理逻辑( 比如记录日志),还可以在目标方法抛出异常时进行一些修复操作……这种改变不需要修改被修饰函数的代码,只要增加一个修饰即可。
总结装饰器语法:
@deco
def target():
print('running target()')
上述代码可写成:
def target():
print('running target()')
target = deco(target)
两种写法的最终结果一样:上述两个代码片段执行完毕后得到的 target 不一定是原来那个 target 函数,而是 decorate(target) 返回的函数。
装饰器通常把函数替换成另一个函数
def deco(func):
def inner():
print('running inner()')
return inner
@deco
def target():
print('running target()')
print(target())
❶ deco 返回 inner 函数对象。
❷ 使用 deco 装饰 target。
❸ 调用被装饰的 target 其实会运行 inner。
❹ 审查对象,发现 target 现在是 inner 的引用。
def auth(fn):
def auth_fn(*args):
#用一条语句模拟执行权限检查
print("---权限检查---")
#回调修饰的目标函数
fn(*args)
return auth_fn
@auth
def test(a,b):
print("执行test函数,参数a: %s,参数b: %s"%(a,b))
test(100,13)
上面程序使用@auth 修饰了 test()函数,这会使得 test()函数被替换成 auth()函数所返回的 auth_fn
函数,而 auth_fn 函数的执行流程是:
① 先执行权限检查;
② 回调被修饰的目标函数——简单来说, auth_fn 函数就为被修饰函数添加了一个权限检查的功能。
def wrapper1(func):
def inner1():
print('wrapper1 ,before func')
ret = func()
print('wrapper1 ,after func')
return ret
return inner1
def wrapper2(func):
def inner2():
print('wrapper2 ,before func')
ret = func()
print('wrapper2 ,after func')
return ret
return inner2
def wrapper3(func):
def inner3():
print('wrapper3 ,before func')
ret = func()
print('wrapper3 ,after func')
return ret
return inner3
@wrapper3
@wrapper2
@wrapper1
def f():
print('in f')
return '哈哈哈'
print(f())
返回值 L:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
哈哈哈
@ 装饰函数顺序决定执行函数顺序
可大概看下是行顺序:
大致代码执行过程: