Python中的排序

来源:互联网 发布:数据迁移工程师 编辑:程序博客网 时间:2024/05/29 18:29

这篇博客介绍了Python(3.3)中的序列数据类型和对象的排序,在原文基础上有所修改。

原文链接http://docs.python.org/3/howto/sorting.html

基本排序:

Python中的列表(list)有两个内建排序函数

1.list.sort(),这会替换原列表:

        sort(*, key=None, reverse=None)

2.sorted()函数会接受一个可迭代序列,返回一个新列表,注意是列表(序列进去,列表出来):

        sorted(iterable[, key][, reverse])

下面是一个升序排序的简单例子:

>>> sorted([5, 2, 3, 1, 4])[1, 2, 3, 4, 5]>>> a = [5, 2, 3, 1, 4]>>> a.sort()>>> a[1, 2, 3, 4, 5]

List.sort()只能在列表上使用,而sorted()可在任何可迭代序列上使用:
>>> sorted({1: ’D’, 2: ’B’, 3: ’B’, 4: ’E’, 5: ’A’})[1, 2, 3, 4, 5]

接受参数的排序:

C++和其他高级语言一样,Python同样接受排序参数来使排序函数按照特定的比较规则进行排序。

>>> sorted("This is a test string from Andrew".split(), key=str.lower)[’a’, ’Andrew’, ’from’, ’is’, ’string’, ’test’, ’This’]>>> sorted("This is a test string from Andrew".split())[’Andrew’,’This’,’a’, ’from’, ’is’, ’string’, ’test’, ]

Key的值是一个函数,返回一种用于排序的比较方式。在复杂的对象中,我们可以用key指定特定的对象成员来进行排序:
>>> student_tuples = [(’john’, ’A’, 15),(’jane’, ’B’, 12),(’dave’, ’B’, 10),]>>> sorted(student_tuples, key=lambda student: student[2])# sort by age[(’dave’, ’B’, 10), (’jane’, ’B’, 12), (’john’, ’A’, 15)]>>> class Student:  def __init__(self, name, grade, age):      self.name = name      self.grade = grade      self.age = age  def __repr__(self):      return repr((self.name, self.grade, self.age))>>> student_objects = [Student(’john’, ’A’, 15),Student(’jane’, ’B’, 12),Student(’dave’, ’B’, 10),]>>> sorted(student_objects, key=lambda student: student.age)# sort by age[(’dave’, ’B’, 10), (’jane’, ’B’, 12), (’john’, ’A’, 15)]

使用Operator 模块中的函数作为key

Operator模块中的itemgetter(), attrgetter(), 和methodcaller()可以更快速便捷的获得对象属性。

例如我们想按照年龄来进行排序:

>>> from operator import itemgetter, attrgetter>>> sorted(student_tuples, key=itemgetter(2))[(’dave’, ’B’, 10), (’jane’, ’B’, 12), (’john’, ’A’, 15)]>>> sorted(student_objects, key=attrgetter(’age’))[(’dave’, ’B’, 10), (’jane’, ’B’, 12), (’john’, ’A’, 15)]

我们也可以先按成绩,后按年龄来排序:
>>> sorted(student_tuples, key=itemgetter(1,2))[(’john’, ’A’, 15), (’dave’, ’B’, 10), (’jane’, ’B’, 12)]>>> sorted(student_objects, key=attrgetter(’grade’, ’age’))[(’john’, ’A’, 15), (’dave’, ’B’, 10), (’jane’, ’B’, 12)]

升序与降序:

除了key参数以外,sortedsort函数同样接受reverse参数,reverse参数是一个布尔值,TrueFalse分别对应升序与降序:

>>> sorted(student_tuples, key=itemgetter(2), reverse=True)[(’john’, ’A’, 15), (’jane’, ’B’, 12), (’dave’, ’B’, 10)]>>> sorted(student_objects, key=attrgetter(’age’), reverse=True)[(’john’, ’A’, 15), (’jane’, ’B’, 12), (’dave’, ’B’, 10)]

顺序稳定性与复杂排序:

在一次排序中,排序函数只会改变需要改变的对象位置,例如我们按照年龄排序,当存在两个相同年龄的人时,排序函数不会改变这两个人在原序列中的顺序。

