FORループとIFステートメントを組み合わせるPythonの方法


266

forループとifステートメントの両方を別々の行に使用する方法を知っています。

>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
...     if x in a:
...         print(x)
0,4,6,7,9

また、ステートメントが次のように単純な場合は、リスト内包表記を使用してこれらを組み合わせることができることを知っています。

print([x for x in xyz if x in a])

しかし、私が見つけられないのは、forループといくつかのifステートメントの組み合わせの後に発生する(「print x」だけでなく)複雑なコマンドのセットを示す(コピーして学ぶための)良い例です。私が期待するものは次のようになります:

for x in xyz if x not in a:
    print(x...)

これはpythonが動作するはずの方法ではないのですか?


23
それがそうです...それらを簡素化しようとすることによって物事を複雑にしすぎないでください。Pythonicは、すべての明示的なforループとifステートメントを回避することを意味しません。
Felix Kling、2011

2
リスト内包で生成されたリストをforループで使用できます。これは最後の例のようになります。
ジェイコブ

では、処理に取り掛かると、forループをifステートメントと組み合わせる最も速い方法は何ですか?ifステートメントが既に一致している値を除外していて、forループの反復中にリストが継続的に増加している場合はどうなりますか?
ChewyChunks、2011

3
@Chewy、適切なデータ構造はコードをより高速にしますが、構文上の砂糖ではありません。たとえば、リストのx in a場合は低速ですa
Nick Dandoulakis、2011

1
これはインタプリタ言語であるPythonです。なぜ誰もがコードがどれほど高速であるかについて議論しているのですか?
ArtOfWarfare 2013年

回答:


323

次のようなジェネレータ式を使用できます。

gen = (x for x in xyz if x not in a)

for x in gen:
    print x

1
gen = (y for (x,y) in enumerate(xyz) if x not in a)戻り>>> 12I型for x in gen: print x-そう列挙と、なぜ予期しない動作?
ChewyChunks、2011

9
可能ですが、元のforおよびifブロックよりも優れています。
マイクグラハム

1
@ChewyChunks。それは機能しますが、列挙する呼び出しは冗長です。
Johnsyweb 2011

132
私はpythonが言えることが本当に恋しいfor x in xyz if x:
bgusach '10

10
for x in (x for x in xyz if x not in a):私にとってはうまくいきますが、なぜあなたができるだけではいけないのか分かりませんfor x in xyz if x not in a:...
Matt Wenham

34

The Zen of Pythonに従って(コードが「Pythonic」かどうか疑問に思っている場合は、ここから始めてください):

  • 醜いよりも美しい方がいいです。
  • 明示的は暗黙的よりも優れています。
  • シンプルは複雑よりも優れています。
  • ネストよりもフラットの方が優れています。
  • 読みやすさが重要です。

2つのを取得するPythonの方法は次のとおりです。sorted intersectionset

>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]

または、次の要素は含まれてxyzいませんa

>>> sorted(set(xyz).difference(a))
[12, 242]

しかし、より複雑なループの場合は、よく名前が付けられたジェネレータ式を繰り返し処理したり、よく名前が付けられた関数を呼び出したりして、ループを平坦化することができます。すべてを1行に収めようとすることは、ほとんど「Pythonic」ではありません。


質問と承認された回答に関する追加のコメントに従って更新してください

あなたが何をしようとしているのかわかりませんが、辞書のenumerate場合aは、おそらく次のようにキーを使用する必要があります。

>>> a = {
...     2: 'Turtle Doves',
...     3: 'French Hens',
...     4: 'Colly Birds',
...     5: 'Gold Rings',
...     6: 'Geese-a-Laying',
...     7: 'Swans-a-Swimming',
...     8: 'Maids-a-Milking',
...     9: 'Ladies Dancing',
...     0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
...     print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
...     print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas

下のコメントのように聞こえますが、ジェネレーターについて勉強している必要があります。私はそれらを使用したことがありません。ありがとう。ジェネレーターは、FORステートメントとIFステートメントの同等の組み合わせよりも高速ですか?セットも使用しましたが、リストの冗長な要素が破棄できない情報になる場合があります。
ChewyChunks、2011

@ChewyChunks:Pythonicになる唯一の方法はジェネレーターではありません!
Johnsyweb

3
@Johnsyweb(Zen of Pythonを引用する場合):「1つ、できれば1つだけ、それを行うための明白な方法があるはずです。」
Wooble 08

@ウーブル:あるはずです。同じ時期に別の質問への私の回答でそのセクションを引用しました!
Johnsyweb

18

私は個人的にこれが最も美しいバージョンだと思います:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
  print x

編集する

ラムダの使用を避けることに非常に熱心な場合は、部分的な関数アプリケーションを使用して、演算子モジュール(ほとんどの演算子の関数を提供する)を使用できます。

https://docs.python.org/2/library/operator.html#module-operator

from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))

4
filter(a.__contains__, xyz)。通常、人々がラムダを使用するとき、彼らは本当にもっと単純なものが必要です。
Veky、2015

