04装饰器模式

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
'''

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容