>>> data = [(’red’, 1), (’blue’, 1), (’red’, 2), (’blue’, 2)]>>> sorted(data, key=itemgetter(0))[(’blue’, 1), (’blue’, 2), (’red’, 1), (’red’, 2)]


这一属性允许我们在序列中使用一系列的步骤进行排序,而不会出现逻辑问题。例如我们想在student对象中按成绩降序排列,且按年龄升序排列。

>>> s = sorted(student_objects, key=attrgetter(’age’))# sort on secondary key>>> sorted(s, key=attrgetter(’grade’), reverse=True)# then sort on primary key, descending[(’dave’, ’B’, 10), (’jane’, ’B’, 12), (’john’, ’A’, 15)]

Python中使用的是Timsort排序算法,这使python的复杂排序效率非常之高。


2.X版本中的DSU排序:

DSU Decorate-Sort-Undecorate)排序包含三个步骤:

1.为要排序的列表添加一个新值--决定列表顺序的值,这个新的列表称为Decorated-List(Decorate)

2.排序(sort)

3.还原拥有了新顺序的原列表,即删除掉1中创建的顺序控制值(Undecorate)

>>> decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)]>>> decorated.sort()>>> [student for grade, i, student in decorated] # undecorate[(’john’, ’A’, 15), (’jane’, ’B’, 12), (’dave’, ’B’, 10)]

包含进索引i并不是必需的,但包含i有这么两点好处:

1.顺序稳定性。元组按照字典序(先第一个比,若相同第二个比)比较,包含进i可使在grade相同的情况下,保持住原列表中的顺序。

2.防止元列表对象无法进行比较。包含i使得decorated-list最多只可比较两个元素,第三个元素(例中的student)比较不到。当第三个元素是不可比较的(如复数),这可拯救一次错误的排序,使得列表得以有稳定顺序。

DSUperl编程中称作 Schwartzian transform,在python中,当我们拥有了key参数后,这个技巧已经不是必需的了。


2.X版本中的cmp参数:

3.0以后的版本中,cmp参数被彻底移除。

Python2.X版本中,我们可以写出一个cmp函数,并将其传入sort函数,像这样:

>>> def numeric_compare(x, y):return x - y>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)[1, 2, 3, 4, 5]#Or you can reverse the order of comparison with:>>> def reverse_numeric(x, y):return y - x>>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric)[5, 4, 3, 2, 1]

3.X中,虽然不再提供cmp,但我们可以使用标准库functools functools.cmp_to_key()函数来转换cmp函数:
>>> sorted([5, 2, 4, 1, 3], key=functools.cmp_to_key(reverse_numeric))[5, 4, 3, 2, 1]

Cmp_to_key的函数实现类似于这样:
def cmp_to_key(mycmp):  ’Convert a cmp= function into a key= function’  class K:      def __init__(self, obj, *args):          self.obj = obj      def __lt__(self, other):          return mycmp(self.obj, other.obj) < 0      def __gt__(self, other):          return mycmp(self.obj, other.obj) > 0      def __eq__(self, other):          return mycmp(self.obj, other.obj) == 0      def __le__(self, other):          return mycmp(self.obj, other.obj) <= 0      def __ge__(self, other):          return mycmp(self.obj, other.obj) >= 0      def __ne__(self, other):          return mycmp(self.obj, other.obj) != 0  return K

其他与结束:

1.对象间的比较:在Python中,对象间的比较默认使用__it__()方法。所以我们可以为对象创建__it__()方法来为其定义顺序。

>>> Student.__lt__ = lambda self, other: self.age < other.age>>> sorted(student_objects)[(’dave’, ’B’, 10), (’jane’, ’B’, 12), (’john’, ’A’, 15)]

2.key的函数不仅仅局限于要排序的序列内,它可以扩展到一个外部序列,当然这个外部序列需要匹配需要排序的键值。
>>> students = [’dave’, ’john’, ’jane’]>>> newgrades = {’john’: ’F’, ’jane’:’A’, ’dave’: ’C’}>>> sorted(students, key=newgrades.__getitem__)[’jane’, ’dave’, ’john’]

在The Art of Computer Programming这本书中,作者指出计算机有25%的时间在进行排序操作。我们不知道这个数字是否准确,但排序的确是计算机科学中极为重要的一部分。