web-dev-qa-db-ja.com

リスト内包表記でwhere句を追加することは可能ですか?

次のリスト内包表記を検討してください

_[ (x,f(x)) for x in iterable if f(x) ]
_

これは、条件fに基づいて反復可能をフィルタリングし、x,f(x)のペアを返します。このアプローチの問題は、f(x)が2回計算されることです。私たちが次のように書くことができれば素晴らしいでしょう

_[ (x,fx) for x in iterable if fx where fx = f(x) ]
or
[ (x,fx) for x in iterable if fx with f(x) as fx ]
_

しかし、pythonでは、f(x)への重複呼び出しを避けるために、ネストされた内包表記を使用して記述する必要があり、内包表記が不明瞭に見えます

_[ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]
_

よりPythonicで読みやすくする他の方法はありますか?


更新

近日公開python 3.8! [〜#〜] pep [〜#〜]

_# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
_
23
balki

pythonリスト内包表記にlet-ステートメントセマンティクスを持たせようとしています。そのスコープは____ for..in_(map)と_if ____( filter)理解の一部であり、そのスコープは_..for ___ in..._に依存します。


変更されたソリューション:[ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]の(読めないことを認めているように)ソリューションは、最適化を記述する最も簡単な方法です。

主なアイデア:xをタプル(x、f(x))に持ち上げます。

物事を行うための最も「Pythonic」な方法は、元の[(x,f(x)) for x in iterable if f(x)]であり、非効率性を受け入れると主張する人もいます。

ただし、これを頻繁に行う予定がある場合は、_((y,fy) for y in iterable)_を関数に因数分解することができます。 _x,fx_(例:_x,fx,ffx_)よりも多くの変数にアクセスしたい場合は、すべてのリスト内包表記を書き直す必要があるため、これは悪いことです。したがって、_x,fx_のみが必要であり、このパターンの再利用を計画していることが確実でない限り、これは優れたソリューションではありません。


ジェネレータ式:

主なアイデア:ジェネレーター式のより複雑な代替手段を使用します。pythonを使用すると、複数行を記述できます。

pythonは次のようにうまく機能するジェネレータ式を使用できます:

_def xfx(iterable):
    for x in iterable:
        fx = f(x)
        if fx:
            yield (x,fx)

xfx(exampleIterable)
_

これが私が個人的に行う方法です。


メモ化/キャッシュ:

主なアイデア:副作用を使用(悪用?)してfにグローバルなメモ化キャッシュを持たせることもできるので、操作を繰り返さないでください。

これには多少のオーバーヘッドが発生する可能性があり、キャッシュのサイズとガベージコレクションのタイミングに関するポリシーが必要です。したがって、これは、fをメモ化するための他の用途がある場合、またはfが非常に高価な場合にのみ使用する必要があります。しかし、それはあなたが書くことを可能にするでしょう...

_[ (x,f(x)) for x in iterable if f(x) ]
_

...技術的に2回呼び出したとしても、fで高価な操作を2回実行するというパフォーマンスへの影響なしに元々望んでいたように。 _@memoized_デコレータをfに追加できます: (最大キャッシュサイズなし)。これは、xがハッシュ可能である限り機能します(たとえば、数値、タプル、フリーズセットなど)。


ダミー値:

主なアイデア:クロージャでfx = f(x)をキャプチャし、リスト内包表記の動作を変更します。

_filterTrue(
    (lambda fx=f(x): (x,fx) if fx else None)() for x in iterable
)
_

ここで、filterTrue(iterable)はfilter(None、iterable)です。リストタイプ(2タプル)が実際にNoneである可能性がある場合は、これを変更する必要があります。

10
ninjagecko

whereステートメントはありませんが、forを使用して「エミュレート」できます。

a=[0]
def f(x):
    a[0] += 1
    return 2*x

print [ (x, y) for x in range(5) for y in [f(x)] if y != 2 ]
print "The function was executed %s times" % a[0]

実行:

$ python 2.py 
[(0, 0), (2, 4), (3, 6), (4, 8)]
The function was executed 5 times

ご覧のとおり、関数は10回または9回ではなく、5回実行されます。

このfor構造:

for y in [f(x)]

where句を模倣します。

10
Igor Chubin

内包表記を使用する必要があるとは何も言われていません。実際、私が見たほとんどのスタイルガイドは、とにかく、単純な構造に制限するように要求しています。

代わりに、ジェネレータ式を使用できます。

def fun(iterable):
    for x in iterable:
        y = f(x)
        if y:
            yield x, y


print list(fun(iterable))
5
Keith

地図とZip?

fnRes = map(f, iterable)
[(x,fx) for x,fx in Zip(iterable, fnRes) if fx)]
3
Vinayak Kolagi