按属性字母数字顺序对对象列表进行排序

3
我有一个相同类型的对象列表。
lis = [<obj>, <obj>, <obj>]

我希望按照对象属性name自然排序。我已经尝试过。

sortedlist = sorted(lis, key=lambda x: x.name)

然而,这种排序方式会按照以下顺序对列表进行排序
A1
A10
A2

不符合我想要的格式

A1
A2
A10

我已经尝试修改排序包含字母数字字符串的Python代码,但我无法使其适用于对象列表。


这被称为“自然”排序,请参见https://dev59.com/2GXWa4cB1Zd3GeqPLEXh以及其中的链接以获取示例。 - georg
感谢所有的建议,但它们都是针对简单列表的排序。而不是对象列表。我希望通过使用自然排序来按对象属性名称对List = [<object>, <object>, <object>]进行排序。 - Scavenger
5个回答

6
这种方法使用groupby,适用于任意数量的字母和数字之间的交换。
from itertools import groupby
def keyfunc(s):
    return [int(''.join(g)) if k else ''.join(g) for k, g in groupby(s, str.isdigit)]

sorted(my_list, key=keyfunc)

演示:

>>> my_list =['A1', 'A10', 'A2', 'B0', 'AA11', 'AB10']
>>> sorted(my_list, key=keyfunc)
['A1', 'A2', 'A10', 'AA11', 'AB10', 'B0']

>>> mylist =['foo1', 'foo10', 'foo2', 'foo2bar1', 'foo2bar10', 'foo2bar3']
>>> sorted(mylist, key=keyfunc)
['foo1', 'foo2', 'foo2bar1', 'foo2bar3', 'foo2bar10', 'foo10']

+1 不错的解决方案,你应该也在这个帖子上发表一下。 - Ashwini Chaudhary
谢谢您发布这个问题,其他提供的解决方案都没有对我起作用。 - Matthew Cassell

5
sorted(obj, key=lambda x: (x.name[0], int(x.name[1:])))

2
这只适用于单个字母后跟一些数字的非常有限的情况:p - John La Rooy
这在我的情况下可能有效,因为命名遵循特定的格式。 - Scavenger
@Scavenger,我根据你的具体情况进行了编写,因为这正是问题所问的。如果我的数据始终是特定格式,我也会使用这个。 - jamylak

5

类似这样:

import re
def func(x):
   foo = re.search(r'([A-Z]+)(\d+)',x.name)
   return foo.group(1), int(foo.group(2))
print sorted(obj, key = func)

示例:

lis =['A1', 'A10', 'A2', 'B0', 'AA11', 'AB10']
def func(x):
   foo = re.search(r'([A-Z]+)(\d+)',x)
   return foo.group(1), int(foo.group(2))
print sorted(lis, key = func)
#['A1', 'A2', 'A10', 'AA11', 'AB10', 'B0']

一个稍微修改过的 sorted_nicely 版本可以适用于您的对象:
def sorted_nicely( x ): 
    """ Sort the given iterable in the way that humans expect.""" 
    convert = lambda text: int(text) if text.isdigit() else text 
    return [ convert(c) for c in re.split('([0-9]+)', x.name) ]

obj.sort(key = sorted_nicely)
#or sorted(obj, key = sorted_nicely)

你的正则表达式有误,因为\w+是贪婪的并且匹配数字。你的\d+只会匹配最后一个数字,因为其余的数字都会被吸入第一组。 - DaoWen

1

以下是回答OP提出的问题如何通过属性“自然”地对对象列表进行排序:

import re

def natkey(s):
    return [w or int(n) for w, n in re.findall('(\D+)|(\d+)', s)]

class X:
    def __init__(self, name):
        self.name = name

lst = [X('AB1'), X('AB10'), X('AB2'), X('AB12')]
lst.sort(key=lambda obj: natkey(obj.name))
print [obj.name for obj in lst]
# ['AB1', 'AB2', 'AB10', 'AB12']

这个可以处理一些带有属性的复杂对象。 - Gero

1
我能够基于beauburriers自然排序解决方案找到一个解决方案。
修改以删除自定义键选项。
import re

def natural_sort(lis):
    """
    Sort the list into natural alphanumeric order.
    """
    def get_alphanum_key_func(key):
       convert = lambda text: int(text) if text.isdigit() else text 
       return lambda s: [convert(c) for c in re.split('([0-9]+)', key(s))]
    sort_key = get_alphanum_key_func(lambda x: x.name)
    lis.sort(key=sort_key)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接