如何在列表理解中添加额外的中间步骤?

Sem*_*nic 26 python list-comprehension

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

timestamps = ["22:58", "03:11", "12:21"]
Run Code Online (Sandbox Code Playgroud)

我想将其转换为list[int]每个时间戳的“自午夜以来的分钟数”值的对象:

converted = [22*60+58, 3*60+11, 12*60+21]
Run Code Online (Sandbox Code Playgroud)

...但我想以风格来完成它并使用单个列表理解来完成它。我天真的构建的(语法不正确的)实现类似于:

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

...但这不起作用,因为这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]
Run Code Online (Sandbox Code Playgroud)

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

wja*_*rea 33

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

[int(hh)*60 + int(mm) for hh, mm in (ts.split(':') for ts in timestamps)]
Run Code Online (Sandbox Code Playgroud)

虽然就我个人而言,我宁愿使用辅助函数:

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))
Run Code Online (Sandbox Code Playgroud)


And*_*ely 13

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

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

印刷:

[1378, 191, 741]
Run Code Online (Sandbox Code Playgroud)

选择:

[1378, 191, 741]
Run Code Online (Sandbox Code Playgroud)

印刷:

[1378, 191, 741]
Run Code Online (Sandbox Code Playgroud)

注意:是 Python 3.8+:=的一项功能,通常称为“海象运算符”。这是带有提案的PEP 。

  • @VishwaMittar `:=` 是 Python 3.8+ 的功能。以下是该提案的 PEP:https://peps.python.org/pep-0572/ (5认同)
  • 您能否解释或分享“:=”这个赋值运算符的任何指南。在我两年的职业生涯中,我第一次看到它,它看起来非常有用。 (2认同)

小智 13

你最初的伪代码

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

与您可以做的非常接近:

[int(hh) * 60 + int(mm) for ts in timestamps for hh, mm in [ts.split(':')]]
Run Code Online (Sandbox Code Playgroud)

在 Python 3.9 中,这样的表达式经过了优化,因此在推导式中创建单元素数组只是为了立即访问其单个元素,就像简单的赋值一样快。

  • @PM2Ring:“当我使用这个技巧时,我通常会对其进行评论。” 那么我们同意。;) (3认同)
  • 优化很有趣,感谢您的链接。不过,我发现“for y in [expr]”语法有点奇怪。 (2认同)
  • @EricDuminil 这并不是真的*那么*奇怪。`.split` 返回字符串列表。我们不想一一循环这些字符串,我们希望一步将它们解压到我们的循环变量(hh 和 mm)中,并且使拆分列表成为列表的单个项目可以让我们做到这一点。我想这可能不是一个*明显的*习语,但我不是荷兰人。:) 当我使用这个技巧时,我通常会对其进行评论。 (2认同)

Eri*_*nil 11

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

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]
Run Code Online (Sandbox Code Playgroud)

您可以将推导式拆分为多个步骤,写在多行上,并且不需要定义任何函数。


Pat*_*ner 5

聚会迟到了..但为什么不使用 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))
Run Code Online (Sandbox Code Playgroud)

输出:

[1378, 191, 741]
Run Code Online (Sandbox Code Playgroud)