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]

这里解释一下,我传递内置函数abskey参数,那么sorted就会用原元素的绝对值来排序,比如7的绝对值是7-10的绝对值是10… 3的绝对值是3sorted函数在内部比较的时候用的是这些原元素的绝对值来做比较操作的。-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参数可以传入类,因为在pythonclass也是可调用对象,但是在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)]

解释一下这里的排序结果,我们可以看到这个排序不是按照成绩的高低来排序的,因为结果是9288667758,这个成绩的顺序既不是升序,也不是降序。此处的排序其实是直接拿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

这一版本的代码是最简单的代码,可以看到我们不实现keyreverse参数,这里使用了非就地排序,开辟新的数组,然后使用类似插入排序的思想一个个插入元素,然后返回该排好序的数组。当然了,这不是重点。还是稍微解释一下排序算法,针对每一个待排序列表中的元素(item),把这个元素和已排序部分进行比较,直到找到这个元素在已排序部分中的位置,最后把这个元素插入到找到的位置中,具体看上面的代码。

 

接下来是第二版,实现reverse参数:

def my_sorted2(iterable, *, key=None, reverse=False):  # reverseFalse表示升序,反之降序
   
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):  # reverseFalse表示升序,反之降序
   
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]),就会使用排序规则,如果keyNone,那么表达式就是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

评论

此博客中的热门博文

OAuth 2教程

网格策略

apt-get详细使用