美文网首页
04装饰器模式

04装饰器模式

作者: 依依东望_220b | 来源:发表于2019-02-10 18:18 被阅读0次
    import time
    '''
    我们先来看一个类装饰器的案例
    Python中一切都是对象,函数也是对象(是个具有特殊__call__方法的对象),执行 对象(*args,**kwargs) 相当于执行对象的 __call__(self,*args,**kargs) 方法
    '''
    class ProfilingDecorator:
        def __init__(self,func):
            self.func=func
    
        def __call__(self,*args,**kwargs):
            start_time=time.time()
            result=self.func(*args,**kwargs)
            end_time=time.time()
            print('[Time elapsed for args={}]:{}'.format(args,end_time-start_time))
    
            return result
    
    
    @ProfilingDecorator #相当于 fib=ProfilingDecorator(fib),我们执行fib(*args,**kwarg)相当于执行fib对象的__call__方法
    def fib(n):
        if n<2:
    
            return 1
    
        current,current_prev=1,1
        for _ in range(2,n):
            current,current_prev=current+current_prev,current
    
        return current
    
    def main_01():
        n=77
        print('[Fib for n={}]:{}'.format(n,fib(n)))
        print('-'*100)
        n=100
        print('[Fib.__call__ for n={}]:{}'.format(n,fib.__call__(n)))
    """
    执行main_01的输出结果是:
        [Time elapsed for args=(77,)]:1.0728836059570312e-05
        [Fib for n=77]:5527939700884757
        ----------------------------------------------------------------------------------------------------
        [Time elapsed for args=(100,)]:8.58306884765625e-06
        [Fib.__call__ for n=100]:354224848179261915075
    """
    
    '''
    我们看多个装饰器装饰同一函数的使用案例
    下面我们通过装饰器类来统计原始函数运行时间,并且将原始函数的输出结果
    '''
    #这个装饰器统计运行时长
    class ProfillingDecorator:
        def __init__(self,func):
            print(">>>>>>>>>>>>>>Inside ProfillingDecorator.__init_")
            self.func=func
    
        def __call__(self,*args):
            print(">>>>>>>>>>>>>>Inside ProfillingDecorator.__call__")
            start_time=time.time()
            result=self.func(*args)
            end_time=time.time()
            print('[Time elapsed for args={}]:{}'.format(args,end_time-start_time))
    
            return result
    #这个装饰器将输出结果转转变为HTML格式
    class ToHTMLDecorator:
        def __init__(self,func):
            print(">>>>>>>>>>>>>>Inside ToHTMLDecorator.__init__")
            self.func=func
    
        def __call__(self,*args,**kwargs):
            print(">>>>>>>>>>>>>>Inside ToHTMLDecorator.__call__")
            return "<html><body>{}</body></html>".format(self.func(*args,**kwargs))
    
    
    @ToHTMLDecorator
    @ProfillingDecorator
    def fib(n):
        print(">>>>>>>>>>>>>>Inside fib")
        if n<2:
            return 1
        current_prev,current=1,1
        for _ in range(2,n):
            current,current_prev=current_prev+current,current
    
        return current
    
    def main_02():
        n=77
        print('[Fib for n={}]:{}'.format(n,fib(n)))
        print('-'*100)
        n=100
        print('[Fib.__call__ for n={}]:{}'.format(n,fib.__call__(n)))
    '''
    main_02执行后输出结果如下:
    我们结合输出来分析一波
        >>>>>>>>>>>>>>Inside ProfillingDecorator.__init_
        >>>>>>>>>>>>>>Inside ToHTMLDecorator.__init__
                                               出现这个输出的原因很简单, 结合我们装饰器初始化的代码,相当于先执行fib_mid=ProfillingDecorator(fib)然后执行fib=ToHTMLDecorator(fib_mid)
                                               
        >>>>>>>>>>>>>>Inside ToHTMLDecorator.__call__    --------------------|结合初始化代码,经过两个装饰器初始化后对象其实是个ToHTMLDecorator对象,所以最先执行的___call___方法也就是ToHTMLDecorator的__call__方法
        >>>>>>>>>>>>>>Inside ProfillingDecorator.__call__ -------------------|--------|ToHTMLDecorator的func其实是ProfillingDecorator对象,所以我们执行当前func的__call__方法其实是执行ProfillingDecorator的__call__方法
        >>>>>>>>>>>>>>Inside fib --------------------------------------------|--------|------ProfillingDecorator执行当前func的__call__方法,ProfillingDecorator的func是fib对象,我们在这里要执行是fib的__call__方法
        [Time elapsed for args=(77,)]:1.049041748046875e-05------------------|--------|
        [Fib for n=77]:<html><body>5527939700884757</body></html> -----------|
                                             
        ----------------------------------------------------------------------------------------------------
                                                       下面我们就不解释了,和上面一样 
        >>>>>>>>>>>>>>Inside ToHTMLDecorator.__call__
        >>>>>>>>>>>>>>Inside ProfillingDecorator.__call__
        >>>>>>>>>>>>>>Inside fib
        [Time elapsed for args=(100,)]:7.3909759521484375e-06
        [Fib.__call__ for n=100]:<html><body>354224848179261915075</body></html>
                                                 
    '''
    
    """
    其实Python装饰器除了通过类来实现,还可以通过函数来实现
    下面来看一个案例
    """
    def profilling_decorator(func):
        print('>>>>>>Inside profilling_decorator')
        def wrapped_func(*args,**kwargs):
            print('>>>>>>Inside profilling_decorator.wrapped_func')
            start_time=time.time()
            result=func(*args,**kwargs)
            end_time=time.time()
            print('[Time alsped for n={}]:{}'.format(args,end_time-start_time))
            return result
    
        return wrapped_func
    
    @profilling_decorator
    def fib(n):
        print('>>>>>>Inside fib')
        if n<2:
            return 1
        current_prev,current=1,1
        for _ in range(2,n):
            current_prev,current=current,current_prev+current
    
        return current
    
    def main_03():
        n=77
        print("Fibonacci number for n={}:{}".format(n,fib(n)))
    '''
    main_03执行后输出结果如下:
        >>>>>>Inside profilling_decorator
        >>>>>>Inside profilling_decorator.wrapped_func
        >>>>>>Inside fib  
        [Time alsped for n=(77,)]:1.0967254638671875e-05
        Fibonacci number for n=77:5527939700884757
    '''
    """
    使用函数装饰器的时候,必须返回一个函数供用户使用,此处返回的是wrapped_func函数,这个函数中使用了func这个对象,这种就是闭包,
    即使profilling_decorator执行完毕,profilling_decorator的传入参数func也会在wrapped_func中保存
    """
    
    '''
    但是装饰器会有些副作用,由于返回的函数不是原函数,所以会失去原函数的__main__与__doc__等属性
    比如以下这个例子
    '''
    def dummy_decorator(f):
        print('Inside dummy_decorator')
        def wrap_f():
            print('Inside wrap_f')
            return f()
        return wrap_f
    
    @dummy_decorator
    def do_nothing():
        print('Inside do_nothing')
    
    def main_04():
        print("\nWrapped Function:",do_nothing.__name__,"\n")
        do_nothing()
    '''
    运行main_04,执行结果如下:
        Inside dummy_decorator
    
        Wrapped Function: wrap_f  
                                    可见相关重要属性已被更改
        Inside wrap_f
        Inside do_nothing
    '''
    '''
    避免者中情况的发生有两种做法
    第一种:直接将返回函数__name__,__doc__等重要属性设置为传入参数func的
    '''
    def dummy_decorator(f):
        print('Inside dummy_decorator')
        def wrap_f():
            print('Inside wrap_f')
            return f()
        wrap_f.__name__=f.__name__
        wrap_f.__doc__=f.__doc__
        return wrap_f
    
    @dummy_decorator
    def do_nothing():
        print('Inside do_nothing')
    
    def main_05():
        print("\nWrapped Function:",do_nothing.__name__,"\n")
        do_nothing()
    '''
    输出结果为:
        Inside dummy_decorator
    
        Wrapped Function: do_nothing 
    
        Inside wrap_f
        Inside do_nothing
    '''
    
    '''
    第二种:直接使用Python标准库提供的模块,保留重要参数
    '''
    from functools import wraps
    
    def dummy_decorator(f):
        print('Inside dummy_decorator')
        @wraps(f)
        def wrap_f():
            print('Inside wrap_f')
            return f()
        return wrap_f
    
    @dummy_decorator
    def do_nothing():
        print('Inside do_nothing')
    
    def main_06():
        print("\nWrapped Function:",do_nothing.__name__,"\n")
        do_nothing()
    '''
    输出结果为:
        Inside dummy_decorator
    
        Wrapped Function: wrap_f 
    
        Inside wrap_f
        Inside do_nothing
    '''
    
    """
    现在我们有了一个新需求,需要耗时统计装饰器按照我们的设置,以秒或者毫秒为单位显示返回运行耗时
    这个时候我们就需要向装饰传入时间单位参数unit,者也是一个闭包,传入的时间单位unit参数被wrap_f保存着
    """
    def profilling_decorator_with_unit(unit):
        def profiling_decorator(f):
            @wraps(f)
            def wrap_f(*args,**kwargs):
                start_time=time.time()
                result=f(*args,**kwargs)
                end_time=time.time()
                if unit=='seconds':
                    elapsed_time=(end_time-start_time)/1000
                else:
                    elapsed_time=(end_time-start_time)
    
                print('[Time elapsed for args={}]:{}'.format(args,elapsed_time))
    
                return result
    
            return wrap_f
    
        return profiling_decorator
    
    @profilling_decorator_with_unit('seconds')
    @profilling_decorator_with_unit('other')
    def fib(n):
        if n<2:
    
            return 1
        current,current_prev=1,1
        for _ in range(2,n):
            current_prev,current=current,current+current_prev
    
        return current
    
    def main_07():
        n=77
        print("Fibonacci number for n={}:{}".format(n,fib(n)))
        print('fib_function_name:{}'.format(fib.__name__))
    '''
    执行main_07时分三种情况:
        情况1
        @profilling_decorator_with_unit('second')
        def fib(n):
            输出为: 
                [Time elapsed for args=(77,)]:1.0967254638671875e-08 #按照秒输出
                Fibonacci number for n=77:5527939700884757            
                fib_function_name:fib
        情况2      
        @profilling_decorator_with_unit('other')
        def fib(n):
            输出为:
               [Time elapsed for args=(77,)]:1.2159347534179688e-05 #按照毫秒输出
               Fibonacci number for n=77:5527939700884757 
               fib_function_name:fib
        情况3
        @profilling_decorator_with_unit('seconds')
        @profilling_decorator_with_unit('other')
        def fib(n):
            输出为:
               [Time elapsed for args=(77,)]:1.0013580322265625e-05 #按照毫秒输出
               [Time elapsed for args=(77,)]:4.720687866210938e-08 #按照秒输出
               Fibonacci number for n=77:5527939700884757
               fib_function_name:fib
    
    其实者个很好理解,我们先得到profilling_decorator_with_unit()执行的结果profiling_decorator,然后我们将fib函数传入profiling_decorator_with_unit
    '''
    
    

    相关文章

      网友评论

          本文标题:04装饰器模式

          本文链接:https://www.haomeiwen.com/subject/xesxeqtx.html