web-dev-qa-db-ja.com

Pythonリストの理解と.NET LINQ

次の簡単なLINQコード

string[] words = { "hello", "wonderful", "linq", "beautiful", "world" };

// Get only short words
var shortWords =
  from Word in words
  where Word.Length <= 5
  select Word;

// Print each Word out
shortWords.Dump();

次のようにリスト内包表記を使用して、pythonに変換できます。

words = ["hello", "wonderful", "linq", "beautiful", "world"]
shortWords = [x for x in words if len(x) <=5]
print shortWords
  • LINQはリスト内包表記を実装するための別のアイデアですか?
  • 例としては、LINQではできるが、リスト内包ではできない場合があります。
56
prosseek

(警告:マンモスが先に答えます。最初の水平線までの部分がtl; drセクシ​​ョンになると思います)

Python guru ...としての資格があるかどうかはわかりませんが、Pythonでの反復についてはしっかりと把握しているので、試してみましょう:)

まず、Afaik、LINQクエリは遅延して実行されます-その場合、ジェネレーター式はより近いPythonの概念です(どちらの方法でも、リスト、辞書、セットの内包表記は概念的には単なるジェネレーター式のフィードです)リスト/辞書/セットコンストラクタに!).

また、概念的な違いもあります。LINQは、名前が示すように、データ構造のクエリに使用されます。リスト/ディクショナリ/セット内包は、これの可能な適用です(例えば、リストのアイテムのフィルタリングと投影)。したがって、実際にはそれほど一般的ではありません(後で説明するように、LINQに組み込まれている多くのものは組み込まれていません)。同様に、ジェネレーター式は、1回限りのフォワードイテレーターをインプレースで作成する方法です(私はそれをジェネレーター関数のラムダと見なしたいのですが、醜い、長いキーワードがない場合のみです))。複雑なクエリを記述する方法ではありません。はい、重複しますが、同一ではありません。 PythonでLINQのすべての機能が必要な場合は、本格的なジェネレーターを作成する必要があります。または、ビルトインおよびitertoolsの多数の強力なジェネレーターを組み合わせます。


今、Python対応するLINQ機能に対応するJon Skeetは、

投影:_(x.foo for ...)_

フィルタリング:_(... if x.bar > 5)_

  • 結合(x.fooのx結合yはy.barと等しい)

一番近いのは_((x_item, next(y_item for y_item in y if x_item.foo == y_item.bar)) for x_item in x)_でしょう。

これは、各x_itemのy全体を反復するのではなく、最初の一致のみを取得することに注意してください。

  • グループ結合(x.fooのx結合yはy.barからgに等しい)

これは難しいです。 Pythonには匿名型はありませんが、___dict___をいじることを気にしない場合は、自明です。

_class Anonymous(object):
    def __init__(self, **kwargs):
        self.__dict__ = kwargs
_

次に、_(Anonymous(x=x, y=y) for ...)_を実行して、それぞれの値を持つxおよびyメンバーを持つオブジェクトのリストを取得できます。正しいことは、通常、適切なクラス、たとえばXYのコンストラクターに結果を供給することです。

  • グループ化(x.fooでx.fooをグループ化)

今それは毛むくじゃらになります...組み込み方法はありません、アファイク。しかし、必要に応じて自分で定義できます。

_from collections import defaultdict

def group_by(iterable, group_func):
    groups = defaultdict(list)
    for item in iterable:
        groups[group_func(item)].append(item)
    return groups
_

例:

_>>> from operator import attrgetter
>>> group_by((x.foo for x in ...), attrgetter('bar'))
defaultdict(<class 'list'>, {some_value_of_bar: [x.foo of all x where x.bar == some_value_of_bar], some_other_value_of_bar: [...], ...})
_

ただし、グループ化するものはすべてハッシュ可能にする必要があります。これを回避することは可能であり、公的な需要があれば私は刺します。しかし、今のところ、私は怠惰です:)

結果に対して.values()を呼び出すことで、グループ化された値なしでグループのイテラブルを返すこともできます(もちろん、thatlistにフィードできますインデックスを作成して数回繰り返すことができるものを取得します)。しかし、グループ値が必要ないかどうかは誰にもわかりません...

  • 順序付け(orderby x.foo昇順、y.bar降順)

