Python 默认排序或自定义排序总结

参考文章

https://www.cnblogs.com/davis12/p/14659385.html
https://blog.csdn.net/ClaireSy/article/details/123762598
https://www.cnblogs.com/Zzbj/p/15522955.html
https://www.ab62.cn/article/7067.html
https://blog.csdn.net/caoxinjian423/article/details/83272321

sort函数

描述

sort() 函数用于对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。


语法

list.sort(key=None, reverse=False)

  • key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse – 排序规则,reverse = True 降序, reverse = False 升序(默认)

注意:函数无返回值,只能对列表进行排序

# 列表
l = [1, 5, 6, 3, 2]
# 降序
l.sort(reverse=True)
print(l)

输出:

[1, 2, 3, 5, 6]

sorted函数

描述

Python内置的sorted()函数,用于对列表进行排序。使用该函数进行排序后,原列表的元素顺序不变。


语法

Python2中: sorted(iterable, cmp=None, key=None, reverse=False)
Python3中: sorted(iterable,key=None,reverse=False)
Python3相比于Python2废弃了cmp参数,这个下文会说。

返回值: 返回一个新的list


升序与降序

简单的升序排序是非常容易的。只需要调用sorted()方法。默认是升序排序。可指定参数reverse 来表示升序降序排序,True表示降序,Fasle表示升序。

# 升序排序
sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
# 降序排序
sorted([5, 2, 3, 1, 4], reverse=True)
[5, 4, 3, 2, 1]

key参数

python2.4开始,list.sort()sorted()函数都增加了key参数来指定一个函数此函数将在每个元素比较前被调用。 默认值为None。例如通过key指定的函数来忽略字符串的大小写:

sorted("A d c E b".split(), key=str.lower)
['A', 'b', 'c', 'd', 'E']

key参数的值为一个函数,此函数只有一个参数且返回一个值用来进行比较。 这个技术是快速的因为key指定的函数将准确地对每个元素调用。

更广泛的使用情况是用复杂对象某些值来对复杂对象的序列排序,例如:

student_tuples = [
        ('john', 'A', 15),
        ('jane', 'B', 12),
        ('dave', 'B', 10),
]
sorted(student_tuples, key=lambda student: student[2])   # 根据年龄排序

# 输出结果
[('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) 

cmp参数与functools.cmp_to_key()函数

Python 2

Python2中,可以使用cmp参数。

cmp指定一个定制的比较函数,这个函数接收两个参数(iterable的元素),如果第一个参数小于第二个参数,返回一个负数;如果第一个参数等于第二个参数,返回零;如果第一个参数大于第二个参数,返回一个正数。cmp的默认值为None。

所以,如果想要升序排序,就要:
return 1 if val1 > val2 else -1或写成return val1 - val2
反之,如果想要降序排序,就要
return 1 if val1 < val2 else -1或写成return val2 - val1

注意,有没有发现上面的第一种跟其他语言中的不太一样,比如在C++中,一般写法是这样的:

bool cmp(int a, int b){
	return a > b; // 降序
	return a < b; // 升序
}

是不是直观感觉是反的,这是为什么呢?
这是因为cmp中定义的规则就相当于告诉sorted()如何对两个数进行比较,让它清楚什么样的条件是“大”什么样的条件是“小”
注意: 定义这个排序规则只是为了让sorted()明白如何得到两个数中较“大”的那个与数组最后是“从小到大”排序还是“从大到小”排序没有关系

此外,C++中是可以返回布尔值的,但Python中就不可以这样写,比如下面的写法就是错误的,会导致无效排序:

def cmp(a, b):
	return a > b

这里因为在Python中,True的值为1, 而False的值为0

举个例子:

# 字典按key降序排序,再按val升序排序
dic = {"a": 4, "c": 3, "b": 2, "d": 2}

def cmp_func(val1, val2):
	if val1[0] != val1[0]:
		return 1 if val1[0] < val2[0] else -1
    else:
        return 1 if val1[1] > val2[0] else -1

dic = sorted(dic.items(), cmp=cmp_func)
print(dic)

[('d', 2), ('c', 3), ('b', 2), ('a', 4)]

Python 3

Python3废弃了Python2 中sorted函数的cmp参数。
所以我们只能使用key参数进行排序,如果非要使用cmp函数进行自定义排序的话,可以借助functools模块中的cmp_to_key函数

# python3的sorted函数,没有cmp参数,只能通过cmp_to_key传给key,实现python2中sorted
dic = sorted(dic.items(), key=cmp_to_key(cmp_func))
print(dic)

[('d', 2), ('c', 3), ('b', 2), ('a', 4)]

Operator 模块函数

面的key参数的使用非常广泛,因此Python提供了一些方便的函数来使得访问方法更加容易和快速。operator模块itemgetterattrgetter,从2.6开始还增加了methodcaller方法。使用这些方法,上面的操作将变得更加简洁和快速:

from operator import itemgetter, attrgetter
student_tuples = [
        ('john', 'A', 15),
        ('jane', 'B', 12),
        ('dave', 'B', 10),
]
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)]

