如何在列表理解中设置局部变量?

时间:2014-10-31 10:31:45

标签: 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

我想获得一个包含所有不是None的映射元素的列表。

像这样:

v_list = [v1, v2, v3, v4]

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

但是在列表解析中两次调用map_to_obj方法似乎并不好。

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

或者编译器是否自动对其进行优化?

这就是我想要的:

(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end] 

7 个答案:

答案 0 :(得分:50)

使用嵌套列表理解:

[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]

答案 1 :(得分:5)

变量分配只是单数绑定:

[x   for v in l   for x in [v]]

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

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

答案 2 :(得分:4)

您可以使用python内置filter

来避免重新计算
list(filter(lambda t: t is not None, map(map_to_obj, v_list)))

答案 3 :(得分:3)

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

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

[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]

答案 4 :(得分:2)

局部变量可以在理解中通过欺骗一点并使用额外的'for'来设置,该'for'“迭代”通过包含局部变量的期望值的1元素元组。以下是使用此方法解决OP问题的方法:

[o for v in v_list for o in (map_to_obj(v),) if o]

此处,o是为每个map_to_obj(v)设置的局部变量等于v

在我的测试中,这比Lying Dog的嵌套生成器表达式稍快(并且也比OP对map_to_obj(v)的双重调用更快,令人惊讶的是,如果{{1},它可能比嵌套生成器表达式更快功能不是太慢了。)

答案 5 :(得分:1)

列表推导适用于简单的情况,但有时一个普通的旧for循环是最简单的解决方案:

other_list = []
for v in v_list:
    obj = map_to_obj(v)
    if obj:
        other_list.append(obj)

现在,如果你真的想要一个列表comp并且不想构建一个tmp列表,你可以使用filtermap的迭代器版本:

import itertools as it
result = list(it.ifilter(None, it.imap(map_to_obj, v_list)))

或更简单:

import itertools as it
result = filter(None, it.imap(map_to_obj, v_list)))

迭代器版本不构建临时列表,它们使用延迟评估。

答案 6 :(得分:0)

我找到了一种使用reduce的方式:

def map_and_append(lst, v):
    mapped = map_to_obj(v)
    if mapped is not None:
        lst.append(mapped)
    return lst

reduce(map_and_append, v_list, [])

这个表现怎么样?