web-dev-qa-db-ja.com

リスト内包表記vs.λ+フィルタ

私は自分自身が基本的なフィルタリングの必要性を持っているのに気付きました:私はリストを持っています、そして私はアイテムの属性によってそれをフィルタリングしなければなりません。

私のコードはこんな感じでした:

my_list = [x for x in my_list if x.attribute == value]

しかし、それから私は思った、それをこのように書くのが良いのではないでしょうか?

my_list = filter(lambda x: x.attribute == value, my_list)

読みやすくなっていますし、パフォーマンスに必要な場合はラムダを取り出して何かを得ることもできます。

質問は次のとおりです。2番目の方法を使用する際の注意点はありますか。パフォーマンスに違いはありますか?私はPythonic Way™を完全に見逃していますか、そしてそれをさらに別の方法(ラムダの代わりにitemgetterを使うなど)でやるべきですか?

726
Agos

人々によって美しさがどれだけ違うかは不思議です。リストの内包表記はfilter + lambdaよりもはるかに明確ですが、どちらか簡単な方を使用してください。しかし、既に組み込み関数に使用されている変数名の指定はやめてください。混乱を招きます。 [ この質問はもともと変数名としてlistを使用していましたが、この回答に対応して更新されました。 ]

filterの使用が遅くなる可能性があることが2つあります。

1つ目は関数呼び出しのオーバーヘッドです。Python関数を使用すると(defまたはlambdaのどちらで作成された場合でも)、フィルターはリスト内包表記より遅くなる可能性があります。ほとんど確実に問題になるほど十分ではありません、そしてあなたがあなたのコードを時間を計って、それがボトルネックであるとわかるまでパフォーマンスについて多くを考えるべきではありません、しかし違いはそこにあるでしょう。

適用される可能性のあるもう1つのオーバーヘッドは、ラムダがスコープ付き変数(value)にアクセスすることを強制されていることです。これはローカル変数にアクセスするより遅く、Python 2.xではリスト内包はローカル変数にのみアクセスします。 Python 3.xを使用している場合、リスト内包表記は別の関数で実行されるため、クロージャを介してvalueにアクセスすることになり、この違いは適用されません。

考慮すべきもう1つの選択肢は、リスト内包表記の代わりにジェネレータを使用することです。

def filterbyvalue(seq, value):
   for el in seq:
       if el.attribute==value: yield el

それからあなたのメインコード(読みやすさが本当に重要なところ)で、リスト内包表記とフィルターの両方を、意味のある意味のある関数名に置き換えました。

499
Duncan

これはPythonではやや宗教的な問題です。 GuidoはPython 3からmapfilterreduceを削除することを検討していましたが、結局のところreduceのみが組み込み関数から functools.reduce )に移動しただけのバックラッシュがありました。

個人的には私はリスト内包表記が読みやすいと思います。すべての振る舞いはフィルタ関数の内側ではなく表面上にあるので、式[i for i in list if i.attribute == value]から何が起こっているのかがより明確になります。

2つのアプローチのパフォーマンスの違いは、ごくわずかなので、あまり心配しないでください。アプリケーションのボトルネックとなる可能性が低いことが判明した場合に限り、これを最適化します。

また、BDFLfilterがその言語から削除されることを望んでいたので、それは確かに自動的にリスト内包表記をPythonicにする;-)

205
Tendayi Mawushe

速度差は非常に小さいため、フィルタとリスト内包表記のどちらを使用するかは好みの問題になります。一般的に私は内包表記を使う傾向がありますが(これは他のほとんどの答えと一致するようです)、私はfilterを好む場合が1つあります。

非常によくあるユースケースは、述語P(x)に従っていくつかの反復可能なXの値を引き出すことです。

[x for x in X if P(x)]

しかし時々あなたは最初に値に何らかの関数を適用したいと思うでしょう:

[f(x) for x in X if P(f(x))]


具体例として、

primes_cubed = [x*x*x for x in range(1000) if prime(x)]

私はこれがfilterを使うより少し良く見えると思います。しかし今検討する

