从代码计时想到的Python计时模块、上下文管理器、变量生存期

Setup

前一篇文章中使用了代码计时的功能,使用time模块获得起始和终止时间然后相减获得运行时间。

import time

start = time.time()
# code to run
end = time.time()
cost = end - start #time in second

Python中除了这种最原始的计时方案,还提供了专门的计时模块timeit,该模块提供了较为完善的计时功能,而且支持重复测试,更多的教程中提倡使用这种方案。以下例子来自博客

def test1():
    n=0
    for i in range(101):
        n+=i
    return n

def test2():
    return sum(range(101))

def test3():
    return sum(x for x in range(101))

if __name__=='__main__':
    from timeit import Timer
    t1=Timer("test1()","from __main__ import test1")
    t2=Timer("test2()","from __main__ import test2")
    t3=Timer("test3()","from __main__ import test3")
    print t1.timeit(1000000)
    print t2.timeit(1000000)
    print t3.timeit(1000000)
    print t1.repeat(3,1000000)
    print t2.repeat(3,1000000)
    print t3.repeat(3,1000000)

以上是两种较为常见的计时功能,有相应的优缺点:

  • 第一种方案较为直观,需要了解time模块的使用方法,并且需要进行单位转换
  • 第二种方案有官方支持,但是不方便用于测量非函数代码块(例如某几行代码的运行时间),而且需要通过参数来配置测试环境

基于以上考虑,我想能不能实现一种更加灵活的代码计时工具。

上下文管理器 (Context Manager)

初见这个概念可能有点陌生,但下面的代码大家一定再熟悉不过了:

with open('afile.txt', 'r') as f:
    content = f.read()

上下文管理器是Python 2.5之后引入的概念,也就是常用的with语句。上下文管理器大大减轻了操作一些需要打开和关闭的资源(这里找不到好的定语了,就是文件和数据库这种资源)的代码。更重要的一点是,with语句使用很灵活,内部可以包含任意的代码。
实现上下文管理器有两种方法,分别是在自定义类中实现__enter()__ __exit()__和使用装饰器@contextlib.contextmanager

变量生存期

Python对于变量生存期的处理很神奇,这里不讨论太多的内容,只指出两点较为特别的地方:

  • 上例中content是在with中定义的变量,但是在外部依旧可以使用
  • Python中的循环变量在循环结束之后依旧可以访问到:
for i in range(10):
    pass
print(i) # 9

timeme

基于以上讨论,实现一个用于代码计时的上下文管理器,代码已托管到gist,之后只在gist更新,文中代码不再更新。
这个模块仅仅用于学习上下文管理器和代码计时,不恰当的地方欢迎大家指正。

__author__ = 'mengpeng'
import time
 
 
class timeme(object):
    __unitfactor = {'s': 1,
                    'ms': 1000,
                    'us': 1000000}
 
    def __init__(self, unit='s', precision=4):
        self.start = None
        self.end = None
        self.total = 0
        self.unit = unit
        self.precision = precision
 
    def __enter__(self):
        if self.unit not in timeme.__unitfactor:
            raise KeyError('Unsupported time unit.')
        self.start = time.time()
        return self
 
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end = time.time()
        self.total = (self.end - self.start) * timeme.__unitfactor[self.unit]
        self.total = round(self.total, self.precision)
 
    def __str__(self):
        return 'Running time is {0}{1}'.format(self.total, self.unit)

实现了基本的时间单位设置、精确位数设置,并提供了默认的输出字符串。

from timeme import timeme
with timeme('ms', 6) as t:
    result = sum(range(100000))
print(r) # 4999950000
print(t.total) # 5.503178
print(t) # Running time is 5.503178ms

在一些简单的计时任务中,相比于使用timeit模块更加灵活而且无需手动配置运行环境;相比于使用手动计时减少了代码行数。

Teardown

  • 前几天闲得无聊写了这个模块,很多都是得益于Python本身机制的便利。
  • 还有很多值得改善的地方,例如重复测试timeit.repeat()的功能支持。
  • 在代码代码计时、运行效率瓶颈检测方面,Python有很多完善的Profiler工具提供了相当强大的功能,我对这方面还不是很熟悉,最近打算学习一下。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容