使用Python Timeit来计时你的代码

在本教程中,你将学习如何使用python的timeit模块中的timeit函数。你将学会如何计时python中的简单表达式和函数。

计时你的代码可以帮助你估计代码的执行时间,并找出需要优化的代码部分。

我们将首先学习python的timeit函数的语法。然后我们将编写示例代码,了解如何使用它来计时代码块和函数。让我们开始吧。

如何使用python的timeit函数

timeit模块是python标准库的一部分,你可以导入它:

import timeit

使用timeit模块中的timeit函数的语法如下:

timeit.timeit(stmt, setup, number)

这里:

  • stmt是要测量执行时间的代码块。你可以将其指定为简单的python字符串或多行字符串,或者传递可调用对象的名称。
  • 正如其名称所示,setup是只需要运行一次的代码块,通常作为stmt运行的一部分。例如,假设您正在计算创建numpy数组的执行时间。在这种情况下,导入numpy是setup代码,而实际的数组创建是要计时的语句。
  • number参数表示运行stmt的次数。number的默认值是100万(1000000),但你也可以将此参数设置为其他任意值。

现在我们已经学会了使用timeit()函数的语法,让我们开始编写一些示例代码。

计时简单的python表达式

在本节中,我们将尝试使用timeit测量简单python表达式的执行时间。

启动python repl并运行以下代码示例。在这里,我们计算了指数和整除运算符在10000次和100000次运行中的执行时间。

请注意,我们将要计时的语句作为python字符串传递,并使用分号分隔语句中的不同表达式。

>>> import timeit
>>> timeit.timeit('3**4;3//4',number=10000)
0.0004020999999738706

>>> timeit.timeit('3**4;3//4',number=100000)
0.0013780000000451764

在命令行中运行python timeit

你也可以在命令行中使用timeit。以下是timeit函数调用的命令行等价形式:

$ python -m timeit -n [number] -s [setup] [stmt]
  • python -m timeit表示我们将timeit作为主模块运行。
  • n是一个命令行选项,表示代码应该运行的次数。这相当于timeit()函数调用中的number参数。
  • 你可以使用选项-s来定义setup代码。

下面,我们使用命令行等价形式重写了前面的示例:

在这个例子中,我们计算内置的len()函数的执行时间。字符串的初始化是使用s选项传递的设置代码。

$ python -m timeit -n 100000 '3**4;3//4'
100000 loops, best of 5: 35.8 nsec per loop

在输出中,注意到我们得到了5次中的最佳运行时间。这是什么意思?当你在命令行上运行timeit时,repeat选项r被设置为默认值5。这意味着对指定次数的stmt的执行将重复五次,并返回最佳的执行时间。

使用timeit进行字符串反转方法的分析

在使用python字符串时,你可能想要对其进行反转。字符串反转的两种常见方法如下:

  • 使用字符串切片
  • 使用reversed()函数和join()方法

使用字符串切片反转python字符串

让我们了解一下字符串切片的工作原理,以及如何使用它来反转python字符串。使用语法some-string[start:stop]返回从索引start开始并延伸到索引stop-1的字符串切片。让我们举个例子。

考虑以下字符串'python'。字符串的长度为6,索引列表为0、1、2直到5。

>>> string_1 = 'python'

当你同时指定startstop值时,你会得到一个字符串切片,它从startstop-1扩展。因此,string_1[1:4]返回'yth'。

>>> string_1 = 'python'
>>> string_1[1:4]
'yth'

当你不指定start值时,将使用默认的start值为零,切片从索引零开始,延伸到stop - 1

这里,stop值为3,所以切片从索引0开始,延伸到索引2。

>>> string_1[:3]
'pyt'

当你不包括stop索引时,你会看到切片从start索引(1)开始,延伸到字符串的末尾。

>>> string_1[1:]
'ython'

忽略startstop值都返回整个字符串的切片。

>>> string_1[::]
'python'

让我们创建一个带有step值的切片。将startstopstep值分别设置为1、5和2。我们得到一个从1开始延伸到4(不包括结束点5)的字符串切片,其中包含每隔一个字符的字符。

>>> string_1[1:5:2]
'yh'

