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

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

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

装饰器实现

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

相关推荐

  • 05linux用户和组的权限总结

    1、文件的权限分类 文件的权限对象分三类:属主(u)、属组(g)、其他(o),每个对象都有rwx,读写执行三类权限。 对于文件 r:可查看文件内容 w:可修改其类容 x:可把此文件提请内核启动为一个进程 对于目录 r:可使用ls查看此目录中文件列表 w:可在此目录中创建和删除文件 x:可使用ls查看目录中文件列表,可以cd进入此目录 X:只给目录x权限,不给…

    Linux干货 2016-11-27
  • linux权限管理

          今天来讲讲linux里面权限的问题,在linux系统中,有这么几类权限,r,w,x,s,t这么几类权限,系统中为什么要有权限这个东西呢,linux一切皆文件,有些文件不想让某些人看到,那么这个时候就需要设置文件的访问的权限了,文件的拥有者一般都是有权力修改删除文件的,但拥有者以外的人未必就能删除修…

    Linux干货 2016-08-04
  • ☞yum源的生成与配置{ local;cdrom;http;ftp;}

    ☞yum源的生成与配置{ local;cdrom;http;ftp;} 本文是继上一篇文章“CentOS程序安装的3种方式{ 源码包安装 | rpm包安装 | yum安装;}”的补充,上篇文章http://www.178linux.com/38812主要介绍了基于cdrom的yum源制作和配置以及归纳了详细的yum命令。本文继续介绍基于本地file、远程ht…

    Linux干货 2016-08-24
  • samba服务部署WordPress

    简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块 ,SMB主要是作为Microsoft的网络通讯协议,后来Samba将SMB通信协议应用到了Linux系统上,就形成了现在的Samba软件。后来微软又把 SMB 改名为 CIFS(Common Inter…

    Linux干货 2017-04-28
  • while循环中的一个常见问题

    在楼主刚刚学习接触while循环的时候,发现了一个问题,在while循环内部对变量赋值、定义变量、数组定义等等环境,在循环外面失效。 一个简单的测试脚本如下: 执行结果证明,$new_var的结果是空值。在google上查了查,才发现问题出在管道上。 先看看下面的内容。 while循环的写法有好几种,它的语法结构为: while test_cmd_list;…

    2017-08-26
  • 马哥教育网络班21期第七周作业

    1、创建一个10G分区,并格式为ext4文件系统;   (1) 要求其block大小为2048, 预留空间百分比为2, 卷标为MYDATA, 默认挂载属性包含acl;   (2) 挂载至/data/mydata目录,要求挂载时禁止程序自动运行,且不更新文件的访问时间戳; [root@localhost ~]#…

    Linux干货 2016-10-09