ソートには特別な構文が必要ですか?ビルトインsortedもイテラブルで機能します:sorted(x % 2 for x in range(10))またはsorted(x for x in xs, key=attrgetter('foo'))。デフォルトでは昇順でソートされ、キーワード引数reverseは降順を示します。

残念ながら、複数の属性によるafaikソートは、特に昇順と降順を混合する場合はそれほど簡単ではありません。うーん...レシピのトピック?

  • 中間変数(tmp = x.fooとします)

いいえ、内包表記やジェネレータ式では使用できません。名前が示すように、これらは式であることが想定されています(通常、1行または2行しかありません)。ただし、ジェネレーター関数では完全に可能です。

_(x * 2 for x in iterable)
_

中間変数を持つジェネレータとして書き直されました:

_def doubles(iterable):
    for x in iterable:
        times2 = x * 2
        yield times2
_

フラット化:_(c for s in ("aa","bb") for c in s )_


LINQ to Objectsはデリゲートを処理しますが、他のクエリプロバイダー(LINQ to SQLなど)は、実行可能なデリゲートを表示するだけでなく、クエリを記述する式ツリーで処理できることに注意してください。これにより、クエリをSQL(または他のクエリ言語)に変換できます。ここでも、Pythonがそのようなことをサポートしているかどうかはわかりません。ただし、LINQの重要な部分です。

Pythonはそのようなことは絶対に行いません。リスト式は、(おそらくネストされた)forループにプレーンリストを累積することに1対1で対応し、ジェネレータ式は1対1でジェネレータに対応します。 parserおよびastモジュールが与えられた場合、理論的には内包を変換するためのライブラリを作成することが可能です。 SQLクエリ。しかし、誰も気にしません。

58
user395760

さて、あなたはいくつかの異なるものを区別する必要があります:

  • LINQ標準クエリ演算子
  • C#のLINQクエリ式
  • VBのLINQクエリ式

C#は、クエリ式でVBほど多くサポートしていませんが、次のようになりますdoesサポート:

  • 投影(select x.foo
  • フィルタリング(where x.bar > 5
  • 結合(x join y on x.foo equals y.bar
  • グループ参加(x join y on x.foo equals y.bar into g
  • グループ化(group x.foo by x.bar
  • 順序付け(orderby x.foo ascending, y.bar descending
  • 中間変数(let tmp = x.foo
  • フラット化(from x in y from z in x

それらのうちいくつがPythonのリスト内包で直接サポートされているかはわかりません。

LINQ to Objectsはデリゲートを処理しますが、他のクエリプロバイダー(LINQ to SQLなど)は、実行可能なデリゲートを表示するだけでなく、クエリを記述する式ツリーで処理できることに注意してください。これにより、クエリをSQL(または他のクエリ言語)に変換できます。ここでも、Pythonがそのようなことをサポートしているかどうかはわかりません。ただし、LINQの重要な部分です。

24
Jon Skeet

asq Pythonパッケージを使用すると、ほとんどのことがPython LINQ-for-オブジェクト。asqを使用すると、Pythonの例は次のようになります。

from asq.initiators import query
words = ["hello", "wonderful", "linq", "beautiful", "world"]
shortWords = query(words).where(lambda x: len(x) <= 5)
16
Rob Smallshire

私はPythonの第一人者ではありませんが、Pythonは実際にそれらすべてをサポートしているので、リスト内包表記をネストして、必要なすべてのラムダ式を含めることができるため、 。(ただし、リストの内包表記は、複雑になりすぎると読みにくくなる傾向があります...)が、それを実現するための「特定の構文」は含まれていません。

ほとんどの機能は次を使用して再現できます:-list comprehensionsまたはgenerators -ラムダ関数または組み込み関数(filter()またはmap()など)またはitertoolsモジュールの関数

たとえば、次の動作をコピーする場合:

  • Projections:これはリスト内包の左側の部分になります...単一の値でもタプルでもかまいません。例:[ (k,v) for k,v in my_dict.items() if k.startswith("abc"]map()を使用することもできます
  • Filtering:右側のifの後の式になります。 filter()を使用することもできます
  • Ordering:組み込みsorted()を使用するだけ
  • Groupingまたはaggregates:組み込みmin()を使用します、max()またはitertools.groupby()

joinsまたはflatteningについては、「実行する必要があると思いますそれを手で」...

(常に Pythonクイックリファレンス が手元にあるとよい)

4
tsimbalar