객체의 속성을 기준으로 객체 목록을 정렬하는 방법은 무엇입니까?


804

객체 자체의 속성별로 정렬하려는 Python 객체 목록이 있습니다. 목록은 다음과 같습니다.

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

각 객체에는 개수가 있습니다.

>>> ut[1].count
1L

카운트를 내림차순으로 정렬해야합니다.

이에 대한 몇 가지 방법을 보았지만 Python에서 모범 사례를 찾고 있습니다.



1
파이썬에서 정렬에 대한 더 많은 정보를 찾고있는 사람들을위한 정렬 방법 .
Jeyekomon

1
operator.attrgetter ( 'attribute_name') 외에도 object_list.sort (key = my_sorting_functor ( 'my_key'))와 같은 펑터를 키로 사용하여 구현을 의도적으로 생략 할 수 있습니다.
vijay shanker

답변:


1312
# 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
문제 없어요. btw, muhuk이 옳고 Django 객체 목록 인 경우 그의 솔루션을 고려해야합니다. 그러나 일반적인 객체 정렬의 경우 내 솔루션이 가장 좋습니다.
Triptych

43
큰 목록에서는 operator.attrgetter ( 'count')를 키로 사용하여 성능을 향상시킬 수 있습니다. 이것은이 답변에서 최적화 된 (낮은 수준) 람다 함수 형식입니다.
David Eyk

4
큰 답변 주셔서 감사합니다. 사전 목록이고 'count'가 해당 키 중 하나 인 경우 다음과 같이 변경해야합니다. ut.sort (key = lambda x : x [ 'count'], reverse = True)
dganesh2002

여러 필드로 정렬 해야하는 경우 파이썬이 안정적인 정렬 알고리즘을 사용하기 때문에 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

객체가 동적으로 속성을 추가 self.__dict__ = {'some':'dict'}한 경우 ( __init__메소드 후에 수행 한 경우) 작동하지 않는 것 같습니다 . 그래도 왜 그렇게 다른지 모르겠다.
tutuca

@ tutuca : 인스턴스를 바꾸지 않았습니다 __dict__. "동적으로 추가 된 속성을 가진 객체"와 "객체의 __dict__속성 설정 "은 거의 직교하는 개념입니다. 귀하의 의견은 __dict__속성 을 설정하는 것이 동적으로 속성을 추가하기위한 요구 사항 임을 암시하는 것처럼 보입니다 .
tzot

@tzot :이시를 잘 찾고 있어요 : github.com/stochastic-technologies/goatfish/blob/master/... 여기에 그 반복자를 사용하여 : github.com/TallerTechnologies/dishey/blob/master/app.py#L28의 인상을 속성 오류. python3 때문일 수도 있지만 여전히 ...
tutuca

1
@tzot :의 사용법을 이해하면 operator.attrgetter속성 이름이있는 함수를 제공하고 정렬 된 컬렉션을 반환 할 수 있습니다.
IAbstract

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 배 정도 느립니다. 그들이 말하는 이유는 파이썬에서 사용되는 정렬 알고리즘을 고도로 최적화했기 때문입니다 ( timsort )에 입니다.

여전히 .sort (lambda)가 일반 .sort ()보다 빠르다는 것은 매우 이상합니다. 나는 그들이 그것을 고치기를 바랍니다.


1
정의 __cmp__.sort(cmp=lambda), not 을 호출하는 것과 .sort(key=lambda)같으므로 전혀 이상하지 않습니다.
tzot

@tzot가 옳습니다. 첫 번째 정렬은 객체를 서로 반복해서 비교해야합니다. 두 번째 정렬은 각 개체에 한 번만 액세스하여 카운트 값을 추출한 다음 고도로 최적화 된 간단한 숫자 정렬을 수행합니다. 보다 공정한 비교는 다음과 같습니다 longList2.sort(cmp = cmp). 나는 이것을 시도했고 그것은 거의 같은 성능을 발휘했다 .sort(). (또한 : "cmp"정렬 매개 변수가 Python 3에서 제거되었습니다.)
Bryan Roach

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 (QuerySet, counts = True)
Nick Sergeant

11

풍부한 비교 연산자를 객체 클래스에 추가 한 다음 목록의 sort () 메서드를 사용하십시오. 파이썬에서 풍부한 비교를
참조하십시오 .


업데이트 :이 방법은 효과가 있지만 Triptych의 솔루션이 더 간단하기 때문에 귀하의 경우에 더 적합하다고 생각합니다.


3

당신이 기준으로 정렬하려는 속성이있는 경우 재산 , 당신은 가져 오기를 피할 수 operator.attrgetter및 재산의 사용 fget대신 방법을.

예를 들어 Circle속성이 있는 클래스의 경우 다음과 같이 radii로 radius목록을 정렬 할 수 circles있습니다.

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

이것은 가장 잘 알려진 기능은 아니지만 종종 가져 오기 기능을 제공합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.