次のリスト内包表記を検討してください
_[ (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]
_
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
である可能性がある場合は、これを変更する必要があります。
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句を模倣します。
内包表記を使用する必要があるとは何も言われていません。実際、私が見たほとんどのスタイルガイドは、とにかく、単純な構造に制限するように要求しています。
代わりに、ジェネレータ式を使用できます。
def fun(iterable):
for x in iterable:
y = f(x)
if y:
yield x, y
print list(fun(iterable))
地図とZip?
fnRes = map(f, iterable)
[(x,fx) for x,fx in Zip(iterable, fnRes) if fx)]