目录
- 参考文章
- sort函数
- 描述
- 语法
- sorted函数
- 描述
- 语法
- 升序与降序
- key参数
- cmp参数与functools.cmp_to_key()函数
- Python 2
- Python 3
- Operator 模块函数
- sort 与sorted区别
- 排序的稳定性
- Python:自定义比较运算符
- 重新回到排序
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):
= name
self.grade = grade
self.age = age
def __repr__(self):
return repr((, 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模块有itemgetter
,attrgetter
,从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)list
的 sort()
方法返回的是对已经存在的列表进行操作,无返回值
,而内建函数 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、并不是每个对象都需要定义整组比较方法。如果真需要定义整组方法的行为,可以使用functools
的total_ordering
。当一个类被标注了@total_ordering
时,必须实现__eq__()
,并选择__lt__()
、__le__()
、__gt__()
、__ge__()
其中一个方法实现
举例:
有一个Number类,它有两个属性:val1
和val2
需要按以下规则比较对象的大小:
(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