あなたは何かを誤解したと思います。__contains__他のようメソッドですが、それは特別なメソッドです。つまり、演算子(inこの場合)から間接的に呼び出すことができます。ただし、直接呼び出すこともでき、パブリックAPIの一部です。プライベート名は、特別なメソッド名の例外を提供するために、多くとも1つの末尾アンダースコアを持つように明確に定義されており、クラススコープ内で字句的には名前のマングリングの影響を受けます。docs.python.org/3/reference/datamodel.html#specialnamesおよびdocs.python.org/3.6/tutorial/classes.html#private-variablesをご覧ください。
Veky

それは確かに大丈夫ですが、属性だけを使用してアクセスできるメソッドを参照できるようにするための2つのインポートは奇妙に見えます(演算子は通常、二重ディスパッチが不可欠な場合に使用されますが、in右オペランドに対して単独でディスパッチされます)。さらに、メソッドoperatorcontains名前__contains__でエクスポートするため、プライベート名ではないことに注意してください。すべてのダブルアンダースコアが「遠ざける」ことを意味するわけではないという事実を理解する必要があると思います。:-]
Veky

私はあなたのlambdaニーズを修正して含める必要があると思いますnotlambda w: not w in a, xyz
javadba

フィルターは、特にラムダの代わりに定義された関数になる複雑な条件の場合、よりエレガントに見えます。ラムダ関数に名前を付けると、読みやすさが向上する可能性があります。反復された要素がリストアイテムの一部の変更である場合、ジェネレーターはより優れているように見えます
Khanis Rok

16

以下は、受け入れられた回答の簡略化/ 1つのライナーです。

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]

for x in (x for x in xyz if x not in a):
    print(x)

12
242

generatorインラインで保持されていることに注意してください。これは、上でテストされたpython2.7python3.6 (中括弧に注意してくださいprint;))


10

私はおそらく使用します:

for x in xyz: 
    if x not in a:
        print x...

@KirillTitovはいpythonは基本的に非関数型言語です(これは純粋に命令型のコーディングです-そして、これはpythonが記述されるように設定されている方法であると私がこの回答の著者に同意します。関数型を使用しようとすると、不十分な読み取りまたは非pythonic結果。私は使用する他のすべての言語(scala、kotlin、javascript、R、swiftなど)で機能的にコーディングできますが、Pythonでは困難/扱いにくい
javadba

9
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]  
set(a) & set(xyz)  
set([0, 9, 4, 6, 7])

非常にZen、@ lazyrですが、1つのリストを反復処理し、別のリストの一致する要素を無視することに依存する複雑なコードブロックを改善するのに役立ちません。最初のリストをセットとして扱い、和/差を2番目の成長する「無視」リストと比較する方が速いですか?
ChewyChunks、2011

これを試してくださいimport time a = [2,3,4,5,6,7,8,9,0] xyz = [0,12,4,6,242,7,9] start = time.time() print (set(a) & set(xyz)) print time.time() - start
Kracekumar

@ChewyChunks反復中にリストのいずれかが変更された場合、各要素を無視リストに対してチェックする方がおそらく速くなります-無視セットにする必要がある場合を除きます。セットのメンバーシップのチェックは非常に高速ですif x in ignore: ...
Lauritz V. Thaulow

@lazyr 無視リストに無視セットを使用してコードを書き直したところです。処理時間がはるかに遅いようです。(私が使用して比較した公正であるためにはif set(a) - set(ignore) == set([]):ので、おそらくのにそれははるかに遅いメンバーシップをチェックするよりもした理由という、私は私が書いているものよりもはるかに簡単な例で、将来的に再びこれをテストします。。
ChewyChunks

5

ジェネレータ式が複雑すぎたり複雑になったりした場合は、ジェネレータも使用できます。

def gen():
    for x in xyz:
        if x in a:
            yield x

for x in gen():
    print x

これは私にとってもう少し便利です。発電機を見たことがない。彼らは恐ろしいように聞こえます(私がそれらを一般的に使用するのが面倒だったモジュールで見たからです)。
ChewyChunks、2011

2

intersectionまたはを使用intersection_update

  • 交差点

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    ans = sorted(set(a).intersection(set(xyz)))
  • intersection_update

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    b = set(a)
    b.intersection_update(xyz)

    その後b、あなたの答えです


2

私はアレックスの答えが好きでした。フィルターはリストに適用された場合とまったく同じなので、条件を指定してリストのサブセットを探索したい場合、これは最も自然な方法のようです

mylist = [1,2,3,4,5]
another_list = [2,3,4]

wanted = lambda x:x in another_list

for x in filter(wanted, mylist):
    print(x)

このメソッドは、問題の分離に役立ちます。条件関数が変更された場合、調整するコードは関数自体のみです。

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

for x in filter(wanted, mylist):
    print(x)

発電方法は、リストのメンバーをしたくない時に良さそうですが、それによりフィット感を思わ言ったメンバーの変更、発電機

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.5 for x in mylist if wanted(x))

for x in generator:
    print(x)

また、フィルターはジェネレーターで機能しますが、この場合は効率的ではありません

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.9 for x in mylist)

for x in filter(wanted, generator):
    print(x)

しかし、もちろん、次のように書くとなおよいでしょう。

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

# for x in filter(wanted, mylist):
for x in mylist if wanted(x):
    print(x)

0

リストaとbの一意の共通要素を見つける簡単な方法:

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.