如何根据对象的属性对对象列表进行排序?

发布于 2024-07-11 05:08:05 字数 197 浏览 6 评论 0 原文

我有一个 Python 对象列表,我想按每个对象的特定属性进行排序:

[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]

How do I sort the list by .count inscending order?

I have a list of Python objects that I want to sort by a specific attribute of each object:

[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]

How do I sort the list by .count in descending order?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

晨与橙与城 2024-07-18 05:08:05

对列表进行就地排序:

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

要返回新列表,请使用 sorted

new_list = sorted(orig_list, key=lambda x: x.count, reverse=True)

说明:

  • key=lambda x: x.count 按计数排序。
  • reverse=True 按降序排序。

有关按键排序的更多信息。

To sort the list in place:

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

To return a new list, use sorted:

new_list = sorted(orig_list, key=lambda x: x.count, reverse=True)

Explanation:

  • key=lambda x: x.count sorts by count.
  • reverse=True sorts in descending order.

More on sorting by keys.

一身骄傲 2024-07-18 05:08:05

最快的方法是使用 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

A way that can be fastest, especially if your list has a lot of records, is to use operator.attrgetter("count"). However, this might run on an pre-operator version of Python, so it would be nice to have a fallback mechanism. You might want to do the following, then:

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
撩人痒 2024-07-18 05:08:05

读者应该注意到 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() 更快。 我希望他们能解决这个问题。

Readers should notice that the key= method:

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

is many times faster than adding rich comparison operators to the objects. I was surprised to read this (page 485 of "Python in a Nutshell"). You can confirm this by running tests on this little program:

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

My, very minimal, tests show the first sort is more than 10 times slower, but the book says it is only about 5 times slower in general. The reason they say is due to the highly optimizes sort algorithm used in python (timsort).

Still, its very odd that .sort(lambda) is faster than plain old .sort(). I hope they fix that.

漫漫岁月 2024-07-18 05:08:05

面向对象的方法

最好将对象排序逻辑(如果适用)作为类的属性,而不是将其合并到需要排序的每个实例中。

这确保了一致性并消除了对样板代码的需要。

您至少应该指定 __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]

Object-oriented approach

It's good practice to make object sorting logic, if applicable, a property of the class rather than incorporated in each instance the ordering is required.

This ensures consistency and removes the need for boilerplate code.

At a minimum, you should specify __eq__ and __lt__ operations for this to work. Then just use 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]
初见 2024-07-18 05:08:05
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
北座城市 2024-07-18 05:08:05

它看起来很像 Django ORM 模型实例的列表。

为什么不在查询中对它们进行排序,如下所示:

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

It looks much like a list of Django ORM model instances.

Why not sort them on query like this:

ut = Tag.objects.order_by('-count')
过气美图社 2024-07-18 05:08:05

如果您要排序的属性是属性,那么您可以避免导入operator.attrgetter并使用属性的fget 方法代替。

例如,对于具有属性 radius 的类 Circle,我们可以按半径对 circle 列表进行排序,如下所示:

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

这不是最好的- 已知的功能,但通常可以节省我的导入时间。

If the attribute you want to sort by is a property, then you can avoid importing operator.attrgetter and use the property's fget method instead.

For example, for a class Circle with a property radius we could sort a list of circles by radii as follows:

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

This is not the most well-known feature but often saves me a line with the import.

°如果伤别离去 2024-07-18 05:08:05

向对象类添加丰富的比较运算符,然后使用列表的 sort() 方法。
请参阅Python 中的丰富比较


更新:虽然这种方法可行,但我认为 Triptych 的解决方案更适合您的情况,因为方法更简单。

Add rich comparison operators to the object class, then use sort() method of the list.
See rich comparison in python.


Update: Although this method would work, I think solution from Triptych is better suited to your case because way simpler.

瑕疵 2024-07-18 05:08:05

另外,如果有人想要对包含字符串和数字的列表进行排序,例如,

 eglist=[
     "some0thing3",
     "some0thing2",
     "some1thing2",
     "some1thing0",
     "some3thing10",
     "some3thing2",
     "some1thing1",
     "some0thing1"]

那么这里是代码:

import re

def atoi(text):
    return int(text) if text.isdigit() else text

