假设我有一个元素列表,我想根据某个函数(例如 distance 到另一个元素)只选择其中一些元素。
我希望得到一个元组列表,包括距离和元素。所以,我写了以下代码
result = [ ( myFunction(C), C) for C in originalList if myFunction(C) < limit ]
但是myFunction
是一个非常耗时的功能,originalList
非常大。这样做,myFunction
将为每个选定的元素调用两次。
那么,有没有办法避免这种情况?
我还有其他两种可能性,但它们并不是那么好:
第一个,就是创造了 未过滤的清单
unfiltered = [ (myFunction(C),C) for C in originalList ]
然后对其进行排序
result = [ (dist,C) for dist,C in unfiltered if dist < limit ]
但在那种情况下,我复制我的
originalList
并浪费一些记忆
(清单可能很大 - 更多
超过10,000个元素)
第二个是棘手的,不是非常pythonic,但有效(我们可以做的最好,因为每个元素应该评估一次函数)。 myFunction
最后存储它
得到一个全局变量(例如lastResult
),并在该值中重用该值
列表理解
result = [ (lastResult,C) for C in originalList if myFunction(C) < limit ]
你是否有更好的想法以高效和pythonic的方式实现这一目标?
感谢您的回答。
答案 0 :(得分:9)
当然,以下两者之间存在差异:
[f(x) for x in list]
和此:
(f(x) for x in list)
是第一个将在内存中生成列表,而第二个是新生成器,具有惰性求值。
因此,只需将“未过滤”列表作为生成器编写。这是您的代码,生成器内联:
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]
请注意,您不会看到打印输出与两者的区别,但如果您要查看内存使用情况,则注释掉的第二个语句将使用更多内存。
要在您的问题中对代码进行简单更改,请按原样重写:
unfiltered = [ (myFunction(C),C) for C in originalList ]
^ ^
+---------- change these to (..) ---------+
|
v
unfiltered = ( (myFunction(C),C) for C in originalList )
答案 1 :(得分:3)
不要使用列表理解;一个正常的循环就好了。
答案 2 :(得分:3)
只需预先计算距离,然后过滤结果:
with_distances = ((myFunction(C), C) for C in originalList)
result = [C for C in with_distances if C[0] < limit]
注意:我使用生成器表达式来构建距离/元素对,而不是构建新列表。
答案 3 :(得分:1)
一些选项:
答案 4 :(得分:1)
Lasse V. Karlsen对您的问题给出了很好的答复。
如果你的距离计算很慢,我想你的元素是折线,或类似的东西,对吗?
有很多方法可以让它更快:
如果对象的边界框之间的距离是> X,那么这些对象之间的距离是> X.所以你只需要计算边界框之间的距离。
如果您想要距对象A的距离小于X的所有对象,则只有边界框与A放大X的边界框相交的对象才是潜在匹配。
使用第二个点你可能会丢弃很多候选匹配,只在需要时进行慢速计算。
必须事先缓存边界框。
如果你真的有很多对象,你也可以使用空间分区......
如果您处于3D中,则为凸包围多边形
答案 5 :(得分:0)
不是像选项2那样使用全局变量,而是可以依赖于Python参数是由对象传递的事实 - 也就是说,传递给myFunction
函数的对象是与列表中的对象相同的对象(这与通过引用调用不完全相同,但它足够接近)。
因此,如果你的myFunction在对象上设置了一个属性 - 比如_result
- 你可以按该属性过滤:
result = [(_result, C) for C in originalList if myFunction(C) < limit]
你的myFunction可能如下所示:
def myFunction(obj):
obj._result = ... calculation ....
return obj._result
答案 6 :(得分:0)
选项1有什么问题?
“复制我的原始列表并浪费一些内存(列表可能非常大 - 超过10,000个元素)”
10,000个元素只有10,000个指向指向现有对象的元组的指针。想想160K左右的内存。非常值得一谈。