Nesnelerin özniteliğine göre bir nesne listesi nasıl sıralanır?


804

Nesnelerin özniteliklerine göre sıralamak istediğim Python nesnelerinin bir listesi var. Liste şöyle görünür:

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

Her nesnenin bir sayısı vardır:

>>> ut[1].count
1L

Listeyi azalan sayım sayısına göre sıralamam gerekiyor.

Bunun için birkaç yöntem gördüm, ancak Python'da en iyi uygulamaları arıyorum.



1
Sıralama Python sıralama hakkında daha fazla bilgi arayanlar için NASIL YAPILIR .
Jeyekomon

1
operator.attrgetter ('attribute_name') dışında, uygulamayı kasıtlı olarak dışarıda bırakarak nesne_list.sort (key = my_sorting_functor ('my_key')) gibi anahtarlar da kullanabilirsiniz.
vijay shanker

Yanıtlar:


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)

Tuşlara göre sıralama hakkında daha fazla bilgi .


1
Sorun değil. btw, muhuk haklıysa ve bu Django nesnelerinin bir listesiyse, onun çözümünü düşünmelisiniz. Ancak, nesneleri sıralamanın genel durumu için, benim çözümüm muhtemelen en iyi uygulamadır.
Triptik

43
Büyük listelerde, anahtarınız olarak operator.attrgetter ('count') kullanarak daha iyi performans elde edersiniz. Bu, bu cevapta lambda fonksiyonunun optimize edilmiş (düşük seviye) bir şeklidir.
David Eyk

4
Harika cevap için teşekkürler. Bir sözlük listesi ve 'sayı' anahtarından biri ise, aşağıdaki gibi değiştirilmesi gerekir: ut.sort (anahtar = lambda x: x ['sayı'], ters = Doğru)
dganesh2002

Aşağıdaki güncellemeyi hak ettiğini varsayalım: birden çok alana göre sıralamaya ihtiyaç varsa, python kararlı sıralama algoritması kullandığından sıralama () için ardışık çağrılarla elde edilebilir.
zzz777

86

Özellikle listenizde çok fazla kayıt varsa, en hızlı olabilen bir yöntem kullanmaktır operator.attrgetter("count"). Ancak, bu Python'un operatör öncesi bir sürümünde çalışabilir, bu nedenle bir geri dönüş mekanizmasına sahip olmak güzel olurdu. Ardından aşağıdakileri yapmak isteyebilirsiniz:

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
Burada karışıklığı önlemek için "cmpfun" yerine "keyfun" değişken adını kullanırsınız. Sort () yöntemi, cmp = argümanı yoluyla da bir karşılaştırma işlevi kabul eder.
akaihola

Nesne dinamik olarak öznitelikler eklediğinde ( yöntemden self.__dict__ = {'some':'dict'}sonra yaptıysanız) bu işe yaramaz __init__. Yine de neden farklı olabileceğini bilmiyorum.
tutuca

@tutuca: Örneği hiç değiştirmedim __dict__. "Dinamik olarak eklenen özniteliklere sahip bir nesnenin" ve "bir nesnenin __dict__özniteliğinin ayarlanmasının " neredeyse dikey kavramlar olduğunu unutmayın. Bunu söylüyorum, çünkü yorumunuz __dict__öznitelik ayarının dinamik olarak öznitelik eklemek için bir gereklilik olduğunu ima ediyor .
13'te tzot

@tzot: Şuna bakıyorum: github.com/stochastic-technologies/goatfish/blob/master/… ve bu yineleyiciyi burada kullanıyorum: github.com/TallerTechnologies/dishey/blob/master/app.py#L28 yükseltir öznitelik hatası. Belki python3 yüzünden, ama yine de ...
tutuca

1
@tzot: kullanımını anlarsam operator.attrgetter, herhangi bir özellik adı ile bir işlev sağlayabilir ve sıralanmış bir koleksiyon döndürebilirim.
IAbstract

