• Python 装饰器详解


    开放封闭原则:

    • 开放对扩展
    • 封闭修改源代码
    • 改变了人家调用方式

    装饰器结构

    """
    默认结构为三层!!!每层返回下一层内存地址就可以进行执行函数,
    传参:语法糖中的传参可以在地二层中进行判断传入。
         第一层:传送语法糖中的参数
         第二层:传送外部函数的名称
         第三层:传送外部函数的参数
    被装饰器装饰的函数名即使没有被调用(因为有@xxx,会触发运行装饰器),
    (装饰器工厂函数)定义装饰器的代码已经运行了(最内部的那个函数并没有运行),
    (把被装饰的原函数引用赋值给了装饰器内部的那个函数名),当下边通过该函数名调用时,
    会调用到装饰器内部的那个函数()
    装饰器:在不修改函数源代码的基础上,添加函数功能
    """
    def Wrapper(*args,**kwargs): #获取`@Wrapper(args)`传入参数
        def Outter(func_name): #获取装饰器所装饰函数的名称
            def Inner(*args,**Kwargs): #获取`def func(*args,**kwargs):`的传入参数
                ret = func_name(*args,**kwargs) #执行装饰器所装饰的函数
                return ret #将返回值传给被装饰的函数
            return Inner #返回`Inner`的内存地址,方便调用
        return Outter #返回`Outter`的内存地址,方便调用
        
    @Wrapper(args) #`func = Outter(func) = Wrapper(args)`装饰器进行传参
    def func(*args,**kwargs): #定义被装饰函数
        return func.__name__ #定义函数返回值    
     
    

    返回装饰器的被装饰函数名

    from functools import wraps
    def warpper(f):
        @wraps(f)   #显示被装饰的函数
        def inner(*args,**kwargs):
            # print(f.__name__)
            print(f)
        return inner  #
    def aa():
        print(1)
    aa = warpper(aa)
    print(aa.__name__)  # 显示被装饰的函数名
    

    账户认证(简易版)

    def Wrapper(auth_type): #默认使用Wrapper名称,获取语法糖的参数
        def Outer(func_name): #获取传入参数的名称
            def file(*args,**kwargs): #获取传入参数的参数
                print('这是在file函数中')
                ret = func_name(*args,**kwargs)
                print('file函数装饰完毕')
                return ret
            def net(*args,**kwargs):
                print('这个是在网络函数中')
                ret = func_name(*args,**kwargs)
                print('网络函数执行结束')
                return ret
            if auth_type == 'localfile': #对传入参数进行判断返回不同结果
                return file
            elif auth_type == 'net':
                return net
            else:
                print('该函数没有被装饰')
        return Outer
    
    @Wrapper(auth_type='net') #语法糖进行传参
    def Login(username):
        print(f'Welcome {username} Login.')
    
    Login('longlong') #大神登陆啦!
    

    Python 装饰器装饰类中的方法

    • 本文介绍如何使用Python的装饰器装饰一个类的方法,同时在装饰器函数中调用类里面的其他方法。本文以捕获一个方法的异常为例来进行说明。
    • 有一个类Test, 它的结构如下:
    class Test(object):
        def __init__(self):
            pass
    
        def revive(self):
            print('revive from exception.')
            # do something to restore
    
        def read_value(self):
            print('here I will do something.')
            # do something.
    
    • 在类中有一个方法read_value(),这个方法在多个地方被调用。由于某些原因,方法read_value()有可能随机抛出Exception导致程序崩溃。所以需要对整个方法做try ... except处理。最丑陋的做法如下面的代码所示:
    class Test(object):
        def __init__(self):
            pass
    
        def revive(self):
            print('revive from exception.')
            # do something to restore
    
        def read_value(self):
            try:
                print('here I will do something.')
                # do something.
            except Exception as e:
                print(f'exception {e} raised, parse exception.')
                # do other thing.
                self.revive()
    
    • 这样写虽然可以解决问题,但是代码不Pythonic。
    • 使用装饰器来解决这个问题,装饰器函数应该写在类里面还是类外面呢?答案是,写在类外面。那么既然写在类外面,如何调用这个类的其他方法呢?
    • 首先写出一个最常见的处理异常的装饰器
    def catch_exception(origin_func):
        def wrapper(*args, **kwargs):
            try:
                u = origin_func(*args, **kwargs)
                return u
            except Exception:
                return 'an Exception raised.'
        return wrapper
    
    
    class Test(object):
        def __init__(self):
            pass
    
        def revive(self):
            print('revive from exception.')
            # do something to restore
    
        @catch_exception
        def read_value(self):
            print('here I will do something.')
            # do something.
    
    • 这种写法,确实可以捕获到origin_func()的异常,但是如果在发生异常的时候,需要调用类里面的另一个方法来处理异常,这又应该怎么办?答案是给wrapper增加一个参数:self.
    • 代码变为如下形式:
    def catch_exception(origin_func):
        def wrapper(self, *args, **kwargs):
            try:
                u = origin_func(self, *args, **kwargs)
                return u
            except Exception:
                self.revive() #不用顾虑,直接调用原来的类的方法
                return 'an Exception raised.'
        return wrapper
    
    
    class Test(object):
        def __init__(self):
            pass
    
        def revive(self):
            print('revive from exception.')
            # do something to restore
    
        @catch_exception
        def read_value(self):
            print('here I will do something.')
            # do something.
    
    • 通过添加一个self参数,类外面的装饰器就可以直接使用类里面的各种方法,也可以直接使用类的属性。

    使用类来写装饰器

    • 前两天发现了装饰器可以直接使用类来写,分享一下,需要用类里面的__call__方法,__call__方法就是可以把这个实例当成一个函数来调用,如果正常使用类里面的方法的话,实例方法要先实例化类,然后才能调用,静态方法、类方法则需要用类名或者实例化之后的对象来调用,而实例化之后的这个对象,是不能被调用的,__call__方法就是把这个实例变成一个可以调用的对象,也就是说实例化之后,这个对象就可以和一个普通函数一样被调用。
    • 示例代码如下:
    class Foo:
        def __call__(self, *args, **kwargs):
            print('call....')
        def test(self):#
            print('test....')
    if __name__ == '__main__':
        t = Foo()#实例化类
        t.test()#正常调用实例方法
        t()#直接调用实例化之后的对象
    
    • 理解了上面的之后,就可以使用class来写一个装饰器了,计算程序的运行时间,当然思想和以前用函数写装饰器是一样的
    class Fuck(object):
        def __init__(self, func):
            self.func = func
        def __call__(self, *args, **kwargs):
            import time
            start_time = time.time()
            res = self.func(*args, **kwargs)
            end_time = time.time()
            print('the function "%s" run time is %s' % (self.func.__name__,
                                                        (end_time - start_time)))
            return res
    @Fuck
    def run(name):
        import time
        time.sleep(1)
        return 'sb_%s' % name
    print(run('hyf'))
    
    魏美龍|DevOps Engineer|will_wei_devops@163.com
  • 相关阅读:
    python字符串字典列表互转
    列表迭代器 ListIterator
    并发修改异常处理
    二月天 案例
    Calendar类
    Date类
    冒泡排序
    内部类
    python第三天
    Layui——checkbox使用
  • 原文地址:https://www.cnblogs.com/meilong/p/PythonWraper.html
Copyright © 2020-2023  润新知