Python中的列表理解:列表中的有效选择

Thi*_*hib 9 python list-comprehension

假设我有一个元素列表,我想根据某个函数(例如到另一个元素的距离)只选择其中一些元素.

我希望得到一个元组列表,包括距离和元素.所以,我写了下面的代码

result = [ ( myFunction(C), C) for C in originalList if myFunction(C) < limit ]
Run Code Online (Sandbox Code Playgroud)

但这myFunction是一个非常耗时的功能,而且originalList相当大.这样做,myFunction将为每个选定的元素调用两次.

那么,有没有办法避免这种情况?

我还有其他两种可能性,但它们并不是那么好:

  1. 第一个是创建未过滤的列表

    unfiltered = [ (myFunction(C),C) for C in originalList ]
    
    Run Code Online (Sandbox Code Playgroud)

    然后对它进行排序

    result = [ (dist,C) for dist,C in unfiltered if dist < limit ]
    
    Run Code Online (Sandbox Code Playgroud)

    但在那种情况下,我复制了我 originalList并浪费了一些内存(列表可能非常大 - 超过10,000个元素)

  2. 第二个是棘手的,不是非常pythonic,但有效(我们可以做的最好,因为每个元素应该评估一次函数).myFunction将它的最后
    结果存储在全局变量中(lastResult例如),并在List comprehension中重用该值

    result = [ (lastResult,C) for C in originalList if myFunction(C) < limit ]
    
    Run Code Online (Sandbox Code Playgroud)

你是否有更好的想法以高效和pythonic的方式实现这一目标?

谢谢你的回答.

ang*_*son 9

当然,以下两者之间的区别:

[f(x) for x in list]
Run Code Online (Sandbox Code Playgroud)

还有这个:

(f(x) for x in list)
Run Code Online (Sandbox Code Playgroud)

是第一个将在内存中生成列表,而第二个是新生成器,具有惰性求值.

因此,只需将"未过滤"列表作为生成器编写.这是您的代码,生成器内联:

def myFunction(x):
    print("called for: " + str(x))
    return x * x

originalList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
limit = 10
result =   [C2 for C2 in ((myFunction(C), C) for C in originalList) if C2[0] < limit]
# result = [C2 for C2 in [(myFunction(C), C) for C in originalList] if C2[0] < limit]
Run Code Online (Sandbox Code Playgroud)

请注意,您不会看到打印输出与两者的区别,但如果您要查看内存使用情况,则注释掉的第二个语句将使用更多内存.

要在您的问题中对代码进行简单更改,请按原样重写未经过滤的代码:

unfiltered = [ (myFunction(C),C) for C in originalList ]
             ^                                         ^
             +---------- change these to (..) ---------+
                                 |
                                 v
unfiltered = ( (myFunction(C),C) for C in originalList )
Run Code Online (Sandbox Code Playgroud)