zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    Bit Manipulation
    218. The Skyline Problem
    Template : Two Pointers & Hash -> String process
    239. Sliding Window Maximum
    159. Longest Substring with At Most Two Distinct Characters
    3. Longest Substring Without Repeating Characters
    137. Single Number II
    142. Linked List Cycle II
    41. First Missing Positive
    260. Single Number III
  • 原文地址:https://www.cnblogs.com/ShawSpring/p/10646506.html
Copyright © 2011-2022 走看看