22

Python のリスト内包表記構文を使用すると、内包内の値を簡単にフィルター処理できます。例えば:

result = [x**2 for x in mylist if type(x) is int]

mylist 内の整数の 2 乗のリストを返します。しかし、テストに (コストのかかる) 計算が含まれていて、結果をフィルタリングしたい場合はどうでしょうか? 1 つのオプションは次のとおりです。

result = [expensive(x) for x in mylist if expensive(x)]

これにより、"偽" ではない高価な (x) 値のリストが生成されますが、それぞれの x に対して高価な () が 2 回呼び出されます。xごとに1回だけ高価な呼び出しを行いながら、このテストを実行できる理解構文はありますか?

4

9 に答える 9

25

少し考えた後、自分なりの答えを思いつきました。ネストされた内包表記で行うことができます:

result = [y for y in (expensive(x) for x in mylist) if y]

ネストされた内包表記はわずかにしか読めないことがわかりますが、それはうまくいくと思います

于 2008-09-24T22:12:24.237 に答える
21

計算がすでに関数にうまくまとめられている場合は、filterandを使用するのはどうmapですか?

result = filter (None, map (expensive, mylist))

itertools.imapリストが非常に大きい場合に使用できます。

于 2008-09-24T22:23:50.137 に答える
7

最も明白な (そして私が最も読みやすいと主張する) 答えは、リスト内包表記やジェネレーター式を使用せず、実際のジェネレーターを使用することです。

def gen_expensive(mylist):
    for item in mylist:
        result = expensive(item)
        if result:
            yield result

水平方向のスペースが大きくなりますが、一目で何をするのかがはるかに簡単にわかり、同じことを繰り返さなくなります。

于 2008-09-24T22:24:42.163 に答える
6
result = [x for x in map(expensive,mylist) if x]

map() は、cost() に渡された mylist 内の各オブジェクトの値のリストを返します。次に、それをリストで理解し、不要な値を破棄できます。

これはネストされた内包表記に似ていますが、より高速になるはずです (Python インタープリターがかなり簡単に最適化できるため)。

于 2008-09-24T22:12:26.060 に答える
5

これはまさにジェネレータが処理するのに適しているものです:

result = (expensive(x) for x in mylist)
result = (do_something(x) for x in result if some_condition(x))
...
result = [x for x in result if x]  # finally, a list
  1. これにより、パイプラインの各段階で何が起こっているかが完全に明確になります。
  2. 明示的より暗黙的
  3. 最終ステップまでどこでもジェネレーターを使用するため、大きな中間リストはありません

cf: 「システムプログラマーのためのジェネレータートリック」David Beazley

于 2008-09-25T15:12:20.943 に答える
2

関数をメモ化expensive()て、2 回目の呼び出しが単に の計算値の検索になるようにすることができますx

これは、 memoize をデコレータとして実装した多くの実装の 1 つにすぎません

于 2008-09-24T22:14:18.177 に答える
2

高価な(x)をメモ化することができます(そして、高価な(x)を頻繁に呼び出す場合は、おそらく何らかの方法でメモ化する必要があります。このページでは、Python用のメモ化の実装を提供します:

http://code.activestate.com/recipes/52201/

これには、重複したエントリが前の実行からのメモを利用するため、expensive(x) の実行回数が N 回未満になるという追加の利点があります。

これは、cost(x) が真の関数であると仮定しており、変化する可能性のある外部状態に依存しないことに注意してください。cost(x) が外部状態に依存し、その状態がいつ変化するかを検出できる場合、またはリストの理解中に変化しないことがわかっている場合、理解の前にメモをリセットできます。

于 2008-09-24T22:15:58.640 に答える
1

私は以下を好みます:

itertools.ifilter(bool, (expensive(x) for x in mylist))

これには次の利点があります。

于 2009-05-17T01:01:11.567 に答える
0

forリストに追加するためのループの昔ながらの使用法もあります。

result = []
for x in mylist:
    expense = expensive(x)
    if expense:
        result.append(expense)
于 2009-05-17T10:25:40.840 に答える