当你使用负步长时,可以从字符串的末尾开始切片。设置步长为-2,string_1[5:2:-2]得到以下切片:

>>> string_1[5:2:-2]
'nh'

所以,为了得到字符串的反向副本,我们跳过startstop值,并将步长设为-1,如下所示:

>>> string_1[::-1]
'nohtyp'

总结一下:string[::-1]返回一个字符串的反向副本。

使用内置函数和字符串方法来反转字符串

python中内置的reversed()函数会返回一个字符串元素的反向迭代器。

>>> string_1 = 'python'
>>> reversed(string_1)

所以你可以使用for循环遍历反向迭代器:

for char in reversed(string_1):
    print(char)

并以反向顺序访问字符串的元素。

# 输出
n
o
h
t
y
p

接下来,你可以在反向迭代器上调用join()方法,其语法为:.join(reversed(some-string))

下面的代码段展示了一些示例,其中分隔符分别为连字符和空格。

>>> '-'.join(reversed(string1))
'n-o-h-t-y-p'
>>> ' '.join(reversed(string1))
'n o h t y p'

在这里,我们不想要任何分隔符;所以将分隔符设置为空字符串,以获得字符串的反向副本:

>>> ''.join(reversed(string1))
'nohtyp'

使用''.join(reversed(some-string))返回一个字符串的反向副本。

使用timeit比较执行时间

到目前为止,我们已经学习了两种反转python字符串的方法。但是哪一种更快呢?让我们来看看。

在之前的一个示例中,我们计时了简单的python表达式,没有任何setup代码。在这里,我们正在反转python字符串。虽然字符串反转操作按照number指定的次数运行,但setup代码是字符串的初始化,只运行一次。

>>> import timeit
>>> timeit.timeit(stmt = 'string_1[::-1]', setup = "string_1 = 'python'", number = 100000)
0.04951830000001678
>>> timeit.timeit(stmt = "''.join(reversed(string_1))", setup = "string_1 = 'python'", number = 100000)
0.12858760000000302

对于相同次数的字符串反转,字符串切片的方法比使用join()方法和reversed()函数更快。

你也可以阅读如何在python中反转列表以逆时针旋转它。

使用timeit计时python函数

在本节中,让我们学习如何使用timeit函数计时python函数。给定一个字符串列表,下面的函数hasdigit返回至少包含一个数字的字符串列表。

def hasdigit(somelist):
     str_with_digit = []
     for string in somelist:
         check_char = [char.isdigit() for char in string]
         if any(check_char):
            str_with_digit.append(string)
     return str_with_digit

现在我们想要使用timeit来测量这个python函数hasdigit()的执行时间。

首先,让我们确定需要计时的语句(stmt)。它是调用函数hasdigit()并将一个字符串列表作为参数的语句。接下来,让我们定义setup代码。你能猜到setup代码应该是什么吗?

为了使函数调用成功运行,setup代码应包括以下内容:

  • 函数hasdigit()的定义
  • 字符串列表参数的初始化

让我们在下面的setup字符串中定义setup代码:

setup = """
def hasdigit(somelist):
    str_with_digit = []
    for string in somelist:
      check_char = [char.isdigit() for char in string]
      if any(check_char):
        str_with_digit.append(string)
    return str_with_digit
thislist=['puffin3','7frost','blue']
     """

接下来,我们可以使用timeit函数并获得hasdigit()函数在100,000次运行中的执行时间。

import timeit
timeit.timeit('hasdigit(thislist)',setup=setup,number=100000)
# 输出
0.2810094920000097

结论

你已经学会了如何使用python的timeit函数来计时表达式、函数和其他可调用对象。这可以帮助你对代码进行基准测试,比较不同实现的相同函数的执行时间等。

让我们回顾一下在本教程中学到的内容。你可以使用语法timeit.timeit(stmt=...,setup=...,number=...)来使用timeit()函数。另外,你还可以在命令行上运行timeit来计时短的代码片段。

作为下一步,你可以探索如何使用其他python性能分析包,如line-profiler和memprofiler,来分析你的代码的时间和内存使用情况。

接下来,学习如何在python中calculate time difference

类似文章