64

Okuyucular key = yönteminin:

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

, nesnelere zengin karşılaştırma işleçleri eklemekten çok daha hızlıdır. Bunu okumak beni şaşırttı (Kısacası "Python in Nutshell"). Bu küçük programda testler yaparak bunu doğrulayabilirsiniz:

#!/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

Benim, çok minimal, testler ilk sıralamanın 10 kattan daha yavaş olduğunu gösteriyor, ancak kitap genel olarak sadece 5 kat daha yavaş olduğunu söylüyor. Dedikleri nedeni, python ( timsort ) 'da kullanılan sıralama algoritmasını son derece optimize ediyor .

Yine de, .sort'un (lambda) düz eski .sort () 'dan daha hızlı olması çok garip. Umarım bunu düzeltirler.


1
Tanımlamak __cmp__aramaya eşdeğerdir .sort(cmp=lambda), değil .sort(key=lambda), bu yüzden hiç de garip değil.
tzot

@tzot tam olarak doğru. İlk sıralama, nesneleri birbirleriyle tekrar tekrar karşılaştırmak zorundadır. İkinci sıralama, sayı değerini ayıklamak için her nesneye yalnızca bir kez erişir ve daha sonra oldukça optimize edilmiş basit bir sayısal sıralama gerçekleştirir. Daha adil bir karşılaştırma olurdu longList2.sort(cmp = cmp). Bunu denedim ve neredeyse aynı performansı gösterdi .sort(). (Ayrıca: "cmp" sıralama parametresinin Python 3'te kaldırıldığını unutmayın.)
Bryan Roach

43

Nesneye yönelik yaklaşım

Nesne sıralama mantığını (varsa), sıralamanın gerekli olduğu her bir örneğe dahil olmaktan ziyade sınıfın bir özelliğini yapmak iyi bir uygulamadır.

Bu tutarlılığı sağlar ve kazan plakası ihtiyacını ortadan kaldırır.

En azından bunun çalışması için belirtmeniz __eq__ve __lt__işlemler yapmanız gerekir . Sonra sadece kullanın 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
Aradığım şey buydu! Bizi neden __eq__ve __lt__minimum uygulama gereklilikleri hakkında ayrıntılı bilgi veren bir belgeye yönlendirebilir misiniz?
FriendFX

1
@FriendFX, bunu ima inanıyoruz bu :•The sort routines are guaranteed to use __lt__() when making comparisons between two objects...
JPP

2
@FriendFX: Bkz portingguide.readthedocs.io/en/latest/comparisons.html Karşılaştırma ve Sıralama için
Cornel Masson

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

16

Django ORM model örneklerinin bir listesine çok benziyor.

Neden bunları şu sorguda sıralamıyorsunuz:

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

Öyle, ama django-tagging kullanarak, bu yüzden böyle belirli bir sorgu kümesi için kullanım tarafından ayarlanmış bir Tag kapma için yerleşik kullanıyordum: Tag.objects.usage_for_queryset (QuerySet, counts = True)
Nick Sergeant

11

Nesne sınıfına zengin karşılaştırma işleçleri ekleyin, ardından listenin sort () yöntemini kullanın.
Python'da zengin karşılaştırmaya bakın .


Güncelleme : Bu yöntem işe yarayacak olsa da, Triptych'in çözümünün sizin durumunuza daha uygun olduğunu düşünüyorum çünkü daha basit.


3

Sıralamak istediğiniz özellik bir özellikse , içe aktarmaktan kaçınabilir operator.attrgetterve fgetbunun yerine özelliğin yöntemini kullanabilirsiniz .

Örneğin, Circlebir özelliğe sahip bir sınıf için , yarıçaplara göre radiusbir listeyi circlesaşağıdaki gibi sıralayabiliriz :

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

Bu en iyi bilinen özellik değil, ancak genellikle içe aktarmayla ilgili bir satır kaydeder.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.