Python:如何在列表推导中设置局部变量?

Hao*_*Tan 26 python list-comprehension

我有一个方法,可以获取一个列表并返回一个对象

# input a list, returns an object
def map_to_obj(lst):
    a_list = f(lst)
    return a_list[0] if a_list else None
Run Code Online (Sandbox Code Playgroud)

我想获得一个包含所有未映射的元素的列表None.

像这样:

v_list = [v1, v2, v3, v4]

[map_to_obj(v) for v in v_list if map_to_obj(v)]
Run Code Online (Sandbox Code Playgroud)

但是map_to_obj在列表推导中调用该方法两次似乎并不好.

有没有办法在列表推导中使用局部变量,以便它可以有更好的性能?

或者编译器是否自动优化它?

这就是我想要的:

(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end] 
Run Code Online (Sandbox Code Playgroud)

Lyi*_*Dog 52

使用嵌套列表理解:

[x for x in [map_to_obj(v) for v in v_list] if x]

或者更好的是,围绕生成器表达式的列表理解:

[x for x in (map_to_obj(v) for v in v_list) if x]

  • 内在理解应该是一个生成器表达式.无需构建整个列表,只有丢弃空项才能构建另一个列表. (3认同)

Xav*_*hot 8

从开始Python 3.8,并引入赋值表达式(PEP 572):=运算符),可以在列表推导中使用局部变量,以避免调用同一函数两次:

在我们的例子中,我们可以在使用表达式的结果来过滤列表时将的评估命名map_to_obj(v)为变量o。并因此o用作映射值:

[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]
Run Code Online (Sandbox Code Playgroud)

  • 这还有一个好处就是不用遍历列表两次。 (2认同)
  • 注意这个!如果出于某种原因,“map_to_obj(v)”将值“0”分配给“o”,则该项目将从列表中省略。相反,我建议使用 `[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v)) is not False]` 这样,零赋值就可以了。天哪,我花了太长时间才弄清楚...... (2认同)

beh*_*uri 5

您可以使用内置的python来避免重新计算filter

list(filter(lambda t: t is not None, map(map_to_obj, v_list)))
Run Code Online (Sandbox Code Playgroud)

  • @HaoTan不是python 3; 在python 3中,`map`返回一个地图对象而不是一个列表,而`filter`返回一个过滤器对象;因此,这将在不创建中间列表的情况下对函数进行链接。 (2认同)

Ova*_*flo 5

可以通过作弊并使用额外的“for”在推导式中设置局部变量,“for”“迭代”包含局部变量所需值的 1 元素元组。这是使用这种方法解决OP问题的方法:

[o for v in v_list for o in (map_to_obj(v),) if o]
Run Code Online (Sandbox Code Playgroud)

这里,o局部变量被设置为等于map_to_obj(v)每个v

在我的测试中,这比 Lying Dog 的嵌套生成器表达式稍快(也比 OP 的双重调用要快,令人惊讶的是,如果函数不太慢的map_to_obj(v)话,它可能比嵌套生成器表达式更快)。map_to_obj


小智 5

变量赋值只是单数绑定:

[x   for v in l   for x in [v]]
Run Code Online (Sandbox Code Playgroud)

这是一个更一般的答案,也更接近您的建议。因此,对于您的问题,您可以编写:

[x   for v in v_list   for x in [map_to_obj(v)]   if x]
Run Code Online (Sandbox Code Playgroud)