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

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

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

装饰器实现

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

相关推荐

  • http加速器varnish

    一、web缓存概述       缓存,又称加速器,用于加速运行速度较快的设备与较慢设备之间的通信。基于程序的运行具有局部性特征其能实现加速的功能:       时间局部性:一个数据被访问之后,在随后较短的时间内有可能被访问。   &nbsp…

    2016-11-18
  • corosync + pacemaker + iscsi实现高可用mysql (下)

    上一篇讲的安装配置iscsi,本章介绍mariadb安装以及高可用的mysql具体实现 一、安装配置mariadb [root@SQL1 ~]# tar xf mariadb-5.5.36-linux-x86_64.tar.gz  -C /usr/local \\解压软件包 [r…

    Linux干货 2015-07-09
  • linux开篇六式

    第一式. Linux上的文件管理类命令,其常用的使用方法及其相关示例。       mkdir, 创建目录       rmdir,删除目录       tree, 显示目录层级       cat和tac均用于查看文件,cat是从文件首向…

    Linux干货 2016-10-13
  • Week9’s homework

    Week9's homework 1、写一个脚本(1)判断当前系统上所有用户的shell是否为可登录shell(即用户的shell不是/sbin/nologin)(2)分别统计两类用户的个数;通过字符串比较来实现。 #!/bin/bash # for i in $(cat /etc/passwd |…

    Linux干货 2016-12-24
  • 学习宣言

    不妥协,不气馁,朝着自己的目标前进。

    Linux干货 2016-10-24
  • glob及IO重定向

    globbing: 文件名通配     匹配模式:元字符           *:匹配任意长度的任意字符    pa* ,*pa* ,*pa,  *p*a*    pa, paa, passwd    ?:  …

    Linux干货 2016-12-21