我们利用一个生活中的场景来进入装饰器的世界!
我们模拟银行存取款的过程,代码如下:
def deposit():
print('存款中...')
def withdraw():
print('取款中...')
key = 1
if key == 1:
deposit()
else:
withdraw()
好像还不完美,我们存取款的时候必需要输入密码的,于是成了下面的样子。
def deposit():
print('存款中...')
def withdraw():
print('取款中...')
def check_password():
print('密码验证中...')
key = 1
if key == 1:
check_password()
deposit()
else:
check_password()
withdraw()
这个样子好像也不成,在if...else中每次判断都需要加上密码的验证函数,当我们的代码很多的时候,那得加得多累啊。
还有一种将check_password()函数分别放在存取款函数中,在代码量增加的时候,还是和这个一样的效果。我们的目标是尽量少些代码,是代码模块话,于是有了下面的版本。
def deposit():
print('存款中...')
def withdraw():
print('取款中...')
def check_password(func):
print('密码验证中...')
func()
key = 1
if key == 1:
check_password(deposit)
else:
check_password(withdraw)
虽然上面的代码量减少,看起来更加的完美,但是如果业务逻辑代码增加的话,我们是不是得改得天荒地老。我们需要一个方法,只要改变check_password()函数,不需要更改其他。
def deposit():
print('存款中...')
def withdraw():
print('取款中...')
def check_password(func):
def inner():
print('密码验证中...')
func()
return inner
deposit = check_password(deposit)
withdraw = check_password(withdraw)
key = 1
if key == 1:
deposit()
else:
withdraw()
当改为上面这个样子后,是不是发现我们的代码结构好了很多!这个就是装饰器的强大之处。上面的check_password()函数就是闭包了。所谓闭包就是在函数中嵌套了另一个函数,该内部函数被当作对象返回是,并且内部函数引用了外部变量,此时就形成了闭包。而check_password()就是deposit()和withdraw()的装饰器函数。
当函数不加括号,是不会被执行的,它代表的是一个函数对象,可以当作变量来传递。可以通过打印看到其返回的是一个地址。
我们利用存款函数再来分析一下调用过程!我们看到deposit 实际上就是check_password(deposit),而deposit作为参数传递给check_password,check_password的子函数inner对func返回的结果进行了一番修饰,(即加上了“密码验证中...”这句打印)返回一个装饰后的结果,最后check_password返回inner,可以说inner就是装饰后的deposit,这个就是一个函数被装饰的过程,这就是我们把check_password叫做装饰函数的原因,因为其内部对deposit进行了装饰。
python中我们还能利用语法糖,让代码更加的简洁。
def check_password(func):
def inner():
print('密码验证中...')
func()
return inner
@check_password
def deposit():
print('存款中...')
@check_password
def withdraw():
print('取款中...')
key = 1
if key == 1:
deposit()
else:
withdraw()
来一段比较官方的定义,“装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。”
def celebrator(func):
def inner(str):
print('我是新增功能')
func(str)
return inner
@celebrator
def myprint(a):
print(a)
#myprint = celebrator(myprint)
myprint('hello python!')
#output:
#我是新增功能
#hello python!
当我们被装饰的函数有参数的时候,我们的装饰器函数的inner函数也需要有参数,这样才能将被装饰的函数传入装饰器;这里我觉得可以简单的理解为我们可以把被装饰函数等同为inner函数。
我们被装饰函数的参数可能有很多,而我们的装饰器只有这一个,为了增加装饰器函数的通用型,我们需要进一步优化代码。
def celebrator(func):
def inner(*args,**kargs):
print('我是新增功能')
func(*args,**kargs)
return inner
@celebrator
def myprint(a):
print(a)
#myprint = celebrator(myprint)
myprint('hello python!')
当被装饰的函数有返回值的时候,我们在装饰器函数中也需要将被装饰的函数返回。
def celebrator(func):
def inner(*args,**kargs):
print('我是新增功能')
ret = func(*args,**kargs)
return ret
return inner
@celebrator
def myprint(a):
print(a)
@celebrator
def myprint1(b):
return b
ret1 = myprint("aaaaaaa")
ret2 = myprint1("bbbbbb")
print(ret1,ret2)
#output:
#我是新增功能
#aaaaaaa
#我是新增功能
#aaaaaaa bbbbbb
可以看到有返回值的被装饰后依然有返回值,没有返回值的函数被装饰后依然没返回值,符合我们的预期。
def printequal(func):
def inner():
print('='*15)
func()
return inner
def printstar(func):
def inner():
print('*'*15)
func()
return inner
@printequal
@printstar
def myprint():
print('hello python')
myprint()
#output:
#===============
#***************
#hello python
显然,上面这个函数还是有很大的改进空间的,明显两个打印函数都有很多重合的地方,于是,我们得到了以下的实现。
def get_celebrator(char):
def print_style(func):
def inner():
print(char*15)
func()
return inner
return print_style
@get_celebrator('=')
@get_celebrator('*')
def myprint():
print('hello python')
myprint()
https://blog.csdn.net/qq_42156420/article/details/81169554