加速 datetime.strptime

4 投票
1 回答
1262 浏览
提问于 2025-04-18 12:27

我正在用以下这段代码从一个字符串中提取日期:

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?

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

撰写回答