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

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

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

装饰器实现

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

相关推荐

  • 第八周作业

    第八周作业 1、写一个脚本,使用ping命令推测172.16.250.1-172.16.250.254之间的所有主机的在线状态; 在线的主机使用绿色显示; 不在线的主机使用红色显示;   #!/bin/bash for i in {1..254};do if ping -c 1 -w 1 192.168.1.$i &> /dev/nu…

    Linux干货 2017-11-13
  • vim简单实用的技巧总结

    vi 和 vim无需过多的区分,vim可认为是vi的增强版。        这篇关于vim的手册,我个人觉得实在无法把它写成文章,只能以手册的方式列举出来,因为,vim是一个动手使用的工具,只能在使用中你才能慢慢发现它的操作多么符合逻辑,我从下面这些简略的描述中,尽量将它的操作步骤写出来,但更重要的…

    Linux干货 2015-09-14
  • 【职位推荐】华图网校/北京/运维工程师/8-12k(14薪)

    北京华图宏阳网络科技有限公司 华图教育集团:       华图教育(www.huatu.com)是华图宏阳股份旗下品牌,拥有遍布全国的100余家分支机构。主要产品包括中央和地方公务员招录考试辅导,事业单位、三支一扶、村官、选调生、招警等考试辅导。      北京华图宏阳教育文化发展股份有限…

    Linux干货 2016-04-20
  • 马哥教育网络班21期+第17周课程练习

    1、结合图形描述LVS的工作原理; lvs是基于内核的虚拟主机用来做四层的负载均衡,工作在netfilter框架的INPUT链上,有四种模式,上图为lvs-nat lvs-nat:有DNAT功能,支持端口映射,请求报文和响应报文都需要经过Director 请求过程:cip–>vip,Director,cip–>rip 响应…

    Linux干货 2016-12-26
  • LVM逻辑卷管理

    一、简述 什么是逻辑卷?LVM(Logical Volume Manager)利用Linux内核device-mapper实现存储系统的虚拟化。通过LVM,把底层存储硬件抽象化成存储逻辑块,再将这些逻辑块集合构成存储池,从存储池空间划分分区,可以简单地扩大或缩小分区,而不用担心硬盘没有足够的连续空间。 使用逻辑卷分区有什么用?使用逻辑卷分区重点在于可以弹性地…

    Linux干货 2016-09-06
  • 推荐-LVS专题: LVS的工作模型和调度算法介绍

    LVS专题: LVS的工作模型和调度算法介绍 LVS专题: LVS的工作模型和调度算法介绍 前言 什么是负载均衡? 什么是LVS? LVS的架构: LVS的实现模型: NAT实现原理: DR实现原理: TUN实现原理: FULLNAT实现原理: LVS的调度算法 静态调度算法(4种) 动态调度算法(6种): 总结 前言 本文大概介绍一下LVS的工作方式和实现…

    Linux干货 2016-04-05