python 装饰器
模拟银行存款取款
def deposit():
print('存款中')
def withdraw():
print('取款中')
button=1
if button==1:
deposit()
else:
withdraw()
存款中
不对,还得加上密码验证功能
def deposit():
print('存款中...')
def withdraw():
print('取款中...')
def check_passwd():
print('密码验证中...')
button=1
if button==1:
check_passwd()
deposit()
else:
check_passwd()
withdraw()
密码验证中...
存款中...
代码冗余度高,每次进行操作都要在前面加上 check_passwd(),除了存款取款,还有查询,转账等功能,也要加上check_passwd(),代码的复用性不好
我们试着把check_passwd() 加到操作内部
def check_passwd():
print('密码验证中...')
def deposit():
check_passwd()
print('存款中...')
def withdraw():
check_passwd()
print('取款中...')
button=1
if button==1:
deposit()
else:
withdraw()
密码验证中...
存款中...
函数内部又实现另外一个函数,违背的代码的开闭原则,对扩展开放,对修改封闭.而且一个函数最好只有一个功能,即单一职责原则
为了在不改变原函数的前提下增加密码验证功能,我们把存款,全款等功能加到密码验证函数里.
def deposit():
print('存款中...')
def withdraw():
print('取款中...')
def check_passwd(func):
print('密码验证中...')
func()
button=1
if button==1:
check_passwd(deposit)
else:
check_passwd(withdraw)
密码验证中...
存款中...
原函数没有变,但是问题已经解决,还可以继续扩展新的银行业务功能,转账,查询等.
但是,业务逻辑代码已经更改, 不再是调用withdraw()了.
有没有什么办法,在不改变原函数,也不改变原函数调用方法的情况下扩展原函数的功能呢?
这就是python中的装饰器
def deposit():
print('存款中')
def withdraw():
print('取款中')
只有中间这段可以改
def check_passwd(func):
pass
button=1
if button==1:
deposit()
else:
withdraw()
def deposit():
print('存款中')
def withdraw():
print('取款中')
def check_passwd(func):
def wrapper():
print('密码验证中')
func()
return wrapper
deposit = check_passwd(deposit)
withdraw = check_passwd(withdraw)
button=1
if button==1:
deposit()
else:
withdraw()
密码验证中
存款中
check_passwd 就是 deposit 和 withdraw 的装饰器函数, 将原有的函数装饰了一下
语法糖 @
def check_passwd(func):
def wrapper():
print('密码验证中')
func()
return wrapper
@check_passwd
def deposit(): ##相当于check_passwd(deposit)
print('存款中')
@check_passwd
def withdraw():
print('取款中')
button=1
if button==1:
deposit()
else:
withdraw()
密码验证中
存款中
装饰器的有参函数
def decoration(func):
def wrapper():
print('log:%s is called'%func.__name__)
func()
return wrapper
@decoration
def greet(name):
print("hello,%s"%name)
greet('Tom')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-bd91aac010af> in <module>
9 print("hello,%s"%name)
10
---> 11 greet('Tom')
TypeError: wrapper() takes 0 positional arguments but 1 was given
被装饰的函数没有传参,表面上调用的是greet,实际上greet被装饰成了wrapper,再来改一下代码
def decoration(func):
def wrapper(name):
print('log:%s is called'%func.__name__)
func(name)
return wrapper
@decoration
def greet(name):
print("hello,%s"%name)
greet('Tom')
log:greet is called
hello,Tom
为了使装饰器能够装饰 有各种参数的函数,我们来修改代码为万金油型的传参方式
def decoration(func):
def wrapper(*args,**kw):
print('log:%s is called'%func.__name__)
func(*args,**kw)
return wrapper
@decoration
def greet(name):
print("hello,%s"%name)
@decoration
def greets(*people):
print("hello,%s"%' and '.join(people))
greet('Tom')
greets('John','Jack','Lina','Scofield')
log:greet is called
hello,Tom
log:greets is called
hello,John and Jack and Lina and Scofield
函数有返回值,又该怎么装饰呢?
def decoration(func):
def wrapper(*args,**kw):
print('log:%s is called'%func.__name__)
func(*args,**kw)
return wrapper
@decoration
def fetch(a):
return a
@decoration
def myprint(a):
print(a)
r1 = fetch(100)
r2 = myprint(100)
print(r1,r2)
log:fetch is called
log:myprint is called
100
None None
不对啊,都是None, 因为实际调用的wrapper根本没有返回值
我们还需要修改wrapper,使它的返回值与被包裹的函数一致
def decoration(func):
def wrapper(*args,**kw):
print('log:%s is called'%func.__name__)
return func(*args,**kw)
return wrapper
@decoration
def fetch(a):
return a
@decoration
def myprint(a):
print(a)
r1 = fetch(100)
r2 = myprint(100)
print(r1,r2)
log:fetch is called
log:myprint is called
100
100 None
双重语法糖 双层装饰/包裹
def decorateStar(func):
def wrapper(*args,**kw):
print("*"*10)
return func(*args,**kw)
return wrapper
def decorateEqual(func):
def wrapper(*args,**kw):
print("="*10)
return func(*args,**kw)
return wrapper
@decorateStar
@decorateEqual
def myprint(a):
print(a)
myprint(100)
**********
==========
100
相当于如下,外层的装饰器先运行
def decorateStar(func):
def wrapper(*args,**kw):
print("*"*10)
return func(*args,**kw)
return wrapper
def decorateEqual(func):
def wrapper(*args,**kw):
print("="*10)
return func(*args,**kw)
return wrapper
def myprint(a):
print(a)
decorateStar(decorateEqual(myprint))(100)
**********
==========
100
但是,上面的两次语法糖太冗余了,我们可以使用带参数的语法糖
def decorateChar(Char):
def decorate(func):
def wrapper(*args,**kw):
print(Char*10)
return func(*args,**kw)
return wrapper
return decorate
@decorateChar("*")
@decorateChar("=")
def myprint(a):
print(a)
myprint(100)
**********
==========
100