• Python进阶之装饰器


    函数也是对象

    要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用。既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回。同时,函数体中也可以再定义函数。

    装饰器本质

    可以通过编写一个纯函数的例子来还原装饰器所要做的事。

    def decorator(func):
        
        def wrap():
            print("Doing someting before executing func()")
            func()
            print("Doing someting after executing func()")
    
        return wrap
    
    
    def fun_test():
        print("func")
    
    
    fun_test = decorator(fun_test)
    fun_test()
    
    # Output:
    # Doing someting before executing func()
    # func
    # Doing someting after executing func()
    
    1. fun_test所指向的函数的引用传递给decorator()函数
    2. decorator()函数中定义了wrap()子函数,这个子函数会调用通过func引用传递进来的fun_test()函数,并在调用函数的前后做了一些其他的事情
    3. decorator()函数返回内部定义的wrap()函数引用
    4. fun_test接收decorator()返回的函数引用,从而指向了一个新的函数对象
    5. 通过fun_test()调用新的函数执行wrap()函数的功能,从而完成了对fun_test()函数的前后装饰

    Python中使用装饰器

    在Python中可以通过@符号来方便的使用装饰器功能。

    def decorator(func):
        
        def wrap():
            print("Doing someting before executing func()")
            func()
            print("Doing someting after executing func()")
    
        return wrap
    
    @decorator
    def fun_test():
        print("func")
    
    
    fun_test()
    
    # Output:
    # Doing someting before executing func()
    # func
    # Doing someting after executing func()
    

    装饰的功能已经实现了,但是此时执行:

    print(fun_test.__name__)
    
    # Output:
    # wrap
    

    fun_test.__name__已经变成了wrap,这是应为wrap()函数已经重写了我们函数的名字和注释文档。此时可以通过functools.wraps来解决这个问题。wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

    更规范的写法:

    from functools import wraps
    
    def decorator(func):
        @wraps(func)
        def wrap():
            print("Doing someting before executing func()")
            func()
            print("Doing someting after executing func()")
    
        return wrap
    
    
    @decorator
    def fun_test():
        print("func")
    
    
    fun_test()
    print(fun_test.__name__)
    
    # Output:
    # Doing someting before executing func()
    # func
    # Doing someting after executing func()
    # fun_test
    

    带参数的装饰器

    通过返回一个包裹函数的函数,可以模仿wraps装饰器,构造出一个带参数的装饰器。

    from functools import wraps
    
    def loginfo(info='info1'):
        def loginfo_decorator(func):
            @wraps(func)
            def wrap_func(*args, **kwargs):
                print(func.__name__ + ' was called')
                print('info: %s' % info)
                
                return func(*args, **kwargs)
            return wrap_func
        return loginfo_decorator
        
    @loginfo()
    def func1():
        pass
        
    func1()
    
    # Output:
    # func1 was called
    # info: info1
    
    @loginfo(info='info2')
    def func2():
        pass
    
    func2()
    # Output:
    # func2 was called
    # info: info2
    

    装饰器类

    通过编写类的方法也可以实现装饰器,并让装饰器具备继承等面向对象中更实用的特性

    首先编写一个装饰器基类:

    from functools import wraps
    
    class loginfo:
        def __init__(self, info='info1'):
            self.info = info
            
        def __call__(self, func):
            @wrap
            def wrap_func(*args, **kwargs):
                print(func.__name__ + ' was called')
                print('info: %s' % self.info)
                
                self.after()    # 调用after方法,可以在子类中实现
                return func(*args, **kwargs)
            return wrap_func
    
        def after(self):
            pass
    
    
    @loginfo(info='info2')
    def func1():
        pass
        
    # Output:
    # func1 was called
    # info: info1
    

    再通过继承loginfo类,扩展装饰器的功能:

    class loginfo_after(loginfo):
        def __init__(self, info2='info2', *args, **kwargs):
            self.info2 = info2
            super(loginfo_after, self).__init__(*args, **kwargs)
    
        def after(self):
            print('after: %s' % self.info2)
    
    
    @loginfo_after()
    def func2():
        pass
    
    func2()
        
    # Output:
    # func2 was called
    # info: info1
    # after: info2
    
  • 相关阅读:
    SQL Server解惑——查询条件IN中能否使用变量
    依赖注入高级玩法——注入接口服务的多个实现类
    最纯净的硬件检测工具箱
    批处理体会hosts文件
    针对m3u8视频的ts文件解密
    HLS协议之m3u8和ts流格式详解
    您的主机不满足在启用Hyper-V或Device/Credential Guard的情况下运行VMwareWorkstation的最低要求
    FFmpeg的安装和使用
    如何下载 blob 地址的视频资源
    Win10系统创建WiFi热点的两种方法
  • 原文地址:https://www.cnblogs.com/haoxi/p/6838428.html
Copyright © 2020-2023  润新知