How to Sorting

翻译:如何排序


Python的list有一个内建函数sort()方法,它是更改元素在list中的位置来排序,另一个内建函数sorted()是从可迭代的对象新建一个排序后的list返回。
进行排序的方法有很多种,下面我们来看一看。

 

向后兼容摘记


许多介绍资料中,都没有提到python2.4中如何排序。在2.4版本之前,没有内建方法sorted(),并且,list.sort()没有关键字参数。

 

基本数据类型排序


简单的升序排序是非常容易的,只需要调用sorted()函数即可,它将返回一个新的排过序的list。

  1. >>> print sorted([52314])
  2. [12345]

也可以用list的sort()方法,它在原list上修改(返回None,避免引起迷惑),通常,如果不需要原始的list,sorted()使用得会更多一下,但是它小巧并且效率更高。

 

  1. >>> ls1 = [52314]
  2. >>> ls1.sort()
  3. >>> print ls1
  4. [12345]

排序的比较函数必须能够经比较后得出结果,默认使用的比较函数等同于使用内建函数cmp:

  1. >>> print sorted([52314], cmp)
  2. [12345]

内建cmp函数是比较两个对象,x<y返回负数,x=y返回0,x>y返回正数。在排序时,比较函数必须对比较对象保持一致的合理比较操作。
当然了,也可以使用自定义的比较函数。比如正数比较:

  1. >>> def numeric_compare(x,y):
  2. ...  return x - y
  3. ... 
  4. >>> 

或者,也可以这样定义(有些繁冗):

  1. >>> def numeric_compare(x, y):
  2. ...  if x > y:
  3. ...   return 1
  4. ...  elif x == y:
  5. ...   return 0
  6. ...  else# x  < y
  7. ...   return -1
  8. ...  
  9. >>> ls1 = [52314]
  10. >>> ls1.sort(numeric_compare)
  11. >>> print ls1
  12. [12345]

顺便说一下,如果减法操作的结果超出最大整数范围,如:sys.maxint - (-1),sort()函数就不会起作用了。

或者,也可以使用lambda定义一个匿名函数,如:

  1. >>> ls1 = [52314]
  2. >>> ls1.sort(lambda x, y : x - y)
  3. >>> print ls1
  4. [12345]

Python2.4中,sort()方法中新增了三个关键字参数:cmp, key, reverse。cmp是排序函数,前面的例子可以改写为:

  1. >>> ls1.sort(cmp=numeric_compare)
  2. >>> ls1.sort(cmp=lambda x, y: x - y)

reverse参数是布尔类型,为True时,翻转升序排序的结果。

  1. >>> ls1 = [52314]
  2. >>> ls1.sort(reverse=True)
  3. >>> ls1
  4. [54321]

在Python2.4之前,翻转比较结果

  1. >>> ls1 = [52314]
  2. >>> def numeric_compare(x,y):
  3. ...  return y - x
  4. ... 
  5. >>> 
  6. >>> ls1.sort(numeric_compare)
  7. >>> print ls1
  8. [54321]

更普遍的写法是 return cmp(y,x)或者-cmp(x,y)。
然而,更高效的使用方法是先排序,再调用reverse()方法

  1. >>> ls1 = [52314]
  2. >>> ls1.sort()
  3. >>> ls1.reverse()
  4. >>> ls1
  5. [54321]
  6. >>> 

根据keys排序


Python2.4的参数key可以让你得到迭代list的每个元素的key,然后使用这个key,如:

  1. >>> a = "This is a test string from Andrew".split()
  2. >>> a.sort(key=str.lower)
  3. >>> a
  4. ['a''Andrew''from''is''string''test''This']

key的值必须是接受一个参数的函数,该函数返回的值作为排序用

内建函数基本上是我们常常使用的,比如string.lower(),operator模块包含了大量的有用函数,比如,可以使用operator.itemgetter():

  1. >>> import operator
  2. >>> ls = [ ('c'2), ('d'1), ('a'4), ('b'3) ]
  3. >>> map(operator.itemgetter(0), ls)
  4. ['c''d''a''b']
  5. >>> map(operator.itemgetter(1), ls)
  6. [2143]
  7. >>> sorted(ls, key=operator.itemgetter(1))
  8. [('d'1), ('c'2), ('b'3), ('a'4)]

在Python2.4之前的版本中,没有提供sort()的key参数,所以需要自己写

  1. >>> a = "This is a test string from Andrew".split()
  2. >>> a.sort(lambda x, y:cmp(x.lower(), y.lower()))
  3. >>> print a
  4. ['a''Andrew''from''is''string''test''This']
  5. >>> 

