Python 列表有内置的 list.sort()
方法来原地排序列表元素,Python 同样发布了扩充了可迭代数据类型的方法 sorted()
。sortingx 是 linjing-lab 基于 6 种主流的排序函数研发的一款排序工具箱,拥有高速地多关键字排序与内存优化的核心设计。
实现一个简单的升序排序非常简单,只需要下载 sortingx,并导入该包的排序模块即可:
>>> pip install sortingx
>>> import sortingx as six
在下载完成 sortingx 之后,以 bubble 函数为例来展示它的用法:
>>> six.bubble([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
在v1.1.0与v1.1.1中,排序一组数据会返回 None,原因在于这两个版本只允许了列表类型数据的传入,并且与 list.sort()
方法对齐的:
>>> data = [5, 2, 3, 1, 4]
>>> six.bubble(data) # v1.1.0 & v1.1.1
None
>>> print(data)
[1, 2, 3, 4, 5]
在v1.1.2及以后,由于增加数据类型转换函数,sortingx 系列可以支持更多地可迭代数据类型,比如下面这个例子:
>>> six.bubble({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
v1.2.0相较于v1.1.2与v1.1.3来说,它优化了生成函数的内核,对于字符串这种可迭代数据类型做出了统一的处理,算法的健壮性得到了很好地保证。v1.1.2与v1.1.3 对于基于字符串的多关键字参数排序的健壮性不是很好,尽管停机速度比较块,但没有与 core 函数达到100%的协同。在v1.2.1中,生成函数相较于v1.2.0中更加轻量级,core函数被替代为内置的比较规则。
sortingx 同样具有关键字参数,用于指定在进行比较之前要对每个列表元素调用的函数(或其他可调用的函数)。
例如,这里是不区分大小写的字符串比较:
>>> six.bubble("This is a test string from LinJing".split(), key=str.lower)
['a', 'from', 'is', 'LinJing', 'string', 'test', 'This']
key 参数的值应该是一个函数(或其他可调用的函数),它接受一个参数并返回用于排序的键。sortingx 对于生成的比较数组有良好的补全与验证。
一种常见的模式使用对象的一些索引作为关键字对复杂对象进行排序,例如:
>>> student_tuples = (
... ('Jack', 'A', 21),
... ('John', 'B', 19),
... ('dave', 'B', 22),
... )
>>> six.bubble(student_tuples, key=lambda student: student[2]) # sort by age
[('John', 'B', 19), ('Jack', 'A', 21), ('dave', 'B', 22)]
相同的技术适用于具有命名属性的对象。例如:
>>> 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('Jack', 'A', 21),
... Student('John', 'B', 19),
... Student('dave', 'B', 22),
... )
>>> six.bubble(student_objects, key=lambda student: student.age) # sort by age
[('John', 'B', 19), ('Jack', 'A', 21), ('dave', 'B', 22)]
上面显示的关键函数模式非常常见,因此Python提供了方便的函数,使访问器函数更容易、更快。运算符模块具有itemgetter()、attrgetter() 和 methodcaller() 函数。
使用这些函数,上述示例变得更简单、更快:
>>> from operator import itemgetter, attrgetter
>>> six.bubble(student_tuples, key=itemgetter(2))
[('John', 'B', 19), ('Jack', 'A', 21), ('dave', 'B', 22)]
>>> six.bubble(student_objects, key=attrgetter('grade', 'age'))
>[('John', 'B', 19), ('Jack', 'A', 21), ('dave', 'B', 22)]
sortingx 中控制是否逆序的参数是与包内驱动函数运作的三个子函数协同处理的,例如下面这个例子:
>>> six.bubble(student_tuples, key=itemgetter(2), reverse=True)
[('dave', 'B', 22), ('Jack', 'A', 21), ('John', 'B', 19)]
>>> six.bubble(student_objects, key=attrgetter('age'), reverse=True)
[('dave', 'B', 22), ('Jack', 'A', 21), ('John', 'B', 19)]
排序保证是稳定的,这意味着当多个记录具有相同的键值时,将保留其原始顺序。
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> six.bubble(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
这些良好的性质允许你在一系列排序步骤中构建复杂的排序。例如,要按 grade 降序然后 age 升序对学生数据进行排序,可以弦 age 排序,然后再使用 grade 排序:
>>> s = six.merge(student_objects, key=attrgetter('age')) # sort on secondary key
>>> six.bubble(s, key=attrgetter('grade'), reverse=True) # now sort on primary key, descending
[('John', 'B', 19), ('dave', 'B', 22), ('Jack', 'A', 21)]
你可以在过程中改变排序方法,以达到更快的速度。这可以被抽象为一个包装函数,该函数能接受一个列表以及字段和顺序的元组,以对它们进行多重排序。
>>> def multisort(xs, specs):
... for key, reverse in specs:
... six.bubble(xs, key=attrgetter(key), reverse=reverse)
... return xs
multisort(list(student_objects), (('age', False), ('grade', True)))
在对两个对象进行比较时,排序例程使用 <。因此,通过定义一个 __lt__()
方法,很容易为一个类添加标准的排序顺序。
>>> Student.__lt__ = lambda self, other: self.age < other.age
>>> six.bubble(student_objects)
[('John', 'B', 19), ('Jack', 'A', 21), ('dave', 'B', 22)]
键函数不需要直接依赖于被排序的对象。键函数还可以访问外部资源。
>>> students = ['dave', 'John', 'Jack']
>>> newgrades = {'John': 'F', 'Jack': 'A', 'dave': 'C'}
>>> six.bubble(students, key=newgrades.__getitem__)
['Jack', 'dave', 'John']
API |
---|
sortingx.bubble(__iterable: Iterable[_T], key: Optional[Callable[[_T], SupportsRichComparison]]=None, reverse: bool=False) -> List[_T] |
sortingx.insert(__iterable: Iterable[_T], key: Optional[Callable[[_T], SupportsRichComparison]]=None, reverse: bool=False) -> List[_T] |
sortingx.heap(__iterable: Iterable[_T], key: Optional[Callable[[_T], SupportsRichComparison]]=None, reverse: bool=False) -> List[_T] |
sortingx.merge(__iterable: Iterable[_T], key: Optional[Callable[[_T], SupportsRichComparison]]=None, reverse: bool=False) -> List[_T] |
sortingx.shell(__iterable: Iterable[_T], key: Optional[Callable[[_T], SupportsRichComparison]]=None, reverse: bool=False) -> List[_T] |
sortingx.quick(__iterable: Iterable[_T], key: Optional[Callable[[_T], SupportsRichComparison]]=None, reverse: bool=False) -> List[_T] |