如果我这样做:
x=[(t,some_very_complex_computation(y)) for t in z]
显然 some_very_complex_computation(y) 不依赖于 t。所以它应该只评估一次。有没有办法让 Python 意识到这一点,所以它不会为每次迭代评估 some_very_complex_computation(y) ?
编辑:我真的很想在一行中做到这一点......
如果我这样做:
x=[(t,some_very_complex_computation(y)) for t in z]
显然 some_very_complex_computation(y) 不依赖于 t。所以它应该只评估一次。有没有办法让 Python 意识到这一点,所以它不会为每次迭代评估 some_very_complex_computation(y) ?
编辑:我真的很想在一行中做到这一点......
通常你应该听从 San4ez 的建议,在这里只使用一个临时变量。我仍将介绍一些在某些情况下可能有用的技术:
通常,如果您只想为子表达式绑定名称(这通常是您需要临时变量的原因),您可以使用 lambda:
x = (lambda result=some_very_complex_computation(y): [(t, result) for t in z])()
在这种特殊情况下,以下是一个非常干净且可读的解决方案:
x = zip(z, itertools.repeat(some_very_complex_computation(y)))
在像 Python 这样的动态语言中,实现将很难确定引用透明some_very_complex_computation
,也就是说,对于相同的参数,它总是会返回相同的结果。如果你想要这样的魔法,你可能想研究像 Haskell 这样的函数式语言。
但是,您可以做的是some_very_complex_computation
显式缓存其最近参数的返回值:
from functools import lru_cache
@lru_cache()
def some_very_complex_computation(y):
# ...
这是 Python 3。在 Python 2 中,您必须自己编写装饰器:
from functools import wraps
def memoize(f):
cache = {}
@wraps(f)
def memoized(*args):
if args in cache:
return cache[args]
res = cache[args] = f(*args)
return res
return memoized
@memoize
some_very_complex_computation(x):
# ...
不,您应该将值保存在变量中
result = some_very_complex_computation(y)
x = [(t, result) for t in z]
我理解有时将所有内容放在一行中的不正当冲动,但同时保持可读性是好的。您可能认为这比lambda
版本更具可读性:
x=[(t,s) for s in [some_very_complex_calculation(y)] for t in z]
但是,您可能更好地选择 San4ez 的答案,因为它简单、易读(并且可能比创建和迭代一个元素列表更快)。
您可以:
将调用移出列表理解
或者
使用记忆化(即当 some_very_complex_computation(y) 被调用时将结果存储在字典中,如果再次使用相同的值调用它,则返回存储在字典中的值
TL;DR 版本
zip(z, [long_computation(y)] * len(z))
原答案:
根据经验,如果您有一些执行时间很长的计算,最好将结果直接缓存在函数中,如下所示:
_cached_results = {}
def computation(v):
if v in _cached_results:
return _cached_results[v]
# otherwise do the computation here...
_cached_results[v] = result
return result
这也可以解决您的问题。
单线
为了他们而做单行代码是糟糕的编码,但是......如果你真的想在一行中做:
>>> def func(v):
... print 'executing func'
... return v * 2
...
>>> z = [1, 2, 3]
>>> zip(z, [func(10)] * len(z))
executing func
[(1, 20), (2, 20), (3, 20)]
@San4ez 给出了传统的、正确的、简单的、漂亮的答案。
不过,本着单线的精神,这是一种将所有内容放在一个语句中的技术。核心思想是使用嵌套的 for 循环来预评估子表达式:
result = [(t, result) for result in [some_very_complex_computation(y)] for t in z]
如果这让您大吃一惊,您可以使用分号将多个语句放在一行中:
result = some_very_complex_computation(y); x = [(t, result) for t in z]
它无法知道函数是否有副作用以及从运行到运行的变化,因此您必须手动将调用移出列表推导。