python中的sort函数与cmp参数

排序,sort的cmp参数,sort实现原理

Posted by UlyC on April 30, 2018

你见过驴在麦尖上跑吗?——大咕咕鸡

我的博客

python的sort函数与cmp参数

1.基本用法

###描述 sorted() 函数对所有可迭代的对象进行排序操作。

###语法 sorted 语法:

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

参数说明:

iterable -- 可迭代对象。
cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
返回值
返回重新排序的列表。

2.sort函数中的cmp参数

sort中的key参数使用lambda只能对取出的一个元素操作,两个值时就会报错。

而且在python2.4前,sorted()和list.sort()函数没有提供key参数,但是提供了其他语言都常用的cmp参数来让用户指定比较函数 ,默认值为None,可以传入一个具有两个参数的匿名函数。

不过在python3中为了节省内存已经取消。

本身cmp是一个独立函数:cmp(x ,y) ,当x<y会返回负数、当x>y会返回正数、当x=y则返回0。

   >>> cmp(11,56)  
     
    -1
     
    >>> cmp(56,11)  
     
    1
     
    >>> cmp(11,11)  
     
    0
     
    >>> cmp('abc','b')   
    -1

简单用法有

>>> def numeric_compare(x, y):
        return x - y
>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]

这样看起来好像没什么用,查了许多资料说的也语焉不详,来试一下:

>>> a = [-5, -2, 3, 6]
>>> sorted(a, cmp=lambda x, y: x-y)
>>> a
[-5, -2, 3, 6]   # 是正常正序排序


>>> a.sort(cmp=lambda x, y: y -x)
>>> a
[6, 3, -2, -5]   # 这是逆序排序

# 好玩的来了~~  
>>> a = [-2, -5, 6, 3]  
>>> sorted(a, cmp=lambda x, y: -x)   
[3, 6, -2, -5]  
>>> sorted(a, cmp=lambda x, y: -y)  
[-2, -5, 3, 6]  
>>> sorted(a,cmp=lambda x, y: x+y)  
[-5, -2, 6, 3]  
>>> sorted(a,cmp=lambda x, y: x )  
[-5, -2, 6, 3]  
>>> sorted(a,cmp=lambda x, y: y )
[6, 3, -5, -2]  
>>> sorted(a,cmp=lambda x, y: 1 )
[-2, -5, 6, 3]  
>>> sorted(a,cmp=lambda x, y: 0)
[-2, -5, 6, 3]  
>>> sorted(a,cmp=lambda x, y: -1)  
[3, 6, -5, -2]
>>> sorted(a,cmp=lambda x, y: True)  
[-2, -5, 6, 3]
>>> sorted(a,cmp=lambda x, y: False)  
[-2, -5, 6, 3] 
>>> sorted(a,cmp=lambda x, y: None)  
Traceback (most recent call last):    
  File "<stdin>", line 1, in <module>  
TypeError: comparison function must return int, not NoneType  
 # 啊哦,不能返回空值,但是说可以返回任意int类型

>>> sorted(a,cmp=lambda x, y: 5)  
[-2, -5, 6, 3]
>>> sorted(a,cmp=lambda x, y: -2)  
[3, 6, -5, -2]

可以看出返回True和False得到的结果相同,且与返回1,和0的结果相同,结果不同的是返回非负数和负数: 正数时,按照列表本身的顺序排列; 负数时,按照列表本身顺序的逆序排序,类似于切片步长为-1时。

要研究返回值为“-x”,“-y”,“x+y”等时,为什么会得到那种奇怪的效果,我用了pycharm 的debug来分析。

sorted(a, cmp=lambda x, y: -x),这会创建一个副本,不会改变列表本身,之前用sorted是为了更直观观察各种不同值对列表的影响。

为了观察x,y在每一步的值,所以现在以a.sort(cmp=lambda x, y: -x)为例,整理如下:

可以看出,系统逻辑为,3要排在6之前,6要排在-2之前,而-2要在-5之前,于是得到结果。 过程中,列表a一直为空,最后一步编译器才将排列好的值填进。

至于编译器取出列表中元素依据的是什么顺序就是更底层的原理了,在此不再深究。

最后: 在pytho3中如果想要在sort函数中使用cmp参数,需要从模块导入函数from functools import cmp_to_key,然后在sort将其赋值给key,来看例子:

 from functools import cmp_to_key
    
 nums = [1, 3, 2, 4]
 nums.sort(key=cmp_to_key(lambda a, b: a - b))
 print(nums)  # [1, 2, 3, 4]

3.sort函数调用Operator模块函数

operator模块有itemgetter,attrgetter,从2.6开始还增加了methodcaller方法。

>>> 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),
]
>>> 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')) #按列表中对象的“age”属性排序
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

允许多级的排序,例如,先以grade,然后再以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)]

4.python中sort函数的实现原理

C++内部的sort是由快排直接插入堆排序混合的,具体详情见STL源码剖析。
当数据量比较大的时候先用的快排,当数据量小的时候用直接插入,因为当数据量变小时,快排中的每个部分基本有序,接近直接插入的最好情况的时间复杂度O(n),就比快排要好一点了。

java内部用的都是归并排序。 因为C++模板有很强的inline优化机制,比较操作相对于赋值(移动)操作要快的多(尤其是元素较大时),而java中的情况正好相反,移动(赋值)一般比比较快;另一方面,一般情况下,归并排序的比较次数小于快速排序的比较次数,而移动次数一般多于快速排序的移动次数,二者大约都是2~3倍的差距。因为这样,java标准库中的一般排序使用的归并排序的一种变种。

python内部的sort采用的是混合(hybrid)排序,规模小的时候采用binary insertion(折半插入排序),规模大的时候采用samplesort(样本排序)


知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。