`

Sorting Basics

 
阅读更多

Python lists have a built-in sort() method that modifies the list in-place and a sorted() built-in function that builds a new sorted list from an iterable.

There are many ways to use them to sort data and there doesn't appear to be a single, central place in the various manuals describing them, so I'll do so here.

 

Sorting Basics

 

A simple ascending sort is very easy -- just call the sorted() function. It returns a new sorted list:

 

>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

 

You can also use the list.sort() method of a list. It modifies the list in-place (and returns None to avoid confusion). Usually it's less convenient than sorted() - but if you don't need the original list, it's slightly more efficient.

 

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

 

Another difference is that the list.sort() method is only defined for lists. In contrast, the sorted() function accepts any iterable.

 

>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]

 

 

Key Functions

 

Starting with Python 2.4, both list.sort() and sorted() added a key parameter to specify a function to be called on each list element prior to making comparisons.

For example, here's a case-insensitive string comparison:

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

 

The value of the key parameter should be a function that takes a single argument and returns a key to use for sorting purposes. This technique is fast because the key function is called exactly once for each input record.

A common pattern is to sort complex objects using some of the object's indices as a key. For example:

 

>>> 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)]

 

The same technique works for objects with named attributes. For example:

 

>>> 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 Module Functions

 

The key-function patterns shown above are very common, so Python provides convenience functions to make accessor functions easier and faster. The operator module has itemgetter, attrgetter, and starting in Python 2.6 a methodcaller function.

Using those functions, the above examples become simpler and faster.

 

>>> 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)]

 

The operator module functions allow multiple levels of sorting. For example, to sort by grade then by age:

 

>>> 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)]

 

 

Ascending and Descending

 

Both list.sort() and sorted() accept a reverse parameter with a boolean value. This is using to flag descending sorts. For example, to get the student data in reverse age order:

 

>>> 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)]

 

 

Sort Stability and Complex Sorts

 

Starting with Python 2.2, sorts are guaranteed to be stable. That means that when multiple records have the same key, their original order is preserved.

 

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

 

Notice how the two records for 'blue' retain their original order so that ('blue', 1) is guaranteed to precede ('blue', 2).

This wonderful property lets you build complex sorts in a series of sorting steps. For example, to sort the student data by descending grade and then ascending age, do the age sort first and then sort again using grade:

 

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

 

The Timsort algorithm used in Python does multiple sorts efficiently because it can take advantage of any ordering already present in a dataset.

 

The Old Way Using Decorate-Sort-Undecorate

 

This idiom is called Decorate-Sort-Undecorate after its three steps:

  • First, the initial list is decorated with new values that control the sort order.
  • Second, the decorated list is sorted.
  • Finally, the decorations are removed, creating a list that contains only the initial values in the new order.

For example, to sort the student data by grade using the DSU approach:

>>> 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)]

 

This idiom works because tuples are compared lexicographically; the first items are compared; if they are the same then the second items are compared, and so on.

It is not strictly necessary in all cases to include the index i in the decorated list. Including it gives two benefits:

  • The sort is stable - if two items have the same key, their order will be preserved in the sorted list.
  • The original items do not have to be comparable because the ordering of the decorated tuples will be determined by at most the first two items. So for example the original list could contain complex numbers which cannot be sorted directly.

Another name for this idiom is Schwartzian transform, after Randal L. Schwartz, who popularized it among Perl programmers.

For large lists and lists where the comparison information is expensive to calculate, and Python versions before 2.4, DSU is likely to be the fastest way to sort the list. For 2.4 and later, key functions provide the same functionality.

 

The Old Way Using the cmp Parameter

 

Many constructs given in this HOWTO assume Python 2.4 or later. Before that, there was no sorted() builtin and list.sort() took no keyword arguments. Instead, all of the Py2.x versions supported a cmp parameter to handle user specified comparison functions.

In Py3.0, the cmp parameter was removed entirely (as part of a larger effort to simplify and unify the language, eliminating the conflict between rich comparisons and the __cmp__ methods).

In Py2.x, sort allowed an optional function which can be called for doing the comparisons. That function should take two arguments to be compared and then return a negative value for less-than, return zero if they are equal, or return a positive value for greater-than. For example, we can do:

 

>>> 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]

 

When porting code from Python 2.x to 3.x, the situation can arise when you have the user supplying a comparison function and you need to convert that to a key function. The following wrapper makes that easy to do:

 

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K(object):
        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

 

To convert to a key function, just wrap the old comparison function:

 

>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
[5, 4, 3, 2, 1]

 

In Python 2.7, the cmp_to_key() tool was added to the functools module.

 

Odd and Ends

 

  • For locale aware sorting, use locale.strxfrm() for a key function or locale.strcoll() for a comparison function.

  • The reverse parameter still maintains sort stability (i.e. records with equal keys retain the original order). Interestingly, that effect can be simulated without the parameter by using the builtin reversed function twice:

    • >>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
      >>> assert sorted(data, reverse=True) == list(reversed(sorted(reversed(data))))
  • To create a standard sort order for a class, just add the appropriate rich comparison methods:
    • >>> Student.__eq__ = lambda self, other: self.age == other.age
      >>> Student.__ne__ = lambda self, other: self.age != other.age
      >>> Student.__lt__ = lambda self, other: self.age < other.age
      >>> Student.__le__ = lambda self, other: self.age <= other.age
      >>> Student.__gt__ = lambda self, other: self.age > other.age
      >>> Student.__ge__ = lambda self, other: self.age >= other.age
      >>> sorted(student_objects)
      [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

      For general purpose comparisons, the recommended approach is to define all six rich comparison operators. The functools.total_ordering class decorator makes this easy to implement.

  • Key functions need not access data internal to objects being sorted. A key function can also access external resources. For instance, if the student grades are stored in a dictionary, they can be used to sort a separate list of student names:
    • >>> students = ['dave', 'john', 'jane']
      >>> newgrades = {'john': 'F', 'jane':'A', 'dave': 'C'}
      >>> sorted(students, key=newgrades.__getitem__)
      ['jane', 'dave', 'john']
  • Alternate datastructure for performance with ordered data
    • If you're needing a sorted list every step of the way as you process each item to be added to the sorted list, then list.sort(), sorted() and bisect.insort() are all very slow and tend to yield quadratic behavior or worse. In such a scenario, it's better to use something like a heap, red-black tree or treap (like the included heapq module, or this treap module - shameless plug added by python treap module author).

0
0
分享到:
评论

相关推荐

    Basics_Sorting:根据需要说明可用的不同排序算法

    在编程领域,排序是至关重要的一个环节,尤其是在处理大量数据时。Java作为一种广泛使用的编程语言,提供了多种排序算法供开发者选择。本篇文章将详细介绍几种常见的排序算法及其在Java中的实现。...

    Data.Structures.and.Algorithms.USING.C

    BASICS 1. Overview 2. Environment Setup ALGORITHM 3. Algorithms ─ Basics 4. Asymptotic Analysis 5. Greedy Algorithms 6. Divide & Conquer 7. Dynamic Programming DATA STRUCTURES 8. Basic Concepts 9. ...

    Coding.Interview.Questions.3rd.Edition.epub )

    Sorting Chapter 15. Searching Chapter 16. Selection Algorithms [Medians] Chapter 17. Symbol Tables Chapter 18. Hashing Chapter 19. String Algorithms Chapter 20. Algorithms Design Techniques Chapter ...

    Java 9 Data Structures and Algorithms

    Use binary search, sorting, and efficient sorting—quicksort and merge sort Work with the important concept of trees and list all nodes of the tree, traversal of tree, search trees, and balanced ...

    Algorithms: Design and Analysis

    The second section covers the data structures basics, trees, graphs, sorting in linear and quadratic time. Section three discusses the various design techniques namely, divide and conquer, greedy ...

    CS_basics:我的CS学习

    toptal-sorting- algorithms-在线排序算法 作法:在Google工作-编码/工程面试范例 bit_manipulation.md-位操作指南表 Py TimeComplexity -Python TimeComplexity doc Py数据模型-Python数据模型doc SQL练习-w3...

    Learning.JavaScript.Data.Structures.and.Algorithms.1783554878

    This book begins by covering the basics of the JavaScript language and then moves on to discuss the most important data structures such as array, queue, stack, and linked list. You will also gain an ...

    算法与数据结构-综合提升 C++版

    算法与数据结构-综合提升 C++版 资源列表: 00-0pening 01-Why-Algorithms 02-Sorting-Basic 03-Sorting-Advance 04-Heap 05-Binary-Search-Tree...07-Graph-Basics08-Minimum-Span-Trees 09-Shortest-Path 10-Ending

    计算机编程及常用术语大全(英汉对照).pdf

    * 排序算法(Sorting Algorithm) * 搜索算法(Searching Algorithm) * 图形算法(Graph Algorithm) * 动态规划(Dynamic Programming) * 贪心算法(Greedy Algorithm) * 回溯算法(Backtracking Algorithm) ...

    Learning Perl (7th Edition)

    Learning Perl teaches you the basics and shows you how to write programs up to 128 lines long—roughly the size of 90% of the Perl programs in use today. Each chapter includes exercises to help you ...

    modern java recipes(英文高清版-带书签)

    The basics of lambda expressions and method references Interfaces in the java.util.function package Stream operations for transforming and filtering data Comparators and Collectors for sorting and ...

    Modern Java Recipes(O'Reilly,2017)

    The basics of lambda expressions and method references Interfaces in the java.util.function package Stream operations for transforming and filtering data Comparators and Collectors for sorting and ...

    算法刷题笔记leetcode/lintcode

    - **Basics Sorting**(基本排序算法) - Bubble Sort(冒泡排序) - Selection Sort(选择排序) - Insertion Sort(插入排序) - Merge Sort(归并排序) - Quick Sort(快速排序) - Heap Sort(堆排序) ...

    Modern Java Recipes

    Comparators and Collectors for sorting and converting streaming data Combining lambdas, method references, and streams Creating instances and extract values from Java’s Optional type New I/O ...

    Learn.to.Program.with.C.1484213726

    Chapter 2: C – The Basics Chapter 3: Programs with Sequence Logic Chapter 4: Programs with Selection Logic Chapter 5: Programs with Repetition Logic Chapter 6: Characters Chapter 7: Functions Chapter...

    PHP.Arrays.in.PHP.7

    Chapter 5: PHP Functions—Changing, Splitting, Slicing, and Sorting Arrays Chapter 6: PHP Functions—Comparing and Merging Arrays Chapter 7: PHP Functions—Searching, Traversing, and Displaying Arrays

    Beginning COBOL for Programmers

    14. Sorting and Merging 15. String Manipulation 16. Creating Large Systems 17. Direct Access Files 18. The COBOL Report Writer 19. OO-COBOL Book Details Paperback: 588 pages Publisher: Apress; 1 ...

Global site tag (gtag.js) - Google Analytics