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

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

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

装饰器实现

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
下一篇 2017-11-18

相关推荐

  • Linux keepalived高可用集群

                       Linux keepalived高可用集群 keepalived简介:    keepalived是为了高可用ipvs集群而设计的,主要用作realserver的健康状态检测,如果有一台web…

    系统运维 2016-11-18
  • 初识linux的进程管理与作业控制(第十二天)

         进程:linux中,运行每一个二制进程序或触发每一个事件,系统都会将它定义为一个进程,并给一个进程号,称为PID。同时依据触发这个进程的用户与相关属性关系,给予这个PID一组有效的权限设置。          &nbsp…

    Linux干货 2016-06-01
  • corosync的高可用——高可用mariadb数据库

    corosync, pacemaker,nfs高可用mariadb 实验环境: 4台CentOS 7 的主机,并关闭的iptalbes和selinux功能 主机1:10.1.43.101 node1 corosync+pacemaker+amp 主机2:10.1.43.102 node2 corosync+pacemaker+amp 主机3:10.1.43.…

    Linux干货 2016-11-24
  • grep正则表达式

    grep:这是一个搜索命令,搜文本并且将文本行显示出来 (1)grep -i 表示搜索的时候忽略大小写 (2)grep –colour 表示搜索关键字带颜色 例如:grep –colour ‘root’ /etc/passwd 显示为在etc的passwd里的 root 选项有颜色 *为了方便我们可以定义个别名,让他搜索的时候默认显示为带颜色 alias…

    Linux干货 2017-07-29
  • N21天天第十六周课程练习

    1、源码编译安装LNMP架构环境; 一、安装Nginx 1、解决依赖 [root@localhost ~]# yum groupinstall 开发工具 服务器平台开发 -y 2、安装 [root@localhost ~]# tar xf pcre-8.37.…

    Linux干货 2016-12-14
  • Redis基于Sentinel哨兵高可用方案

    下载最新redis版本,当前最新版本为  2.8.19  2014-12-30 安装redis 首页地址:http://redis.io/ 最新稳定版下载地址: http://download.redis.io/releases/redis-2.8.19.tar.gz # tar -xvf redis-2.8.19.tar.gz # cd…

    2015-03-03