列表推导式/生成器中的海象赋值表达式

4
我想把foo_list中的每个元素传递给函数expensive_call,并获取所有输出为真值的项目列表。我想使用列表推导式来实现,这有可能吗?例如:

类似以下代码:

 result_list = [y := expensive_call(x) for x in foo_list if y]

or....

 result_list = [y for x in foo_list if y := expensive_call(x)]

注意:这不是解决方案,因为它会调用两次昂贵的调用。
 result_list = [expensive_call(x) for x in foo_list if expensive_call(x)]

如果有人推荐使用“none list comprehension”,在此之前我需要说明一下,我们可以这样做:

result_list = []
for x in foo_list:
   result = expensive_call(x)
   result and result_list.append(result)

5
“列出 expensive_call 输出为真值的所有项。” 等等,这是否意味着您希望结果包含输入中的原始元素?难道这不仅仅是 [x for x in foo_list if expensive_call(x)] 吗?有何困难?(这也对应于您的非压缩版本的代码。) - Karl Knechtel
3
你的第二个版本有什么问题?你试过了吗? - RufusVS
3
这段代码的意思是:对于foo_list中的每个元素,调用expensive_call函数并将结果存储在一个列表中。只有当调用返回一个真值时才会将其添加到列表中。 - RufusVS
3
在您的循环版本中,避免使用“result and result_list.append(x)”这样可爱的东西,而是使用“if result: result_list.append(x)” ,习惯用法Python强调清晰明确,而非简洁。 - juanpa.arrivillaga
1
注意,在赋值表达式出现之前,可以使用以下方式进行操作:[y for x in data for y in [expensive_call(x)] if y]。这是一个非常常见的习惯用法,实际上有一种字节码优化方法,它不会创建一个列表 [expensive_call(x)],而是直接将 expensive_call(x) 的结果分配给本地变量 y。个人而言,我更喜欢使用普通的循环。 - juanpa.arrivillaga
显示剩余2条评论
3个回答

7

引用上文:

result_list = [y for x in foo_list if y := expensive_call(x)]

这个代码基本上应该能够正常工作,只需要在赋值操作符 := 周围加上括号即可,如下所示。

foo_list = [1, 2, 3]
def check(x): return x if x != 2 else None

result_list = [y for x in foo_list if (y := check(x))]
print(result_list)

结果:

[1, 3]

没有括号会失败吗? - RufusVS
是的,对我来说肯定会失败;至少在我使用 Python 3.9 环境进行测试时是这样。 - rv.kvetch
有趣。我想知道它是如何解析的。 - RufusVS
1
从关于海象运算符的PEP来看,似乎有一些边缘情况需要在表达式周围加上(); 说实话,我不确定为什么在这里需要它。 - rv.kvetch

2

正如RufusVS所建议的那样,这个方法是可行的并且具有预期的复杂度。

def expensive_call(x):
    return x == 5

foo_list = [1,2,3,4,5,6,6,5]
print([y for y in map(expensive_call,foo_list) if y])

导致
[True, True]

由于有两个5满足expensive_call,因此......


0
你想检查 expensive_call(x) 是否为真,并且如果是,则将其附加到列表中?像这样的东西应该可以工作。不需要使用海象运算符:
[x for x in foo_list if expensive_call(x)]

如果你想要存储 expensive_call(x) 的输出而不是 x,那么你的第二种方法需要在海象赋值周围加上括号,因为赋值运算符的工作方式。请参考 PEP 572 获取更多信息。
[y for x in foo_list if (y := expensive_call(x))]

2
OP想要保存调用的结果,而不是参数。据我所知。 - RufusVS
如果你提到OP的不理解,他们使用result_list.append(x)而不是result_list.append(result),并且他们说他们想要存储项目,如果函数的输出为真,而不是存储函数的输出,如果它是真的。 - Jab
我甚至没有读他发布的循环版本。显然它的逻辑与他的其他尝试不同,但现在我明白你的想法了。 - RufusVS
对于造成的困扰,我已经纠正了我的代码,感谢您! - run_the_race

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