python装饰器详解

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

带参数的语法糖,因为有了参数,就多需要一层包裹,相当于decorateChar("*")(myprint)

posted on 2019-04-03 04:50  ShawSpring  阅读(203)  评论(0编辑  收藏  举报

导航