operator模块还允许多级的排序,例如,先以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)]

sort 与sorted区别

(1)sort() 是应用在 list上的方法,sorted() 可以对所有可迭代的对象进行排序操作。
(2)listsort() 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted()方法返回的是一个新的 list,而不是在原来的基础上进行的操作。


排序的稳定性

从Python2.2开始,排序被保证为稳定的。意思是说多个元素如果有相同的key,则排序前后他们的先后顺序不变

data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
sorted(data, key=lambda x: x[0])

[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]

Python:自定义比较运算符

1、object类中定义了__lt__()__le__()__eq__()__ne__()__gt__()__ge__()等方法,这组方法定义了对象之间可以使用<<==!=>>=比较结果,也就是python的Rich comprison方法(富比较或厚比较)。但由于object定义这些方法的局限性,实际使用时我们往往需要重新定义这些方法。例如:object定义的__eq__()is两比较两个对象是否同一对象,并不是实质的相等性。

2、__ne__()会默认调用__eq__(),并对结果求反,因此定义了__eq__()就相当于定义了 __ne__()

3、由于__lt__()__ge__()互补,__le__()__gt__()互补,故只需要定义__gt__()__ge__()

4、并不是每个对象都需要定义整组比较方法。如果真需要定义整组方法的行为,可以使用functoolstotal_ordering。当一个类被标注了@total_ordering时,必须实现__eq__(),并选择__lt__()__le__()__gt__()__ge__()其中一个方法实现

举例:
有一个Number类,它有两个属性:val1val2
需要按以下规则比较对象的大小:
(1)首先按照val2的值进行比较,val2的值越大,其对象大小越大;
(2)val2的值相同时,val1的值越大,其对象大小越大

#普通方法定义,定义eq、gt、ge
class Number:
    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2
    
    def __eq__(self, other):
        if self is other:
            return True
        if hasattr(other, 'val1') and hasattr(other, 'val2'):
            return self.val1 == other.val1 and self.val2 == other.val2
    
    def __gt__(self, other):
        if self.val2 == other.val2:
            return self.val1 > other.val1
        else:
            return self.val2 > other.val2
    
    def __ge__(self, other):
        if self.val2 == other.val2:
            return self.val1 >= other.val1
        else:
            return self.val2 > other.val2

number1 = Number(10, 99)
number2 = Number(10, 100)
number3 = Number(11, 99)
print(number1 > number2)
'False'
print(number3 > number1)
'True'
from functools import total_ordering

@total_ordering         #total_ordering定义
class Number:
    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2
    
    def __eq__(self, other):
        if self is other:
            return True
        if hasattr(other, 'val1') and hasattr(other, 'val2'):
            return self.val1 == other.val1 and self.val2 == other.val2
    
    def __gt__(self, other):
        if self.val2 == other.val2:
            return self.val1 > other.val1
        else:
            return self.val2 > other.val2

重新回到排序

所以,如果使用了自定义比较运算符,那么将对象排序时就无需再使用key参数cmp参数了。

num_list = []
num_list.append(Number(10, 99))
num_list.append(Number(6, 99))
num_list.append(Number(3, 100))
num_list.append(Number(6, 100))

num_list.sort() # 升序排序
for i in range(4):
    print(num_list[i].val1, num_list[i].val2)

# 输出
6 99
10 99
3 100
6 100
    
num_list.sort(reverse=True) # 降序排序
for i in range(4):
    print(num_list[i].val1, num_list[i].val2)
    
# 输出
6 100
3 100
10 99
6 99
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值