比较一下字符串转为小写的效率,耗时O(n log n),但是Python2.4中的耗时是 O(n),它更高效。你自己也可以优化key函数达到高效。


 

  1. >>> words =  "This is a test string from Andrew".split()
  2. >>> deco = [(word.lower(), i, word) for i, word in enumerate(words)]
  3. >>> deco
  4. [('this'0'This'), ('is'1'is'), ('a'2'a'), ('test'3'test'), ('string'4'string'), ('from'5'from'), ('andrew'6'Andrew')]
  5. >>> deco.sort()
  6. >>> deco
  7. [('a'2'a'), ('andrew'6'Andrew'), ('from'5'from'), ('is'1'is'), ('string'4'string'), ('test'3'test'), ('this'0'This')]
  8. >>> new_words = [word for _, _, word in deco]
  9. >>> new_words
  10. ['a''Andrew''from''is''string''test''This']

这种风格被称为装饰-排序-反装饰,它包含三步操作:
1.初始list,用控制排序的新值装饰list
2.对装饰后的list排序
3.去除装饰,创建新list,改list包含在新排序后的初始值

 

类的比较


比较两个基本数据类型,比如int或者string,python内建的函数就可以完成,比较类对象实例,就需要用__cmp__自定义比较函数,如:

  1. >>> class Spam:
  2. ...  def __init__(self, spam, eggs):
  3. ...   self.spam = spam
  4. ...   self.eggs = eggs
  5. ...  def __cmp__(self, other):
  6. ...   return cmp(self.spam + self.eggs, other.spam + other.eggs)
  7. ...  def __str__(self):
  8. ...   return str(self.spam + self.eggs)
  9. ...  
  10. >>> a = [Spam(1,4),Spam(9,3),Spam(4,6)]
  11. >>> a.sort()
  12. >>> a
  13. [<__main__.Spam instance at 0x02A21A08>, <__main__.Spam instance at 0x02A219B8>, <__main__.Spam instance at 0x02A21A30>]
  14. >>> for spam in a:
  15. ...  print str(spam)
  16. ...  
  17. 5
  18. 10
  19. 12

有时候,需要根据class的某个属性排序,最好的办法就是定义__cmp__方法,比较他们的值,但是不能在不同的时间比较不同的值。
Python2.4中有operator.attrgetter()函数,使用它来完成比较更容易一些:

 

  1. >>> import operator
  2. >>> a = [Spam(1,4),Spam(9,3),Spam(4,6)]
  3. >>> a.sort(key=operator.attrgetter('eggs'))
  4. >>> for spam in a:
  5. ...  print spam.eggs, str(spam)
  6. ...  
  7. 3 12
  8. 4 5
  9. 6 10

在Python2.4中,如果你不想包含operator模块,那么可以这样做:

  1. >>> a = [Spam(1,4),Spam(9,3),Spam(4,6)]
  2. >>> a.sort(key=lambda obj:obj.eggs)
  3. >>> for spam in a:
  4. ...  print spam.eggs, str(spam)
  5. ...  
  6. 3 12
  7. 4 5
  8. 6 10

再提一次,Python早期版本中,你需要这样做:

  1. >>> a = [Spam(1,4),Spam(9,3),Spam(4,6)]
  2. >>> a.sort(lambda x, y :cmp(x.eggs, y.eggs))
  3. >>> for spam in a:
  4. ...  print spam.eggs, str(spam)
  5. ...  
  6. 3 12
  7. 4 5
  8. 6 10

如果你想比较随意的两个属性,你可以定义自己的比较函数对象,这将使用实例的__call__方法,如:

  1. >>> class CmpAttr:
  2. ...  def __init__(self, attr):
  3. ...   self.attr = attr
  4. ...  def __call__(self, x, y):
  5. ...   return cmp(getattr(x, self.attr), getattr(y, self.attr))
  6. ...  
  7. >>> a = [Spam(1,4),Spam(9,3),Spam(4,6)]
  8. >>> a.sort(CmpAttr('spam'))
  9. >>> for spam in a:
  10. ...  print spam.spam, spam.eggs, str(spam)
  11. ...  
  12. 1 4 5
  13. 4 6 10
  14. 9 3 12

当然,如果你想取得更快的排序速度,你可以把对象的属性作为组成一个list类型的中间值,然后对中间值进行排序。

我们再来总结一下,一共有6中方法对list进行排序:
1.使用默认的方法进行排序
2.使用比较函数进行排序
3.使用reverse,不使用比较函数
4.使用中间值进行排序
5.使用定义了cmp方法的class排序
6.使用排序函数对象

 

 

 

 

 

 

 

 

 

 

 


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值