Python装饰器(python计算函数运行花费时间)

Python装饰器(计算函数运行花费时间)

    python的装饰器和java的面向切面变成很相似,面向切面编程主要关心的是函数运行前(Before)和函数运行结束(After),可以针对函数前后做功能修复、功能插入工作,比如一些通用的打印日志,计算函数运行时间,拦截函数做一些事前处理, 相比而言python的装饰器更加灵活好用。

  1. 现在想要比较两个功能相同函数的性能,计算函数的运行时间

    def parse_m3u8_file(m3u8_uri)pass
    	# Before: 运行开始时间
        # Running: 函数体运行中
        # After:   运行结束时间
        # 计算函数运行时间差并输出
    

        完成这个功能,需要改写每个函数的函数体,并向上述描述添加重复代码,冗余而又乏味;有没有一种操作可以不用触及函数体的改动(或者微小改动)?装饰器可以优雅的完成这一功能。

    import time
    
    def timefn1(fn):
        '''
        :param fn: 被装饰器修饰的函数,将作为装饰器的默认参数传入
        :return: 被装饰的函数本身 
        '''
        def measure_use_time(*args, **kw):
            '''
            函数传参方式 fn(arg1, arg2, param1=a, param2=b)
            :param args: 位置参数(arg1, arg2, ....)  传参方式 fn(arg1, arg2, ...)
            :param kw: 字典参数{param1=a, param2=b, .....} 传参方式 fn(param1=a, param2=b, ....)
            :return: 
            '''
            t1 = time.time()
            res = fn(*args, **kw)
            t2 = time.time()
            print("@timefn: %s took %s" % (fn.__name__, t2-t1)
            return res
        return measure_use_time
    
    # 函数的定义上加@timefn1即可输出函数运行时间
    '''
    	形如:
    	@timefn1
    	def parse_m3u8_file(m3u8_uri):
    		pass
    '''
            
    
  2. 功能函数介绍:这个函数是对m3u8文件进行解析,提取其中的ts视频片段链接:

    文件部分内容( 本文解析的m3u8文件附件):

    #EXTM3U
    #EXT-X-VERSION:3
    #EXT-X-TARGETDURATION:6
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-KEY:METHOD=AES-128,URI="https://ts1.yuyuangewh.com:9999/20201006/a15C23PF/1000kb/hls/key.key"
    #EXTINF:4.28,
    https://ts1.yuyuangewh.com:9999/20201006/a15C23PF/1000kb/hls/nKw4m62o.ts
    #EXTINF:1.96,
    https://ts1.yuyuangewh.com:9999/20201006/a15C23PF/1000kb/hls/LFoLf2u7.ts
    #EXTINF:3.12,
    https://ts1.yuyuangewh.com:9999/20201006/a15C23PF/1000kb/hls/qhKKazou.ts
    #EXT-X-ENDLIST
    

    函数代码部分:

    def parse_m3u8_file(m3u8_uri):
        '''
        解析m3u8格式文件,得到ts视频片段链接
            全部读取文件内容,进行正则匹配(ts链接URL)
        :param m3u8_uri: 本地缓存的m3u8文件路径
        :return: 
        '''
        with open(m3u8_uri, 'r') as fr:
            data = fr.read()
            ts_pattern = r"https://.*\.ts"
            ts_urls = re.findall(ts_pattern, data)
        return ts_urls
    
    def parse_m3u8(m3u8_uri):
        '''
        解析m3u8格式文件,得到ts视频片段链接
            逐行读取文件内容,添加ts链接URL(不以#开头)
        :param m3u8_uri: 本地缓存的m3u8文件路径
        :return: 
        '''
        ts_urls = []
        fr = open(m3u8_uri, 'r')
        while True:
            buf = fr.readline()
            if not buf:
                print(m3u8_uri + ' has read...')
                break
            if not buf.startswith('#'):
                ts_urls.append(buf.strip())
        fr.close()
        return ts_urls
    
  3. 装饰器改进:
    ​     如果你现在想给装饰器提供参数来简要说明一下函数的用法(或者其他用途的参数),这个fn(被装饰器修饰的函数)不是不能作为默认参数传入了?python提供了解决方案(funtools.wrap来装饰函数):

    from functools import wraps
    import logging
    
    def timefn2(fn_use=''):
        '''
        :param fn_use: 装饰器的参数 
        '''
        def wrapper(fn):
            @wraps(fn)			#lookat here
            def measure_use_time(*args, **kw):
                '''
                函数传参方式 fn(arg1, arg2, param1=a, param2=b)
                :param args: 位置参数(arg1, arg2, ....)  传参方式 fn(arg1, arg2, ...)
                :param kw: 字典参数{param1=a, param2=b, .....} 传参方式 fn(param1=a, param2=b, ....)
                :return:
                '''
                t1 = time.time()
                res = fn(*args, **kw)
                t2 = time.time()
                logging.warning("@timefn: %s(%s) took %s" % (fn.__name__, fn_use, format_use_time(t2 - t1)))
                return res
            return measure_use_time
        return wrapper
    
    # 性能计算功能的添加需要加入装饰器@timefn2
    '''
    	形如:
    	# 不带函数用途的写法
    	@timefn2()
    	def parse_m3u8_file(m3u8_uri):
    		pass
    	
    	# 带函数用途的写法
    	@timefn2('parse2')
    	def parse_m3u8(m3u8_uri):
    		pass
    '''
    

    ​     注意到上面使用到了一个函数format_use_time是将秒数转化成时分秒的格式,方便查看; 第二个引入了日志模块,不在使用print来进行控制台输出:

    def format_use_time(seconds):
        '''
           将秒数转化为 HH:MM:SS格式
           :param seconds: 
           :return: 格式化字符串
        '''
        
        """
            # 其他语言通用写法
            h = int(seconds/3600)
            seconds_ = seconds % 3600
            m = int(seconds_/60)
            s = seconds_ % 60
            return "{:0>2d}:{:0>2d}:{:0>9.6f}".format(h, m, s)
        """
    
        m, s = divmod(seconds, 60)
        h, m = divmod(m, 60)
        return "{:0>2d}:{:0>2d}:{:0>9.6f}".format(int(h), int(m), s)
    

    ​     优化上面的装饰器函数,加入异常处理机制:

    from functools import wraps
    import logging
    
    def timefn(fn_use=''):
        '''
        :param fn_use: 装饰器的参数
        '''
        def wrapper(fn):
            @wraps(fn)
            def measure_use_time(*args, **kw):
                '''
                函数传参方式 fn(arg1, arg2, param1=a, param2=b)
                :param args: 位置参数(arg1, arg2, ....)  传参方式 fn(arg1, arg2, ...)
                :param kw: 字典参数{param1=a, param2=b, .....} 传参方式 fn(param1=a, param2=b, ....)
                :return:
                '''
                t1 = time.time()
                try:
                    res = fn(*args, **kw)
                except Exception as e:
                    logging.error(f"@timefn %s(%s) execute error" % (fn.__name__, fn_use))
                    # decide whether throw exception or not (决定受否抛出异常,终止程序)
                    # raise e
                    return None
                else:
                    t2 = time.time()
                    logging.warning("@timefn: %s(%s) took %s" % (fn.__name__, fn_use, format_use_time(t2 - t1)))
                    return res
            return measure_use_time
        return wrapper
    
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值