def natural_keys(text):
    return [ atoi(c) for c in re.split(r'(\d+)', text) ]

eglist=[
         "some0thing3",
         "some0thing2",
         "some1thing2",
         "some1thing0",
         "some3thing10",
         "some3thing2",
         "some1thing1",
         "some0thing1"
]

eglist.sort(key=natural_keys)
print(eglist)

Also if someone wants to sort list that contains strings and numbers for e.g.

 eglist=[
     "some0thing3",
     "some0thing2",
     "some1thing2",
     "some1thing0",
     "some3thing10",
     "some3thing2",
     "some1thing1",
     "some0thing1"]

Then here is the code for that:

import re

def atoi(text):
    return int(text) if text.isdigit() else text

def natural_keys(text):
    return [ atoi(c) for c in re.split(r'(\d+)', text) ]

eglist=[
         "some0thing3",
         "some0thing2",
         "some1thing2",
         "some1thing0",
         "some3thing10",
         "some3thing2",
         "some1thing1",
         "some0thing1"
]

eglist.sort(key=natural_keys)
print(eglist)
何以笙箫默 2024-07-18 05:08:05

@Jose M Vidal 的答案提到了一些重要的事情:使用丰富的比较(__lt____eq__ 等,如 @jpp 的答案)使得排序比传递关键函数慢得多(如已接受的答案)。 然而,他们最终展示了一个使用 Python 3 中不存在的 __cmp__ 方法的测试(tbf 确实比 Python 2 中传递密钥慢得多)。

我想指出的是,即使在 Python 3.12.0 中,使用 @jpp 的答案中的丰富比较也会使排序比传递关键函数慢得多。

下面显示了一些 timeit 测试的结果,其中比较了使用丰富比较进行排序与​​ lambda 键函数与 operator.attrgetter 作为键的排序。 对于包含 10k 项的列表,使用丰富比较时大约花费 18.8 毫秒,而使用 lambda 键函数时花费 2.93 毫秒,使用 operator.attrgetter 时花费 2.47 毫秒。

因此,正如 @tzot 提到的,operator.attrgetter 比 lambda 更快; 然而,首先使用关键函数而不是丰富的比较可以使排序速度提高 5 倍以上。

import timeit
import random
from operator import attrgetter

class Card(object):

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

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

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

n = 100
random.seed(0)
lst = [Card(random.randrange(10000)) for _ in range(10000)]


min(timeit.repeat(lambda: sorted(lst), number=n))/n
# 0.018813106999732553

min(timeit.repeat(lambda: sorted(lst, key=lambda card: card.rank), number=n))/n
# 0.0029304770001908763

min(timeit.repeat(lambda: sorted(lst, key=attrgetter('rank')), number=n))/n
# 0.00247172600007616

@Jose M Vidal's answer mentions something important: using rich comparisons (__lt__, __eq__ etc. as in @jpp's answer) makes sorting much slower than passing a key function (as in the accepted answer). However, they end up showing a test using __cmp__ method which doesn't exist in Python 3 (which tbf was indeed so much slower than passing key in Python 2).

I want to point out that even in Python 3.12.0, using rich comparisons as in @jpp's answer makes sorting much slower than passing a key function.

The results of a little timeit test is shown below where sorting using rich comparisons vs a lambda key function vs operator.attrgetter as key are compared. For a list with 10k items, it took about 18.8 ms when rich comparisons were used whereas it took 2.93 ms when a lambda key function was used and 2.47 ms when operator.attrgetter was used.

So, as @tzot mentioned, operator.attrgetter is faster than a lambda; however, using a key function in the first place instead of rich comparisons makes sorting over 5 times faster.

import timeit
import random
from operator import attrgetter

class Card(object):

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

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

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

n = 100
random.seed(0)
lst = [Card(random.randrange(10000)) for _ in range(10000)]


min(timeit.repeat(lambda: sorted(lst), number=n))/n
# 0.018813106999732553

min(timeit.repeat(lambda: sorted(lst, key=lambda card: card.rank), number=n))/n
# 0.0029304770001908763

min(timeit.repeat(lambda: sorted(lst, key=attrgetter('rank')), number=n))/n
# 0.00247172600007616
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文