2

C# や Ruby では些細でエレガントな、オブジェクトの配列に対する単純なクエリを Python で作成しようとしていますが、Python でそれをエレガントにするのに苦労しています。私は何か間違ったことをしていると思います。

C# の場合:

list.Where(x => x.Foo > 10).Select(x => x.Bar).Where(x => x.Baz.StartsWith("/"))

list[0].Barこれにより、リスト内の他のすべてのアイテムについて、list[0].Foo10 を超えて でlist[0].Bar.Baz始まるなどの列挙が作成されます。'/'データは左から右に明確に流れ、右側にさらにフィルタリング/射影/集計を追加できます。

ルビーの場合:

list.select { |x| x.foo > 10 }.map(&:bar).select { |x| x.baz.starts_with? '/' }

繰り返しますが、これは左から右へのかなり明確なフローであり、さらに操作を簡単に追加できます。

しかし、私のPythonでの試みは裏返しで、裏返しで、一般的に醜いようです:

[x for x in (x.bar for x in (x for x in list if x.foo > 10)) if x.baz.startswith('/')]

これで、リスト内包表記を使用してマップとフィルターを 1 つのステップで組み合わせることができ、上記を次のように書き直すことができることがわかりました。

[x.bar for x in list if x.foo > 10 and x.bar.baz.startswith('/')]

しかし、それはむしろ要点を逃しています。1 つには、射影 x.bar はコストがかかる可能性があるため、2 回評価したくありません。別の例では、射影とフィルタリングは、ストリームに適用する可能性のある操作の 2 つにすぎません。並べ替え、集計、ページ分割などを行うことができます。すべての射影とフィルタが隣接している必要はなく、射影の前にフィルタを適用する必要もありません。後ではなく。

私はPythonをそうではないものにひねろうとしていますか? コマンドライン (シェル パイプ)、C#、Ruby、または Java (Python よりもはるかに面倒) のいずれであっても、私は通常、できる限りこのスタイルでプログラミングしようとします。痛いところを突くのはやめたほうがいいですか?

4

2 に答える 2

4

ジェネレーターを使用してbar値を生成できます。不要なジェネレーター レベルが 1 つあります。

[bar for bar in (x.bar for x in somelist if x.foo > 10) if bar.baz.startswith('/')]

最初に、ネストされたジェネレーターを変数に割り当てることができます。

bars = (x.bar for x in somelist if x.foo > 10)
[bar for bar in bars if bar.baz.startswith('/')]

行の長さの制限内に収めたい場合。.barジェネレーターは 1 回だけ消費され、 の各要素に対して 1 回だけ高価な属性にアクセスしますsomelist

C# と Ruby のコードの読み取り順序を複製したい場合は、ステップごとに別のジェネレーターを使用して、これをさらに進めることができます。

filtered_on_foo = (x for x in somelist if x.foo > 10)
bar_selected = (x.bar for x in filtered_on_foo)
filtered_on_baz = [bar for bar in bar_selected if bar.baz.startswith('/')]

しかし、個別に選択することで余分なループが発生しています。

于 2013-09-14T17:48:18.140 に答える
1

実際、私は C# 開発者として働いており、LINQ がとても好きです (Python ほどではありませんが :) )、LINQ の Python バージョンがないのはなぜだろうといつも思っていました。

しかし、私は楽しみのためだけに Python を使用しているため、これを適切にチェックする時間はありませんでした。あなたの質問の後、Python用のLINQのようなものが存在するかどうかを検索し始めました(そのようなモジュールが存在しない場合は、実際に自分でこのようなものを書くことを考えています)。

これは良いと思います - A Python implementation of LINQ to objects and Parallel LINQ to objects (ASQ) :

あなたの場合、次のように機能します。

from asq.initiators import query

a = [{"foo":1, "bar": {"baz":"aaaa"}}, {"foo": 11, "bar": {"baz":"/ddddd"}}]

q = query(a).where(lambda x: x["foo"] > 10).select(lambda x: x["bar"]).where(lambda x: x['baz'].startswith('/'))

q.to_list()
# gives [{'foo': 11, 'bar': {'baz': '/ddddd'}}]

私が見つけた唯一の欠点は、このクエリを次のようにフォーマットできないことです。

q = query(a).where(lambda x: x["foo"] > 10)
            .select(lambda x: x["bar"])
            .where(lambda x: x['baz'].startswith('/'))

この処理を機能的なスタイルで行うこともできます。

q = ifilter(lambda x: x["foo"] > 10, a)
q = imap(lambda x: x["bar"], q)
q = ifilter(lambda x: x["baz"].startswith('/'), q)
于 2013-09-14T18:56:23.903 に答える