加速datetime.strptime函数的执行

4

我正在使用下面的代码从字符串中提取日期:

try:
    my_date = datetime.strptime(input_date, "%Y-%m-%d").date()
except ValueError:
    my_date = None

如果我运行750,000次,它需要19.144秒(使用cProfile确定)。现在我用下面的(丑陋的)代码替换它:

a= 1000 * int(input_date[0])
b=  100 * int(input_date[1])
c=   10 * int(input_date[2])
d=    1 * int(input_date[3])
year = a+b+c+d

c=   10 * int(input_date[5])
d=    1 * int(input_date[6])
month = c+d

c=   10 * int(input_date[8])
d=    1 * int(input_date[9])
day = c+d

try:
    my_date = date(year, month, day)
except ValueError:
    my_date = None

如果我运行这个程序750,000次,只需要5.946秒。但是,我觉得代码非常丑陋。有没有另一种快速从字符串中提取日期的方法,而不使用strptime?

使用 timeit 进行时间测试,而不是 cProfile。我并不是说结果会有所不同,但这样做肯定更准确。 - Martijn Pieters
1
为什么不使用 year = int(input_date[:4]) 这样的代码呢?try 语句块是用来防止无效的日期格式导致索引失败的。 - jonrsharpe
@martijn:我使用cProfile的原因是我需要所有方法的近似结果,而不仅仅是这一个方法。 - physicalattraction
@physicalattraction:但是在这篇文章中,你正在谈论这个。如果您想运行时间试验以比较单个任务的方法,请使用timeit - Martijn Pieters
1
@physicalattraction:顺便说一下,strptime确实比你的丑陋方法慢(大约慢2倍),因为它在输入上执行了更多的验证。例如,它可以处理未零填充的月份和日期。 - Martijn Pieters
@jon:年份=int(input_date[:4])确实是个好的提示,我现在正在使用它。它甚至可以更快(3.3秒)。我想我也得把这些命令放在try里面。 - physicalattraction
1个回答

6

是的,如果你放弃很多灵活性和验证,解析日期的速度比datetime.strptime()更快。 strptime()允许带有或不带有零填充的数字,并且仅匹配使用正确分隔符的字符串,而您的“丑陋”版本则不会。

您应该始终使用timeit模块进行时间测试,它比cProfile更准确。

实际上,您的“丑陋”方法比strptime()快两倍:

>>> from datetime import date, datetime
>>> import timeit
>>> def ugly(input_date):
...     a= 1000 * int(input_date[0])
...     b=  100 * int(input_date[1])
...     c=   10 * int(input_date[2])
...     d=    1 * int(input_date[3])
...     year = a+b+c+d
...     c=   10 * int(input_date[5])
...     d=    1 * int(input_date[6])
...     month = c+d
...     c=   10 * int(input_date[8])
...     d=    1 * int(input_date[9])
...     day = c+d
...     try:
...         my_date = date(year, month, day)
...     except ValueError:
...         my_date = None
... 
>>> def strptime(input_date):
...     try:
...         my_date = datetime.strptime(input_date, "%Y-%m-%d").date()
...     except ValueError:
...         my_date = None
... 
>>> timeit.timeit('f("2014-07-08")', 'from __main__ import ugly as f')
4.21576189994812
>>> timeit.timeit('f("2014-07-08")', 'from __main__ import strptime as f')
9.873773097991943

你的方法可以改进一下,可以使用切片操作:
>>> def slicing(input_date):
...     try:
...         year = int(input_date[:4])
...         month = int(input_date[5:7])
...         day = int(input_date[8:])
...         my_date = date(year, month, day)
...     except ValueError:
...         my_date = None
... 
>>> timeit.timeit('f("2014-07-08")', 'from __main__ import slicing as f')
1.7224829196929932

现在速度快了近6倍。我还将int()调用移动到try-except中,以处理在将字符串转换为整数时出现的无效输入。
你也可以使用str.split()来获取部分,但这会使它稍微变慢:
>>> def split(input_date):
...     try:
...         my_date = date(*map(int, input_date.split('-')))
...     except ValueError:
...         my_date = None
... 
>>> timeit.timeit('f("2014-07-08")', 'from __main__ import split as f')
2.294667959213257

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接