如何在列表推导中添加额外的中间步骤?



假设我有一个list[str]对象,它包含"HH:mm"格式的时间戳,例如

timestamps = ["22:58", "03:11", "12:21"]

我想将其转换为list[int]对象,并带有"自午夜以来的分钟数";每个时间戳的值:

converted = [22*60+58, 3*60+11, 12*60+21]

…但我想做得有风格,用一个单列表推导来做。我天真地构造的一个(语法错误)实现类似于:

def timestamps_to_minutes(timestamps: list[str]) -> list[int]:
return [int(hh) * 60 + int(mm) for ts in timestamps for hh, mm = ts.split(":")]

…但这不起作用,因为for hh, mm = ts.split(":")不是有效的语法。

相同内容的有效写法是什么?

澄清一下:我可以看到一个形式上令人满意的解决方案:

def timestamps_to_minutes(timestamps: list[str]) -> list[int]:
return [int(ts.split(":")[0]) * 60 + int(ts.split(":")[1]) for ts in timestamps]

…但这是非常低效的,我不想分割字符串两次。

您可以使用内部生成器表达式来进行拆分:

[int(hh)*60 + int(mm) for hh, mm in (ts.split(':') for ts in timestamps)]

尽管就我个人而言,我宁愿使用辅助函数来代替:

def timestamp_to_minutes(timestamp: str) -> int:
hh, mm = timestamp.split(":")
return int(hh)*60 + int(mm)
[timestamp_to_minutes(ts) for ts in timestamps]
# Alternative
list(map(timestamp_to_minutes, timestamps))

初始伪代码

[int(hh) * 60 + int(mm) for ts in timestamps for hh, mm = ts.split(":")]

非常接近你可以做的事:

[int(hh) * 60 + int(mm) for ts in timestamps for hh, mm in [ts.split(':')]]

在Python 3.9中,这样的表达式被优化了,以便在推导式中创建一个单元素数组,只是为了立即访问它的单个元素,与简单的赋值操作一样快。

如果你不想分割字符串两次,你可以使用:=赋值运算符:

timestamps = [int((s := t.split(":"))[0]) * 60 + int(s[1]) for t in timestamps]
print(timestamps)

打印:

[1378, 191, 741]
替代:

print([int(h) * 60 + int(m) for h, m in (t.split(":") for t in timestamps)])

打印:

[1378, 191, 741]

注意::=是Python 3.8+的一个特性通常称为"海象操作符"。这是PEP建议。

如果在中间步骤中使用生成器(而不是列表推导式),则整个列表仍将在一次传递中转换:

timestamps = ["22:58", "03:11", "12:21"]
#NOTE: Use () for generators, not [].
hh_mms = (timestamp.split(':') for timestamp in timestamps)
converted = [int(hh) * 60 + int(mm) for (hh, mm) in hh_mms]
print(converted)
# [1378, 191, 741]

您可以将理解分为多个步骤,在多行中编写,并且不需要定义任何函数。

来晚了。但是为什么不使用datetime/timedelta来转换时间呢?

为"hh: mm"这可能有点夸张,但您可以轻松地将其调整为更复杂的时间字符串:

from datetime import datetime as dt
import typing
def timestamps_to_minutes(timestamps: typing.List[str]) -> typing.List[any]:
"""Uses datetime.strptime to parse a datetime string and return
minutes spent in this day."""
return [int(((p := dt.strptime(t,"%H:%M")) - dt(p.year,p.month, p.day)
).total_seconds()//60) for t in timestamps]
timestamps = ["22:58", "03:11", "12:21"]
print(timestamps_to_minutes(timestamps))

输出:

[1378, 191, 741]

为了好玩,我们也可以使用operator.methodcaller:

from operator import methodcaller
out = [int(h) * 60 + int(m) for h, m in map(methodcaller("split", ":"), timestamps)]

输出:

[1378, 191, 741]

最新更新