• Python装饰器的使用【面试必学】


    装饰者模式是常用的软件设计模式之一。通过此设计模式,我们能够在不修改任何底层代码情况下,给已有对象赋予新的职责。python中可以用装饰器简单地实现装饰者模式。

    PS注意:很多人学Python过程中会遇到各种烦恼问题,没有人解答容易放弃。为此小编建了个Python全栈免费答疑.裙 :七衣衣九七七巴而五(数字的谐音)转换下可以找到了,不懂的问题有老司机解决里面还有最新Python实战教程免非下,,一起相互监督共同进步!

    1.1 将函数作为参数传递

    C/C++中,函数指针可以将函数作为参数传递给另一函数。而在python中,函数也是对象的一种,函数可以被引用,也可直接作为参数传入函数,以及作为容器对象的元素。python中可以采用如下方法实现装饰者模式:

    #!/usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    
    def add(x, y):
        result = x+y
        return result
    
    def log(func):
        def wrapper(*args, **kwargs):
            result = func(*args)
            print(func.__name__,'has been called
    ')
            return result
        return wrapper
    
    if __name__ == '__main__':
        print(log(add)(1,2))
    

    上述代码中,log函数以需要被装饰的函数作为参数,并返回函数对象。被返回的函数的参数为可变参数*args**kwargs*args参数会被封装成tuple**kwargs参数则会被封装成字典对象),以适应不同函数的不同参数,保证通用性。

    1.2 装饰器

    上面的实现方法有些繁杂,所有调用被装饰的函数之处的代码,都要进行相应修改,自然不符合python简洁易读的特性。因此python中给出相应语法糖来增加可读性和易用性,那便是“装饰器”。

    #!/usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    
    from functools import wraps
    
    def log(func):
        #@wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args)
            print(func.__name__,'has been called')
            return result
        return wrapper
    
    #等价于add = log(add)
    @log
    def add(x, y):
        result = x+y
        return result
    
    if __name__ == '__main__':
        print(add(1,2))
        print(add.__name__)
    

    运行情况如下

    >>print(add(1,2))
    add has been called
    3
    >>print(add.__name__)
    wrapper
    

    但上述方法亦有缺陷,原函数add的元数据(比如名字、文档字符串、注解和参数签名)会丢失。为避免缺陷,任何时候你定义装饰器的时候,都应该使用functools库中的@wraps装饰器来注解底层包装函数(代码中注释部分)。@wraps有一个重要特征是它能让你通过属性 __wrapped__ 直接访问被包装函数。
    改进后运行情况

    >>print(add(1,2))
    add has been called
    3
    >>print(add.__name__)
    add
    

    1.3 解除装饰器

    当装饰器已经作用于某函数,而你想撤销它,那么可以访问 __wrapped__属性来访问原始函数

    orig_add = add.__wrapped__
    orig_add(1,2)
    

    但若使用了多个装饰器, __wrapped__属性会变得不可控,应尽量避免使用。
    若有如下代码:

    #!/usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    
    import functools
    import time
    
    def metric(func):
        @functools.wraps(func)
        def wrapper(*args,**kv):
            print('Decorator1')
            f = func(*args,**kv)
            return f
        return wrapper
    
    def logging(func):
        @functools.wraps(func)
        def wrapper(*args,**kv):
            print('Decorator2')
            f = func(*args,**kv)
            return f
        return wrapper
    
    @metric
    @logging
    def normalize(name):
        sName = name[0:1].upper() + name[1:].lower()
        print(sName)
    
    if __name__ == '__main__':
        normalize('heLlO')
        normalize.__wrapper__('')
    

    运行情况如下

    >>normalize('helLo')
    Decorator1
    Decorator2
    Hello
    >>normalize.__wrapped__('world')
    Decorator2
    World
    

    1.4 定义带参数的装饰器

    from functools import wraps
    
    def log(text):
        def decorator(func):
            @wraps(func) 
            def wrappering(*args,**kv):
                print('%s %s():'%(text,func.__name__))
                return func(*args,**kv)
            return wrappering
        return decorator
    
    @log('run')
    def normalize(name):
        sName = name[0:1].upper() + name[1:].lower()
        print(sName)
    

    装饰器函数可以带参数,最外层的函数会将参数传给内层的装饰器函数,即wrappering函数是可以使用log的传入参数的。
    装饰器处理过程与下面是等价的:

    normalize = log('run')(normalize)

    总结注意:很多人学Python过程中会遇到各种烦恼问题,没有人解答容易放弃。为此小编建了个Python全栈免费答疑.裙 :七衣衣九七七巴而五(数字的谐音)转换下可以找到了,不懂的问题有老司机解决里面还有最新Python实战教程免非下,,一起相互监督共同进步!

    本文的文字及图片来源于网络加上自己的想法,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

  • 相关阅读:
    会话管理?
    为什么要用 Dubbo?
    abstract class和interface有什么区别?
    接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?抽象类中是否可以有静态的main方法?
    用最有效率的方法算出2乘以8等於几?
    如何把一段逗号分割的字符串转换成一个数组?
    查看文件内容有哪些命令可以使用?
    使用哪一个命令可以查看自己文件系统的磁盘空间配额 呢?
    Spring框架中的单例bean是线程安全的吗?
    你更倾向用那种事务管理类型?
  • 原文地址:https://www.cnblogs.com/chengxuyuanaa/p/12539400.html
Copyright © 2020-2023  润新知