您寻求let
在 python 列表推导中具有 -statement 语义,其范围可用于推导的___ for..in
(map) 和if ___
(filter) 部分,其范围取决于..for ___ in...
.
你的解决方案,修改:
你的(你承认不可读的)解决方案[ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]
是编写优化的最直接的方法。
主要思想:将 x 提升到元组 (x,f(x)) 中。
有些人会争辩说,最“pythonic”的做事方式将是原始的[(x,f(x)) for x in iterable if f(x)]
并接受低效率。
但是,如果您打算经常这样做,您可以将其((y,fy) for y in iterable)
分解为一个函数。这很糟糕,因为如果您希望访问比x,fx
(例如x,fx,ffx
)更多的变量,那么您将需要重写所有列表推导。因此,除非您确定您只需要x,fx
并计划重用此模式,否则这不是一个很好的解决方案。
生成器表达式:
主要思想:使用更复杂的生成器表达式替代方法:python 可以让您编写多行。
您可以只使用生成器表达式,python 可以很好地使用它:
def xfx(iterable):
for x in iterable:
fx = f(x)
if fx:
yield (x,fx)
xfx(exampleIterable)
这就是我个人的做法。
记忆/缓存:
主要思想:您还可以使用(滥用?)副作用并制作f
一个全局记忆缓存,这样您就不会重复操作。
这可能会产生一些开销,并且需要一个关于缓存应该有多大以及何时应该进行垃圾收集的策略。因此,只有当您对记忆 f 有其他用途,或者 f 非常昂贵时,才应该使用它。但它会让你写...
[ (x,f(x)) for x in iterable if f(x) ]
...就像您最初想要的那样,没有两次执行昂贵操作的性能f
损失,即使您在技术上调用它两次。您可以将@memoized
装饰器添加到f
:示例(没有最大缓存大小)。只要 x 是可散列的(例如数字、元组、冻结集等),这将起作用。
虚拟值:
主要思想:在闭包中捕获 fx=f(x) 并修改列表理解的行为。
filterTrue(
(lambda fx=f(x): (x,fx) if fx else None)() for x in iterable
)
其中 filterTrue(iterable) 是 filter(None, iterable)。如果您的列表类型(一个 2 元组)实际上能够成为None
.