私がこれを行う場合:
x=[(t,some_very_complex_computation(y)) for t in z]
どうやら some_very_complex_computation(y) は t に依存していないようです。したがって、一度だけ評価する必要があります。Python にこれを認識させる方法はありますか?
編集:私は本当にそれを1行でやりたいです...
私がこれを行う場合:
x=[(t,some_very_complex_computation(y)) for t in z]
どうやら some_very_complex_computation(y) は t に依存していないようです。したがって、一度だけ評価する必要があります。Python にこれを認識させる方法はありますか?
編集:私は本当にそれを1行でやりたいです...
通常、San4ez のアドバイスに従い、一時変数をここで使用するだけです。特定の状況下で役立つと思われるテクニックをいくつか紹介します。
一般に、サブ式のためだけに名前をバインドしたい場合 (これが通常、一時変数が必要な理由です)、ラムダを使用できます。
x = (lambda result=some_very_complex_computation(y): [(t, result) for t in z])()
この特定のケースでは、以下は非常にクリーンで読みやすいソリューションです。
x = zip(z, itertools.repeat(some_very_complex_computation(y)))
some_very_complex_computation
Python のような動的言語では、が参照透過的であること、つまり、同じ引数に対して常に同じ結果を返すことを実装が理解するのは非常に困難です。そのような魔法が必要な場合は、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]
すべてを 1 行にまとめたいというひねくれた衝動は理解できますが、同時に、物事を読みやすく保つことは良いことです。これはバージョンよりも読みやすいと考えることができます:lambda
x=[(t,s) for s in [some_very_complex_calculation(y)] for t in z]
ただし、San4ez による答えは、シンプルで読みやすい (そして、1 つの要素リストを作成して反復するよりもおそらく高速である) 方がよいでしょう。
次のいずれかを実行できます。
呼び出しをリスト内包表記の外に移動します
また
メモ化を使用します (つまり、 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
これであなたの問題も解決します。
ワンライナーで
それらのためにワンライナーを実行するのはコーディングが不十分ですが、それでも...本当に1行で実行したい場合:
>>> 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 は、伝統的で、正しく、シンプルで、美しい答えを出しました。
ただし、ワンライナーの精神で、すべてを 1 つのステートメントにまとめるテクニックを次に示します。コアとなるアイデアは、ネストされた for ループを使用して部分式を事前評価することです。
result = [(t, result) for result in [some_very_complex_computation(y)] for t in z]
それが気になる場合は、セミコロンを使用して複数のステートメントを 1 行に入れることができます。
result = some_very_complex_computation(y); x = [(t, result) for t in z]
関数に副作用があり、実行ごとに変化するかどうかを知ることができないため、リスト内包表記から手動で呼び出しを移動する必要があります。