我有一个我要排序的元组列表,可以使用一些帮助。
我想在元组中排序的字段看起来像“XXX_YYY”。首先,我想以相反的顺序对XXX值进行分组,然后,在这些组中,我想以正常的排序顺序放置YYY值。 (注意:我同样高兴,实际上,以这种方式排序元组中的第二项,反向顺序第一个字,正常顺序第二个。)
以下是我所拥有的以及最终我想要的内容的一个例子...不知道该怎么做。
mylist = [
(u'community_news', u'Community: News & Information'),
(u'kf_video', u'KF: Video'),
(u'community_video', u'Community: Video'),
(u'kf_news', u'KF: News & Information'),
(u'kf_magazine', u'KF: Magazine')
]
我想在此列表中执行某种sort()
,将输出更改为:
sorted = [
(u'kf_magazine', u'KF: Magazine'),
(u'kf_news', u'KF: News & Information'),
(u'kf_video', u'KF: Video'),
(u'community_news', u'Community: News & Information'),
(u'community_video', u'Community: Video'),
]
我怀疑可能有一种pythonic方式来处理这个问题,但我无法绕过它。
答案 0 :(得分:10)
def my_cmp(x, y):
x1, x2 = x[0].split('_')
y1, y2 = y[0].split('_')
return -cmp(x1, y1) or cmp(x2, y2)
my_list = [
(u'community_news', u'Community: News & Information'),
(u'kf_video', u'KF: Video'),
(u'community_video', u'Community: Video'),
(u'kf_news', u'KF: News & Information'),
(u'kf_magazine', u'KF: Magazine')
]
sorted_list = [
(u'kf_magazine', u'KF: Magazine'),
(u'kf_news', u'KF: News & Information'),
(u'kf_video', u'KF: Video'),
(u'community_news', u'Community: News & Information'),
(u'community_video', u'Community: Video'),
]
my_list.sort(cmp=my_cmp)
assert my_list == sorted_list
答案 1 :(得分:8)
用于排序的自定义比较函数,如现有答案所示,确实可以轻松地按升序和降序排序 - 但它们存在严重的性能问题,并已在Python中删除3,只留下首选的自定义方法 - 自定义密钥提取功能......更加快速,但对于相对罕见的混合升序/降序分类用例更加精巧。
在Python 2.*
中,支持任意一种自定义(在sort
或sorted
: - )的同一调用中不支持 ,自定义比较function可以作为cmp=
命名参数传递;或者,自定义键提取函数可以作为key=
命名参数传递。在Python 3.*
中,只有后一个选项可用。
绝对值得理解密钥提取方法,即使您认为自己刚刚使用自定义比较方法解决了问题:不仅仅是为了提高性能,还为了面向未来(Python 3)和一般性( key=
方法也适用于min
,max
,itertools.groupby
......比cmp=
方法更为通用!)。
当所有关键子字段以相同的方式排序(所有提升或全部降序)时,密钥提取非常简单 - 您只需提取它们;如果“另一种方式”的子字段是数字(你只是在提取时改变它们的符号),它仍然很容易;精巧的案例就是你所拥有的 - 多个字符串字段必须以相反的方式进行比较。
解决问题的一个相当简单的方法是一个小小的垫片类:
class Reverser(object):
def __init__(self, s): self.s = s
def __lt__(self, other): return other.s < self.s
def __eq__(self, other): return other.s == self.s
请注意,您只需提供__lt__
和__eq__
(<
和==
运营商) - sort
,并且朋友会综合所有其他比较,如果需要的话,基于这两个。
因此,有了这个小辅助工具,我们可以轻松地进行......:
def getkey(tup):
a, b = tup[0].split('_')
return Reverser(a), b
my_list.sort(key=getkey)
如您所见,一旦您“获得”反向器和密钥提取概念,您基本上不需要为使用密钥提取而不是自定义比较付出代价:我建议的代码是反向器类的4个语句(您可以编写一次并将其放入你的“好东西包”模块中,三个用于密钥提取功能,当然还有一个用于sort
或sorted
调用 - 总共八个与4 + 1 == 5最简洁形式的自定义比较方法(即使用带有符号更改的cmp或带有交换参数的cmp)。三个陈述对于密钥提取的优势来说并不是一个代价! - )
对于这么短的名单来说,表现显然不是一个大问题,但是平均时间稍长(10倍)......:
# my_list as in the Q, my_cmp as per top A, getkey as here
def bycmp():
return sorted(my_list*10, cmp=my_cmp)
def bykey():
return sorted(my_list*10, key=getkey)
...
$ python -mtimeit -s'import so' 'so.bykey()'
1000 loops, best of 3: 548 usec per loop
$ python -mtimeit -s'import so' 'so.bycmp()'
1000 loops, best of 3: 995 usec per loop
也就是说,key=
方法在处理50个项目列表时已经显示了近两倍的性能提升(将列表排序速度提高了两倍) - 非常值得“8行”的适度价格超过5“,特别是我已经提到的所有其他优点!
答案 2 :(得分:2)
>>> def my_cmp(tuple_1, tuple_2):
xxx_1, yyy_1 = tuple_1[0].split('_')
xxx_2, yyy_2 = tuple_2[0].split('_')
if xxx_1 > xxx_2:
return -1
elif xxx_1 < xxx_2:
return 1
else:
return cmp(yyy_1, yyy_2)
>>> import pprint
>>> pprint.pprint(sorted(mylist, my_cmp))
[(u'kf_magazine', u'KF: Magazine'),
(u'kf_news', u'KF: News & Information'),
(u'kf_video', u'KF: Video'),
(u'community_news', u'Community: News & Information'),
(u'community_video', u'Community: Video')]
不是世界上最漂亮的解决方案......