28

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

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

これは、条件に基づいて iterable をフィルタリングし、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]
4

4 に答える 4

13

ステートメントはありません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 句を模倣します。

于 2012-07-23T09:22:01.767 に答える
11

内包表記の(マップ)部分と (フィルター) 部分のlet両方でスコープが使用可能で、スコープが.___ for..inif ___..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) ]

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


ダミー値:

主なアイデア: クロージャで 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

于 2012-07-23T07:54:33.113 に答える
5

内包表記を使用しなければならないということはありません。実際、私が見たほとんどのスタイル ガイドでは、単純な構造に限定するように要求されています。

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

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


print list(fun(iterable))
于 2012-07-23T07:54:22.317 に答える
3

地図と郵便番号 ?

fnRes = map(f, iterable)
[(x,fx) for x,fx in zip(iterable, fnRes) if fx)]
于 2012-07-23T08:06:57.677 に答える