【问题标题】:Reusing an expression in a comprehension在理解中重用表达式
【发布时间】:2020-06-04 21:41:43
【问题描述】:

是否可以在推导式中重用可能很大、计算成本高或非幂等的子表达式(如生成器)?

假设有一个要转换为字典的字符串列表:

items = ['ab: 1', 'cd: 2', 'ef:3']  ===>  {'ab': '1', 'cd': '2', 'ef': '3'}

循环计算一次“昂贵”的拆分表达式:

d = {}
for item in items:
    k, v = item.split(':', maxsplit=1)
    d.update({k.strip(): v.strip()})

理解为每个输出元素重复计算:

d = {x.split(':', maxsplit=1)[0].strip(): x.split(':', maxsplit=1)[1].strip() for x in items}

但所需的解决方案是计算表达式一次然后重用它的理解:

d = {k.strip(): v.strip() for x in items for k, v in x.split(':', maxsplit=1)}     # Wrong - need an assignment

可以吗?

【问题讨论】:

  • 在这种情况下你有d = {k.strip(): v.strip() for x in items for k, v in [x.split(':')]},但我认为这并不能一概而论。你看过python.org/dev/peps/pep-0572吗?
  • 问题不在于它不能泛化,问题在于它不起作用。这是一个语法正确的理解,但它循环分割输出而不是分配它。
  • 如果你有多个元素,你就无法摆脱循环。您的玩具示例中昂贵的操作是拆分,它只发生一次(items 中的每个元素当然是必要的)。此外,for k, v in [x.split(':')] 只有一个循环迭代。它采用单元素列表[['ab', '1']] 并使用元组解包将其映射到kv
  • PEP 572 正是我们所需要的——我正在运行 Python 3.7,但将安装 3.8 来尝试它。可惜实现了这么长时间,似乎即使在 Python 2 中对赋值表达式的需求也是显而易见的。这是 40 年前的 C 语言。

标签: python dictionary-comprehension


【解决方案1】:

如果您不需要字典解析,dict 对象已经接受了元组:

dict(map(str.strip, x.split(':')) for x in items)

# {'ab': '1', 'cd': '2', 'ef': '3'}

如果你愿意,这将是 dict 理解:

{k: v for k, v in [map(str.strip, x.split(':')) for x in items]}

# {'ab': '1', 'cd': '2', 'ef': '3'}

【讨论】:

  • 谢谢 - 我的工具箱里已经有了压缩包,但我显然也需要把地图放在那里。
  • 我以前用过 - 我怎么会错过它? dict 将全部消耗掉。
【解决方案2】:
a = ['ab: 1', 'cd: 2', 'ef:3']

使用带有地图和字典的生成器表达式理解

In [46]: b = (map(str.strip,thing.split(':')) for thing in a)

In [47]: d = {k:v for (k,v) in b}

In [48]: d
Out[48]: {'ab': '1', 'cd': '2', 'ef': '3'}

a 中的每个项目都被拆分一次,结果列表中的项目被剥离一次。这些函数的执行会延迟到字典 comprehension 被执行。

【讨论】:

    猜你喜欢
    • 2020-05-07
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-29
    相关资源
    最近更新 更多