オブジェクトの属性に基づいてオブジェクトのリストをソートする方法は?


804

オブジェクト自体の属性でソートしたいPythonオブジェクトのリストがあります。リストは次のようになります。

>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
 <Tag: aes>, <Tag: ajax> ...]

各オブジェクトにはカウントがあります。

>>> ut[1].count
1L

カウント数の降順でリストをソートする必要があります。

これについていくつかの方法を見てきましたが、Pythonでのベストプラクティスを探しています。



1
Pythonでの並べ替えに関する詳細情報を探している人のためのHOW TOの並べ替え
ジェイェコモン

1
operator.attrgetter( 'attribute_name')とは別に、object_list.sort(key = my_sorting_functor( 'my_key'))のようにファンクタをキーとして使用して、実装を意図的に除外することもできます。
vijay shanker

回答:


1313
# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)

# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)

キーによるソートの詳細。


1
問題ない。ところで、muhukが正しく、それがDjangoオブジェクトのリストである場合は、彼の解決策を検討する必要があります。ただし、オブジェクトを並べ替える一般的なケースでは、私の解決策はおそらくベストプラクティスです。
トリプティク

43
大規模なリストでは、キーとしてoperator.attrgetter( 'count')を使用するとパフォーマンスが向上します。これは、この回答のラムダ関数の最適化された(下位レベル)形式にすぎません。
デビッドアイク

4
すばらしい答えをありがとう。辞書のリストであり、 'count'がそのキーの1つである場合、次のように変更する必要があります。ut.sort(key = lambda x:x ['count']、reverse = True)
dganesh2002

次の更新に値すると思います:複数のフィールドでソートする必要がある場合、pythonは安定したソートアルゴリズムを使用しているため、sort()を連続して呼び出すことで達成できます。
zzz777

86

特にリストに多くのレコードがある場合、最も速くなる方法はを使用することoperator.attrgetter("count")です。ただし、これはオペレーターの前のバージョンのPythonで実行される可能性があるため、フォールバックメカニズムを用意すると便利です。次に、次の操作を実行します。

try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda

ut.sort(key=keyfun, reverse=True) # sort in-place

7
ここでは、混乱を避けるために、「cmpfun」の代わりに「keyfun」という変数名を使用します。sort()メソッドは、cmp =引数を通じて比較関数も受け入れます。
akaihola 2009年

これは、オブジェクトに動的に追加された属性がある場合は機能しないようです(メソッドのself.__dict__ = {'some':'dict'}後に行った__init__場合)。なぜ違うのかはわかりません。
tutuca 2013年

@tutuca:インスタンスを置き換えたことはありません__dict__。「動的に追加された属性を持つオブジェクト」と「オブジェクトの__dict__属性の設定」は、ほぼ直交する概念です。あなたのコメントは、__dict__属性の設定が属性を動的に追加するための要件であることを暗示しているように見えるので、私は言っています。
tzot 2013年

@tzot:私はこれを正しく見ています:github.com/stochastic-technologies/goatfish/blob/master/…そしてそのイテレーターをここで使用します:github.com/TallerTechnologies/dishey/blob/master/app.py#L28 raises属性エラー。多分python3のためかもしれませんが、それでも...
tutuca 2013年

1
@tzot:の使用法を理解していればoperator.attrgetter、関数に任意のプロパティ名を指定して、ソートされたコレクションを返すことができます。
IAbstract 2016

64

読者は、key =メソッドに注意してください。

ut.sort(key=lambda x: x.count, reverse=True)

オブジェクトに豊富な比較演算子を追加するよりも何倍も高速です。これを読んで驚いた(「Python in a Nutshell」の485ページ)。これを確認するには、この小さなプログラムでテストを実行します。

#!/usr/bin/env python
import random

class C:
    def __init__(self,count):
        self.count = count

    def __cmp__(self,other):
        return cmp(self.count,other.count)

longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]

longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs

私の非常に最小限のテストでは、最初のソートは10倍以上遅いことが示されていますが、本では一般的に約5倍遅いだけです。彼らが言う理由は、Python(timsort)で使用される高度に最適化されたソートアルゴリズムによるものです。

それでも、.sort(lambda)が従来の.sort()よりも高速であるのは非常に奇妙です。私は彼らがそれを修正することを望みます。


1
定義すること__cmp__.sort(cmp=lambda)、ではなくを呼び出すことと同じ.sort(key=lambda)であるため、まったく奇妙なことではありません。
tzot

@tzotはまさに正しいです。最初のソートでは、オブジェクトを相互に何度も比較する必要があります。2番目のソートは、各オブジェクトに1回だけアクセスしてカウント値を抽出し、高度に最適化された単純な数値ソートを実行します。より公平な比較が行われますlongList2.sort(cmp = cmp)。これを試してみたところ、とほぼ同じように動作しました.sort()。(また、「cmp」ソートパラメータはPython 3で削除されたことに注意してください)
ブライアンローチ

43

オブジェクト指向のアプローチ

オブジェクトの並べ替えロジックを適用する場合は、順序付けが必要な各インスタンスに組み込むのではなく、クラスのプロパティを作成することをお勧めします。

これにより、一貫性が確保され、定型コードの必要がなくなります。

最低でも、次のように指定すべきである__eq____lt__の作業には、このための事業。次にを使用しますsorted(list_of_objects)

class Card(object):

    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit

    def __eq__(self, other):
        return self.rank == other.rank and self.suit == other.suit

    def __lt__(self, other):
        return self.rank < other.rank

hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]
hand_order = [c.rank for c in hand]  # [10, 2, 12, 13, 14]

hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted]  # [2, 10, 12, 13, 14]

1
それが私が探していたものです!あなたは理由について詳しく説明することをいくつかのドキュメントに私たちを指すようでした__eq__し、__lt__最低限の実装要件がありますか?
FriendFX

1
@FriendFX、私はそれがで暗示だと信じて、この•The sort routines are guaranteed to use __lt__() when making comparisons between two objects...
JPP

2
@FriendFX:比較と並べ替えについては、portingguide.readthedocs.io / en / latest / comparisons.htmlを参照してください
Cornel Masson

37
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)

16

Django ORMモデルインスタンスのリストによく似ています。

次のようにクエリで並べ替えないでください。

ut = Tag.objects.order_by('-count')

それはですが、私が使用していたので、ジャンゴ・タグを使用したビルトインそうのように、特定のクエリセットの使用によってタグのセットをつかむために:Tag.objects.usage_for_queryset(クエリセット、カウント=真)
ニック軍曹

11

オブジェクトクラスに豊富な比較演算子を追加し、リストのsort()メソッドを使用します。pythonの豊富な比較を
ご覧ください。


更新:この方法は機能しますが、Triptychの解決策はあなたのケースに適していると思います。


3

あなたがでソートしたい属性がある場合は財産、あなたはインポート避けることができますoperator.attrgetterし、プロパティの使用fgetの代わりの方法を。

たとえばCircle、プロパティradiusを持つクラスの場合、circles次のようにリストを半径でソートできます。

result = sorted(circles, key=Circle.radius.fget)

これは最もよく知られた機能ではありませんが、インポートの手間を省くことがよくあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.