4

如果我这样做:

x=[(t,some_very_complex_computation(y)) for t in z]

显然 some_very_complex_computation(y) 不依赖于 t。所以它应该只评估一次。有没有办法让 Python 意识到这一点,所以它不会为每次迭代评估 some_very_complex_computation(y) ?

编辑:我真的很想在一行中做到这一点......

4

7 回答 7

7

通常你应该听从 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):
  # ...
于 2012-04-21T13:39:32.983 回答
4

不,您应该将值保存在变量中

result = some_very_complex_computation(y)
x = [(t, result) for t in z]
于 2012-04-21T13:32:42.107 回答
3

我理解有时将所有内容放在一行中的不正当冲动,但同时保持可读性是好的。您可能认为这比lambda版本更具可读性:

x=[(t,s) for s in [some_very_complex_calculation(y)] for t in z]

但是,您可能更好地选择 San4ez 的答案,因为它简单、易读(并且可能比创建和迭代一个元素列表更快)。

于 2012-04-21T13:52:33.130 回答
1

您可以:

将调用移出列表理解

或者

使用记忆化(即当 some_very_complex_computation(y) 被调用时将结果存储在字典中,如果再次使用相同的值调用它,则返回存储在字典中的值

于 2012-04-21T13:41:26.660 回答
1

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)]
于 2012-04-21T13:43:38.907 回答
1

@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] 
于 2012-04-21T14:18:16.673 回答
0

它无法知道函数是否有副作用以及从运行到运行的变化,因此您必须手动将调用移出列表推导。

于 2012-04-21T13:34:42.433 回答