Python的内置函数sorted,从入门到放弃
Python内置函数sorted
Python内置函数sorted用法1:基本用法
最简单的用法:sorted函数默认是升序
import random
lst = [random.randint(-10, 10) for _ in range(8)]
print(lst)
print(sorted(lst))
结果如下:
[-5, 9, -7, -7, 4, -5, -6, 6]
[-7, -7, -6, -5, -5, 4, 6, 9]
sorted函数支持reverse参数,该参数默认值是False,表示升序,调用时传入True,表示降序:
import random
lst = [random.randint(-10, 10) for _ in range(8)]
print(lst)
print(sorted(lst,reverse=True))
结果如下:
[-8, 7, -10, 8, 6, 2, 10, -9]
[10, 8, 7, 6, 2, -8, -9, -10]
sorted函数支持key参数,key参数可以设置排序的规则,通常可以传入可调用对象,python中的可调用对象有很多,常用的有函数,lambda,类等等:
import random
lst = [random.randint(-10, 10) for _ in range(8)]
print(lst)
print(sorted(lst, key=abs))
结果如下:
[7, -10, 1, -4, 4, 8, 5, 3]
[1, 3, -4, 4, 5, 7, 8, -10]
这里解释一下,我传递内置函数abs给key参数,那么sorted就会用原元素的绝对值来排序,比如7的绝对值是7,-10的绝对值是10,… 3的绝对值是3,sorted函数在内部比较的时候用的是这些原元素的绝对值来做比较操作的。-10的绝对值在此例是最大的,所有排在最右边(默认是升序,我没有改变reverse参数)。
在看一个例子:用原元素的绝对值排序,而且是降序
import random
lst = [random.randint(-10, 10) for _ in range(8)]
print(lst)
print(sorted(lst, key=abs, reverse=True))
结果如下:
[1, 9, 9, 5, -7, 1, 2, -7]
[9, 9, -7, -7, 5, 2, 1, 1]
Python内置函数sorted用法2:对元祖,自定义类等排序
假设我们有王小明同学的各个科目的个人成绩,数学成绩88分,程序设计70分,英文92分,操作系统66分,web开发58分。现在想要给这个同学按照成绩分数从小大排序?
grade = [('math', 88), ('programming', 77), ('english', 92), ('operating system', 66), ('web develop', 58)]
print(sorted(grade,key=lambda x: x[1]))
结果如下:
[('web develop', 58), ('operating system',
66), ('programming', 77), ('math', 88), ('english', 92)]
解释一下,此处key传入了一个lambda匿名函数,前面已经提过了,key用于设置排序规则,这里的key=lambda
x: x[1]表示要用原元素的索引为1的项来排序。
当然了,如果对于python的匿名函数不熟悉,也可以定义普通函数,下面的例子和上面的一样,但是是普通函数的版本:
def my_sort_rule(item):
return item[1]
grade = [('math', 88), ('programming', 77), ('english', 92), ('operating system', 66), ('web develop', 58)]
print(sorted(grade,key=my_sort_rule,reverse=True))
结果如下:
[('english', 92), ('math', 88),
('programming', 77), ('operating system', 66), ('web develop', 58)]
也可以使用operator模块来生成传给key参数的函数:
import operator
grade = [('math', 88), ('programming', 77), ('english', 92), ('operating system', 66), ('web develop', 58)]
print(sorted(grade,key=operator.itemgetter(1),reverse=True))
运行结果:
[('english', 92), ('math', 88),
('programming', 77), ('operating system', 66), ('web develop', 58)]
前面提到过,key参数可以传入类,因为在python中class也是可调用对象,但是在sorted内部有比较操作,所以必须实现__lt__魔术方法,看下面的例子:
class MySortRule:
def __init__(self, obj):
self.obj = obj
def __lt__(self, other):
return True if self.obj[1] < other.obj[1] else False
grade = [('math', 88), ('programming', 77), ('english', 92), ('operating system', 66), ('web develop', 58)]
print(sorted(grade, key=MySortRule))
运行结果如下:
[('web develop', 58), ('operating system',
66), ('programming', 77), ('math', 88), ('english', 92)]
学习过java的应该知道,java的排序和python的不一样,通常javaer是实现一个cmp比较函数:可能是下面这样:
def cmp(obj1, obj2):
if obj1 > obj2: return 1 #左>右,返回大于0的数
if obj1 < obj2: return -1 #左<右,返回小于0的数
return 0 #相等返回0
python通过functools模块的cmp_to_key函数把这种cmp函数改造成符合pythonic
看下面的例子:
def cmp(obj1, obj2):
if obj1 > obj2: return 1
if obj1 < obj2: return -1
return 0
import functools
grade = [('math', 88), ('programming', 77), ('english', 92), ('operating system', 66), ('web develop', 58)]
print(sorted(grade, key=functools.cmp_to_key(cmp)))
运行结果:
[('english', 92), ('math', 88), ('operating
system', 66), ('programming', 77), ('web develop', 58)]
解释一下这里的排序结果,我们可以看到这个排序不是按照成绩的高低来排序的,因为结果是92,88,66,77,58,这个成绩的顺序既不是升序,也不是降序。此处的排序其实是直接拿grade列表里的元组出来排序的,元组排序的话,从元组第0项开始比较,english < math < operating
system < programming < web develop,如果第0项相等,就依次比较第1项,等等,如此下去 …
解释一下:下面是cmp_to_key的具体实现:
def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
__slots__ = ['obj']
def __init__(self, obj):
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
__hash__ = None
return K
解释一下:
print(sorted(grade, key=functools.cmp_to_key(cmp)))
这句代码被调用的时候,functools.cmp_to_key(cmp)会返回cmp_to_key函数内部定义的类K,该类实现了比较魔术方法,是的,非常齐全,分析其中的__lt__魔术方法,该方法借助传入的cmp函数实现比较操作。
按照科目名称排序意义不大,如果我要和之前一样按照成绩排序,要怎么办?
看代码吧:
def cmp(obj1, obj2):
if obj1[1] > obj2[1]: return 1
if obj1[1] < obj2[1]: return -1
return 0
import functools
grade = [('math', 88), ('programming', 77), ('english', 92), ('operating system', 66), ('web develop', 58)]
print(sorted(grade, key=functools.cmp_to_key(cmp), reverse=True))
运行结果如下:
[('english', 92), ('math', 88),
('programming', 77), ('operating system', 66), ('web develop', 58)]
你已经懂了很多了,接下来自己来实现一个sorted函数吧,加深理解。
不要继续往下看了,实现好你的sorted函数之后,再往下看。
实现自己的sorted函数:
直接给代码:第一版
def my_sorted(iterable, *, key=None, reverse=False):
newlist = []
for item in iterable: # 遍历待排序列表
j = len(newlist) - 1
while j >= 0 and newlist[j] > item: # 找到
j -= 1
newlist.insert(j + 1, item)
return newlist
这一版本的代码是最简单的代码,可以看到我们不实现key和reverse参数,这里使用了非就地排序,开辟新的数组,然后使用类似插入排序的思想一个个插入元素,然后返回该排好序的数组。当然了,这不是重点。还是稍微解释一下排序算法,针对每一个待排序列表中的元素(item),把这个元素和已排序部分进行比较,直到找到这个元素在已排序部分中的位置,最后把这个元素插入到找到的位置中,具体看上面的代码。
接下来是第二版,实现reverse参数:
def my_sorted2(iterable, *, key=None, reverse=False): # reverse为False表示升序,反之降序
newlist = []
for item in iterable: # 遍历待排序列表
j = len(newlist) - 1
while j >= 0 and (newlist[j] > item if reverse == False else newlist[j] < item): # 找到待排序元素的位置
j -= 1
newlist.insert(j + 1, item)
return newlist
可以看到reverse==False就是参数默认值,此时,比较条件和第一版是一样的,相反的,如果reverse==True,那么比较条件就会变成newlist[j] < item,这样就可以降序了。是不是很简单
最后是第三版,实现key参数:也是最后一版,最完整的版本
def my_sorted3(iterable, *, key=None, reverse=False): # reverse为False表示升序,反之降序
result = []
for item in iterable: # 遍历待排序列表
j = len(result) - 1
while j >= 0 and (
(key(result[j]) if key else result[j]) > (key(item) if key else item) if reverse == False else (
(key(result[j]) if key else result[j]) < (key(item) if key else item))
): # 找到待排序元素的位置
j -= 1
result.insert(j + 1, item)
return result
解释一下这个:(key(result[j]) if key
else result[j])
首先是判断key参数是否为None,如果key不是None,那么表达式结果就是key(result[j]),就会使用排序规则,如果key是None,那么表达式就是result[j],也就不使用排序规则。其他的都是一样的道理,读者自己理解。
第4版代码如下:为何会有第4版,因为第3版的while条件的可读性太差了,我们可以剥离排序逻辑,看代码:
def my_sorted4(iterable, *, key=None, reverse=False):
result = []
# 定义比较函数,根据reverse参数决定使用大于还是小于
def compare(a, b):
if reverse:
return a < b
else:
return a > b
for item in iterable:
j = len(result) - 1
item_key = key(item) if key else item # 提取或计算item的键值
# 找到插入位置
while j >= 0:
result_key = key(result[j]) if key else result[j] # 提取或计算result[j]的键值
if compare(result_key, item_key):
j -= 1
else:
break
# 插入元素
result.insert(j + 1, item)
return result
评论
发表评论