总目录:https://www.jianshu.com/p/e406a9bc93a9
Python - 子目录:https://www.jianshu.com/p/50b432cb9460
装饰器三前提:作用域、高阶函数,闭包
作用域
L_E_G_B
a = 10
def f():
a=5
def inner():
a = 7
print(a)
return 1
7
高阶函数
1.函数名可以作为参数输入
2.也可以作为返回值
闭包
def outer():
x = 10
def inner(): #条件1: inner就是内部变量
print(x) #条件2:外部环境的一个变量
return inner #结论:内部函数inner是一个闭包
f=outer()
f()
10
装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
现在我们来举一个例子:
假设要写一个银行存款取款的程序,那么主干程序肯定要实现存款功能和取款功能:
def a():
print("存款中……")
def b():
print("取款中……")
button=1
if button ==1:
a()
else:
b()
但是除了存款功能和取款功能外,需要添加密码验证功能,那么菜鸡程序员肯定会添加一个新的函数:
def a():
print("存款中……")
def b():
print("取款中……")
def c():
print("密码验证中……")
button=1
if button ==1:
c()
a()
else:
c()
b()
或者:
def a():
c()
print("存款中……")
def b():
c()
print("取款中……")
def c():
print("密码验证中……")
button=1
if button ==1:
a()
else:
b()
但是这两种写法冗余程度高,都违背了开放封闭原则,这只是两个功能就要每一步都要做出修改,那么上百个功能呢?
那么我们在不改变原函数的情况下进行修改:
def a():
print("存款中……")
def b():
print("取款中……")
def c(fun):
print("密码验证中……")
fun()
button=1
if button ==1:
c(a)
else:
c(b)
这样修改确实没有修改原代码,但是程序逻辑已经改变,如果功能多的话,都要一一修改,可维护性差。
这样,就需要我们使用装饰器了,在不影响原程序,原逻辑,不违背开放封闭原则的情况下:
def c(func):
def inner():
print("密码验证中……")
func()
return inner
def a():
print("存款中……")
def b():
print("取款中……")
button=1
if button ==1:
a1=c(a)
a1()
else:
b1=c(b)
b1()
这个例子只是在说明装饰器的用途和标准用法。
带参装饰器
现在让我们摆脱这个例子,看一下带参数的装饰器,
def c(func):
def inner():
print("woshi")
func()
return inner
def my(a):
print(a)
my1=c(my("xiaobai"))
my1()
运行这个代码,会报错,原因是装饰器的返回值是inner,而my等同于inner。
现在我们修改一下
def c(func):
def inner(*a,**b):
print("woshi")
func(*a,**b)
return inner
def my(a):
print(a)
my1=c(my("xiaobai"))
my1()
现在我们来看一下,装饰器中的函数返回值:
def c(func):
def inner(*a,**b):
print("woshi")
func(*a,**b)
return inner
@c
def my1(a):
return a
@c
def my2(a):
print(a)
re1 = my1("111")
re2 = my2("222")
print(re1,re2)
woshi
woshi
222
None None
这是打印的结果,可以看出来两个函数的返回值均为空,是因为,无论被装饰的函数有无返回值,其结果都无返回值,原因其实很简单,因为inner()函数根本就没有返回值。为了实现有返回值的函数被装饰之后仍然有返回值,需要inner函数与被装饰函数的返回值保持一致。
再来简单修改一下:
def c(func):
def inner(*a,**b):
print("woshi")
re3=func(*a,**b)
return re3
return inner
@c
def my1(a):
return a
@c
def my2(a):
print(a)
re1 = my1("111")
re2 = my2("222")
print(re1,re2)
woshi
woshi
222
111 None
可以看到,有返回值的函数被装饰之后依然有返回值,没有返回值的函数被装饰之后则没有返回值,符合我们想要的结果。
语法糖
上面的@c便是语法糖。
我们来定义一个函数
def my():
print("123")
然后我们要在123上加一行=和一行*
def a(func):
def inner():
print('='*15)
func()
return inner
def b(func):
def inner():
print('*'*15)
func()
return inner
@a
@b
def my():
print("123")
my()
带参语法糖
但是这样的话,函数a和函数b代码冗余,可以用带参数的语法糖来优化一下:
def a(char):
def b(func):
def inner():
print(char*15)
func()
return inner
return b
@a('=')
@a('*')
def my():
print("123")
my()