prime_cubes = [x*x*x for x in range(1000) if prime(x*x*x)]

この場合、計算後の値に対してfilterを付けたいと思います。立方体を2回計算する問題(より高価な計算を想像してください)の他に、式を2回書いて _ dry _ 審美的な表現に違反するという問題があります。この場合私は使いがちですが

prime_cubes = filter(prime, [x*x*x for x in range(1000)])
64
I. J. Kennedy

filterは「より速い方法」かもしれませんが、「Pythonicの方法」はパフォーマンスが絶対的に重要でない限りそのようなことを気にすることではないでしょう(その場合あなたはPythonを使わないでしょう!)。

28
Umang

Python 3では、filter()は実際にはイテレータオブジェクトなので、フィルタリストを作成するには、list()にフィルタメソッド呼び出しを渡す必要があります。 Python 2では:

lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = filter(lambda num: num % 2 == 0, lst_a)

リストbとcは同じ値を持ち、filter()が同等であったのとほぼ同じ時間に完了しました[xがxの場合、yの場合]。しかし、3では、この同じコードによって、リストcにフィルタオブジェクトではなくフィルタオブジェクトが含まれることになります。 3で同じ値を生成するには:

lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = list(filter(lambda num: num %2 == 0, lst_a))

問題は、list()が引数としてイテラブルを取り、その引数から新しいリストを作成することです。その結果、python 3でこの方法でfilterを使用すると、元のリストと同様にfilter()からの出力を反復する必要があるため、[x for y in z]メソッドの2倍の時間がかかります。

15
Jim50

重要な違いは、リスト内包表記はlistを返すのに対し、リスト内包表記はfilterを返すことです。listのように操作することはできません(つまり、lenを呼び出すと、filterを返すことはできません)。

私自身の自己学習は私にいくつかの同様の問題をもたらしました。

そうは言っても、結果として得られるlistfilterから取得する方法があるのであれば、lst.Where(i => i.something()).ToList()を実行するときに.NETで行うのと同じように、私はそれを知っていたいと思います。

編集:これはPython 3の場合で、2ではない(コメント中の議論を参照)。

10
Adeynack

私は2番目の方法が読みやすいと思います。これは、意図が何であるかを正確に示しています。リストをフィルタリングします。
シモンズ:変数名として 'list'を使わない

9
unbeli

フィルター はそれだけです。リストの要素を除外します。あなたは定義が同じことを述べているのを見ることができます(私が前に述べた公式の文書リンクで)。一方、リスト内包表記は、前のリストに対して something を実行した後に新しいリストを作成するものです(filterとリスト内包表記の両方で新しいリストが作成され、古いリストの代わりに操作は実行されません)整数型から文字列型への変換など、まったく新しいデータ型を持つリストのようなものです。

あなたの例では、定義に従って、リスト内包表記よりもfilterを使うほうが良いです。ただし、必要に応じて、リスト要素からother_attributeを言うと、この例では新しいリストとして取得されるので、リスト内包表記を使用できます。

return [item.other_attribute for item in my_list if item.attribute==value]

これが私がフィルタとリストの理解について実際に覚えている方法です。リスト内のいくつかのものを削除して他の要素をそのままにして、filterを使用してください。要素に独自のロジックを使用して、目的に適した給水リストを作成します。リスト内包表記を使用します。

7
thiruvenkadam

組み込み関数を使用する場合、一般的にfilterは少し速くなります。

私はあなたの場合リストの内包表記がもう少し速いと思うでしょう

6
John La Rooy

これは私が何かをフィルタリングする必要があるときに使う短い断片です 後に リスト内包表記。フィルター、ラムダ、リストの組み合わせ(猫の忠誠心と犬の清潔さとしても知られています)。

この場合、私はファイルを読んでいて、空白行を取り除き、行をコメントアウトし、そして行にコメントを付けた後は何でもします。

