上下文管理练习(为加法函数计时)

上下文管理(为加法函数计时)

  • 为加法函数计时
    • 使用装饰器显示该函数的执行时长
    • 使用上下文管理显示该函数的执行时长

装饰器实现

import time
import datetime
from functools import wraps

def logger(fn):
    @wraps(fn)    # wraps(fn)(wrapper)
    def wrapper(*args, **kw):
        start = datetime.datetime.now()
        ret = fn(*args, **kw)
        delta = (datetime.datetime.now() - start).total_seconds()
        print(delta)
        return ret
    return wrapper

@logger
def add(x, y):
    time.sleep(2)
    return x + y

上下文实现

  • 最简单实现
    5a0cea2230e4d913bf000000

  • 增加__call__用法
    import time
    import datetime
    
    class TimeIt:
        def __init__(self, fn):
            self.fn = fn
    
        def __enter__(self):
            self.start = datetime.datetime.now()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.delta = (datetime.datetime.now() - self.start).total_seconds()
            print(self.delta)
    
        def __call__(self, *args, **kwargs):
            ret = self.fn(*args, **kwargs)
            return ret
    
    def add(x, y):
        """ This is add"""
        time.sleep(2)
        return x + y
    
    with TimeIt(add) as foo:
        print(foo(3,4))
    

    5a0ceac630e4d913bf000001

    • 需要增加初始化方法,为下面__call__使用
    • __enter__方法返回self是为了,有了__call__方法以后,可以这样使用with TimeIt(add) as foo: foo(3,4)
      • 因为有了__call__后,实例变成可调用,而foo就是实例化后的实例
      • TimeIt(add)是将add函数名作为形参传进去

  • 改成装饰器
    import time
    import datetime
    
    class TimeIt:
        def __init__(self, fn):
            self.fn = fn
    
        def __enter__(self):
            self.start = datetime.datetime.now()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.delta = (datetime.datetime.now() - self.start).total_seconds()
            print(self.delta)
    
        def __call__(self, *args, **kwargs):
            print('call')
            ret = self.fn(*args, **kwargs)
            return ret
    
    @TimeIt          # add = TimeIt(add)
    def add(x, y):
        """ This is add"""
        time.sleep(2)
        return x + y
    
    # 上下文用法
    # with TimeIt(add) as foo:
    #     print(foo(3,4))
    
    print(add(5,6))
    print(add.__doc__)
    print(add.__dict__)
    print(add.__name__)
    

    5a0cf0b330e4d913bf000005
    5a0cef5530e4d913bf000004

    • 装饰器用法很简单,直接在add函数上加个@TimeIt
      • 这是由于@TimeIt等价于add = TimeIt(add),把add函数名作为实参传入到TimeIt类中
      • 就相当于为add函数加了一个类封装的功能或属性
    • 而这个时候,我们发现,用装饰器实现后,直接走的是__call__方法中的语句块,而上下文没有执行(因为没有用with..as语句)
    • 也就是说,要么用with..as语句,要么用装饰器方法,这是两个方法
      • (用with..as语句执行装饰器方法,这样比较繁琐,重复计算,因题而异)
    • 但是,如何解决文档字符串的问题,怎么把add函数的配置信息也弄过来(看__doc__和__dict__就可以知道)

  • 解决文档字符串问题
    • 方法1
    • 把函数对象的文档字符串赋给类
      class TimeIt:
          def __init__(self, fn):
              self.fn = fn
              self.__doc__ = self.fn.__doc__
              self.__name__ = self.fn.__name__
              self.__dict__ = self.fn.__dict__
      

    • 方法2
    • 使用functools.wraps函数

最终完整版

import time
import datetime
from functools import wraps

class TimeIt:
    """This is Class"""
    def __init__(self, fn):
        self.fn = fn

        # 把函数对象的文档字符串赋给类
        # self.__doc__ = self.fn.__doc__
        # self.__name__ = self.fn.__name__

        # @wraps = wraps(fn)(wrapper)
        wraps(fn)(self)   # wraps用法

#     def __enter__(self):
#         self.start = datetime.datetime.now()
#         return self

#     def __exit__(self, exc_type, exc_val, exc_tb):
#         self.delta = (datetime.datetime.now() - self.start).total_seconds()
#         print(self.delta)

    def __call__(self, *args, **kwargs):
        print('call')
        self.start = datetime.datetime.now()
        ret = self.fn(*args, **kwargs)
        delta = (datetime.datetime.now() - self.start).total_seconds()
        print(delta)
        return ret

@TimeIt          # add = TimeIt(add)
def add(x, y):
    """ This is add"""
    time.sleep(2)
    return x + y

# 上下文用法
# with TimeIt(add) as foo:
#     print(foo(3,4))

print(add(5,6))
print(add.__doc__)
print(add.__dict__)
print(add.__name__)
print(type(add))

5a0cf72130e4d913bf000006
5a0cf74130e4d913bf000007

  • 第15行的用法wraps(fn)(self)是根据这个@wraps = wraps(fn)(wrapper)来的
    • @wraps是带参装饰器,fn就是带参,wrapper是传入的实参
    • 简单来说,@wraps = wraps(fn)(wrapper)就是把fn的配置信息赋值给wrapper
    • 所以wraps(fn)(self)就是把fn的配置信息赋值给实例(self就是实例化后的实例)

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88584

(1)
nolannolan
上一篇 2017-11-18 15:14
下一篇 2017-11-18 19:54

相关推荐

  • Linux发展及OS基础

    开源协定:          GPL  LGPL,Apache,BSD          BSD:      …

    Linux干货 2016-08-08
  • shell脚本编程2

    shell脚本编程2   今天继续学习了shell脚本编程,续之前写的脚本编程1。上次主要学了编程的基础,脚本格式,变量类型及变量的使用,条件测试,数字运算并且进行了部分练习,因为这部分逻辑思维能力要求不是很高,因此记忆起来难度不是特别大。   从今天开始便进入了流程控制以及函数、数组、高级字符串操作、高级变量等内容的学习,总体来说难度和…

    Linux干货 2016-08-16
  • 硬盘的读写原理

     硬盘的种类主要是SCSI 、IDE 、以及现在流行的SATA等;任何一种硬盘的生产都要一定的标准;随着相应的标准的升级,硬盘生产技术也在升级;比如 SCSI标准已经经历了SCSI-1 、SCSI-2、SCSI-3;其中目前咱们经常在服务器网站看到的 Ultral-160就是基于SCSI-3标准的;IDE 遵循的是ATA标准,而目前流行的SATA,…

    Linux干货 2015-04-10
  • LVS管理平台使用手册(第一版)[原创]

     为了更好管理、维护LVS平台,本人基于Django+certmaster+func开发了一套管理平台,主要功能模块分为性能图表、数据中心、虚拟IP池、主机管理、监控模块等功能,基本上是按F5-LTM管理平台思路来设计,下面只要对这几大块功能进行说明。1、性能图表 功能说明:以小时、日、星期、月、年的图表展示LVS SERVER、VIP、SERVE…

    Linux干货 2015-03-28
  • 常见RAID总结

    RAID浅析 目录索引 一、定义 二、特点 三、常用RAID级别      四、RAID实现方式 五、实际环境测试 一、定义: 磁盘阵列(Redundant Arrays of Independent Disks,RAID),有“独立磁盘构成的具有冗余能力的阵列”之意。 独立磁盘冗余阵列(RAID,redundant array…

    Linux干货 2016-04-12