# Throw out blank lines and comments
with open('file.txt', 'r') as lines:        
    # From the inside out:
    #    [s.partition('#')[0].strip() for s in lines]... Throws out comments
    #   filter(lambda x: x!= '', [s.part... Filters out blank lines
    #  y for y in filter... Converts filter object to list
    file_contents = [y for y in filter(lambda x: x != '', [s.partition('#')[0].strip() for s in lines])]
5
rharder

受け入れられた答えに加えて、リスト内包表記の代わりにfilterを使用すべき場合があります。リストがハッシュ不可能な場合は、リスト内包表記で直接処理することはできません。実際の例は、データベースから結果を読み取るためにpyodbcを使用する場合です。 cursorの結果のfetchAll()は、扱いにくいリストです。この場合、返された結果を直接操作するには、filterを使用する必要があります。

cursor.execute("SELECT * FROM TABLE1;")
data_from_db = cursor.fetchall()
processed_data = filter(lambda s: 'abc' in s.field1 or s.StartTime >= start_date_time, data_from_db) 

ここでリスト内包表記を使用すると、エラーが発生します。

TypeError:扱いにくいタイプ: 'list'

5
C.W.praen

higher order functionsfilterおよびmapに慣れるのに時間がかかりました。だから私はそれらに慣れ、実際にはfilterが好きでした。なぜなら、それは真実であるものを何でも保持することによってフィルタリングすることは明白であり、いくつかのfunctional programming用語を知っているとクールだと感じたからです.

次に、この文章を読みました(Fluent Python Book):

マップ関数とフィルター関数は、Python 3にまだ組み込まれていますが、リスト内包表記とジェネレーター表現の導入以来、それほど重要ではありません。 listcompまたはgenexpは、マップとフィルターを組み合わせたジョブを実行しますが、より読みやすくなっています。

そして今、リスト内包表記のような既に広く普及しているイディオムでそれを達成できるなら、なぜfilter/mapの概念に悩まされるのだと思います。さらに、mapsおよびfiltersは一種の関数です。この場合、Anonymous functionsラムダを使用することを好みます。

最後に、テストするためだけに、両方のメソッド(maplistComp)のタイミングを計りましたが、それについて議論をするのに正当な関連する速度の違いは見られませんでした。

from timeit import Timer

timeMap = Timer(lambda: list(map(lambda x: x*x, range(10**7))))
print(timeMap.timeit(number=100))

timeListComp = Timer(lambda:[(lambda x: x*x) for x in range(10**7)])
print(timeListComp.timeit(number=100))

#Map:                 166.95695265199174
#List Comprehension   177.97208347299602
3
user1767754

Python 3について興味深く、リスト内包表記よりもフィルタの実行が速いと思います。

私はいつもリスト内包表記の方がパフォーマンスが良いと思いました。 [nameがNoneでない場合、brand_names_dbのnameの名前]生成されるバイトコードは少し良くなります。

>>> def f1(seq):
...     return list(filter(None, seq))
>>> def f2(seq):
...     return [i for i in seq if i is not None]
>>> disassemble(f1.__code__)
2         0 LOAD_GLOBAL              0 (list)
          2 LOAD_GLOBAL              1 (filter)
          4 LOAD_CONST               0 (None)
          6 LOAD_FAST                0 (seq)
          8 CALL_FUNCTION            2
         10 CALL_FUNCTION            1
         12 RETURN_VALUE
>>> disassemble(f2.__code__)
2           0 LOAD_CONST               1 (<code object <listcomp> at 0x10cfcaa50, file "<stdin>", line 2>)
          2 LOAD_CONST               2 ('f2.<locals>.<listcomp>')
          4 MAKE_FUNCTION            0
          6 LOAD_FAST                0 (seq)
          8 GET_ITER
         10 CALL_FUNCTION            1
         12 RETURN_VALUE

しかし、実際は遅くなります。

   >>> timeit(stmt="f1(range(1000))", setup="from __main__ import f1,f2")
   21.177661532000116
   >>> timeit(stmt="f2(range(1000))", setup="from __main__ import f1,f2")
   42.233950221000214
0
Rod Senra