在计算机科学中, 排序算法 列表 中的元素 按特定 order 。它们很重要,因为它们通常可以降低问题的复杂性。让我们来了解如何使用自定义排序函数在 Python 中实现自定义顺序和比较。
在我之前关于
在 Python 中使用流的
和
list.sort()
的排序方法
sorted()
。
list.sort()
和
sorted()
都有一个关键参数,该参数指定在进行比较之前在每个列表元素上调用一个函数。
在本文中,我想进一步讨论排序主题,并探索如何在 Python 中编写自定义排序函数。换句话说,我将解释如何使用自定义 lambda 函数作为关键参数。
那么在深入阅读本文之前, 最好先阅读 如何在 Python 中定义函数
使用 Python 中的自定义排序函数进行排序
和
sort()
之间的区别
sorted()
。从语法上讲,
sort()
是实例方法实现为
list_to_sort.sort()
,而 则
sorted()
用作
sorted(list_to_sort)
.
需要注意的一件重要事情是
sort()
直接修改初始变量,因此初始顺序将会丢失。
另一方面,
sorted()
保留初始变量的副本,这样就可以在需要时恢复到初始顺序。 由于
sort()
不复制任何初始变量,因此它比 更高效
sorted()
。 但是,这是以牺牲便利性为代价的。
还需要注意的是
sorted()
将返回一个列表;因此,您必须将输出分配给一个新变量。
至于
list.sort()
,它会就地修改列表,并且没有返回值。最后但同样重要的是,
list.sort()
只能对列表起作用,而
sorted()
接受任何可迭代对象。
例如,这是一个不区分大小写的字符串比较:
>>> sorted(" is awesome to learn about custom sort functions in Python".split(), key=str.lower)
['about', 'awesome', 'custom', 'functions', 'in', 'is'
'Learn', '', 'Python', 'sort', 'to']
注意: 在 Python 中,通常将自定义 lambda 函数作为关键参数传递,以对复杂对象进行排序。
现在,让我们讨论一下 Python 中的自定义排序函数。在 Python 中,我们可以编写与
sort()
和
sorted()
.
key 参数的值应为一个函数,该函数接受单个参数并返回一个
key
用于排序的函数。由于 key 函数对于每个输入记录仅调用一次,因此这是在 Python 中执行排序的有效方法。
一种常见的模式是使用对象的一些索引对复杂对象进行排序
key
。例如,我们可以定义自定义顺序来对元组列表进行排序:
>>> pokemon = [
... ('Charmander', 'Fire', 52),
... ('Blastoise', 'Water', 83),
... ('Beedrill', 'Poison', 90),
... ]
>>> sorted(pokemon, key=lambda x: x[2]) # sort by attack power
[('Charmander', 'Fire', 52),
('Blastoise', 'Water', 83),
('Beedrill', 'Poison', 90)]
它也适用于具有名称属性的对象:
>>> class Pokemon:
... def __init__(self, name, category, attack):
... self.name = name
... self.category = category
... self.attack = attack
... def __repr__(self):
... return repr((self.name, self.category, self.attack))
>>> pokemon_objects = [
... Pokemon('Beedrill', 'Poison', 90),
... Pokemon('Charmander', 'Fire', 52),
... Pokemon('Blastoise', 'Water', 83),
... ]
>>> sorted(pokemon_objects, key=lambda x: x.attack) # sort by attack
[('Charmander', 'Fire', 52),
('Blastoise', 'Water', 83),
('Beedrill', 'Poison', 90)]
您可以在文章《 在 Python 中创建自己的类的简单步骤》 .
了解如何操作数据、用 Python 编写自定义排序函数以及执行自定义比较是必须掌握的技能。我们的“ 数据科学 Python 入门” 是掌握这一热门技能的绝佳方式。
使用 Python 中的排序函数进行自定义比较
您还可以使用
sorted()
自定义比较器作为其参数。
在Python 2中,
sorted()
或
cmp
来实现
key
。
需要注意的是,
cmp
需要传递两个参数(x 和 y),它们是列表的一部分。它将返回一个数字,逻辑如下:
- 如果返回正数:x > y
- 如果返回 0: x == y
- 如果返回负数:x < y
但是,
key
接收一个参数,计算结果,然后利用计算结果进行排序和比较。这意味着在 Python 2 中,您可以用两种不同的方式按立方值对数字列表进行排序:
>>> l = [6, 8, 10, 23, -4, -7]
>>> # The cmp parameter has been removed in Python 3
>>> sorted_l = sorted(l, cmp=lambda x, y: x ** 3 - y ** 3) # Sort with cmp
>>> sorted_l = sorted(l, key=lambda x: x ** 3) # Sort with key
>>> print(sorted_l)
[-7, -4, 6, 8, 10, 23]
在Python 3中,该
cmp
参数已被删除,主要有两个原因。
首先,用 完成的所有事情
cmp
都可以用 完成
key
。其次,
key
它比 更快
cmp
。当
cmp
作为参数传递时,排序算法会比较成对的值,并且对每个项目调用多次比较函数。
另一方面,key 只执行一次计算。因此,复杂性降低了。由于语法简化,这使得代码不易出错。(在 key 之前,可以通过遵循 Decorate-Sort-Undecorate 原则(也称为 Schwartzian 变换 。)
如果你熟悉 Java 或 C++,你可能对
cmp
比
key
。事实上,在 Python 3 中,你可以使用
cmp
with
func为ols.cmp_to_key(func)
,它将转换
cmp
to
key
。让我们在下一节中进一步探讨这一点。
在 Python 中自定义排序函数 functools.cmp_to_key(func)
functools.cmp_to_key(func) 用于将旧式比较函数转换为键函数。它在 Python 2.7、Python 3.2 及更高版本中可用。
根据
Python 3 文档
,“比较函数是任何可调用函数,它接受两个参数,对它们进行比较,如果小于则返回负数,如果相等则返回零,如果大于则返回正数。函数
key
是可调用函数,它接受一个参数并返回另一个值以用于排序
key
。”
在 Python 2.4 之前,没有
sorted()
,
list.sort()
也不接受关键字参数。相反,Python 2 支持一个
cmp
参数来处理用户指定的比较函数。
当将代码从 Python 2 移植到 Python 3 时,您可能需要将函数从 转换
cmp
为
key
。在 Python 3 中,
functools.cmp_to_key(func)
引入了 以简化该过程。
我们将使用
functools.cmp_to_key(func)
接受关键函数(例如
s或 )ted()
or
itertools.groupby()
,我在之前的文章中讨论过这些函数。使用我们之前的示例按立方值对数字进行排序,您可以编写自定义
cmp
函数,如下所示:
>>> import functools
>>> l = [6, 8, 10, 23, -4, -7]
>>> def compare(x, y):
... return x ** 3 - y ** 3
>>> sorted_l = sorted(l, key=functools.cmp_to_key(compare))
>>> print(sorted_l)
[-7, -4, 6, 8, 10, 23]
有时,使用 key 可能不如 明显
cmp
。在这种情况下,使用 可能更好
functools.cmp_to_key(func)
,因为它更具可读性和直观性。
例如,在去年的 matura (类似于 A Levels、Abitur 或 Baccalauréat 的波兰考试)中,可选的 IT 部分有一项练习,其中包括:
满足 以下 对 (number1, word1) 小于 对 (number2, word2) :
- 数字 1 < 数字 2
或者:
- number1 == number2 并且 word1 按字母顺序小于 word2 .
例如, 对 (1, bbbb) 小于 对 (2, aaa) ,但 对 (3, aaa) 小于对 (3, ab) .
换句话说,我们希望该对按第一个元素和第二个元素的升序排序。
因此,我们期望按以下顺序返回这些对: (1,bbbb),(2,aaa),(3,aaa),(3,ab)。
下面是
cmp
解决此问题的自定义函数:
from functools import cmp_to_key
def compare(pair1, pair2):
number1, word1 = pair1
number2, word2 = pair2
if number1 == number2:
if word1 < word2:
return -1
else:
return 1
if number1 < number2:
return -1
else:
return 1
compare_key = cmp_to_key(compare)
通过对元组列表进行排序
key
来解决问题
>>> # List of tuples
>>> l = [(3, 'aaa'), (1, 'bbbb'), (3, 'ab'), (2, 'aaa')]
>>> # Sort with key on first and second element of each tuple
>>> sorted(l, key = lambda x: (x[0], x[1]))
[(1, 'bbbb'), (2, 'aaa'), (3, 'aaa'), (3, 'ab')]
我们还可以尝试通过按降序对第一个元素进行排序,按升序对第二个元素进行排序,使问题更具挑战性。同样,我们可以用以下方法解决它
key
:
>>> # Sort number in descending order and word in ascending order
>>> sorted(l, key = lambda x: (-x[0], x[1]))
[(3, 'aaa'), (3, 'ab'), (2, 'aaa'), (1, 'bbbb')]
假设我们把问题反过来,第一个元素按升序排列,第二个元素按降序排列。在这种情况下,传递参数
reverse
as
True
就可以解决问题。
>>> # Sort number in ascending order and word in descending order
>>> sorted(l, key = lambda x: (-x[0], x[1]), reverse=True)
[(1, 'bbbb'), (2, 'aaa'), (3, 'ab'), (3, 'aaa')]
很难找到
cmp
不能被 替代的
key
。由于性能方面
functools.cmp_to_key(func)
与 相比非常慢
key
,因此它只能作为在 Python 中实现自定义排序函数的最后手段。
如果您想了解有关映射函数的更多信息,请查看我关于 filter()、map() 和 reduce() .
关于 Python 中自定义排序函数的总结
在本文中,我们探讨了如何在 Python 中实现自定义排序和比较函数。我们了解了一些 Python 历史,并尝试了解
cmp
在 Python 2 和 3 之间使用和键做出的选择,以便在 Python 中实现自定义排序函数。
为了更好地理解这些文章中解释的概念,使用代码片段并构建自己的示例总是一个好主意。
最后,如果你想了解有关 Python 中数据操作的更多信息,请随时查看 Yigit 的精彩文章,了解 如何使用 Pandas 数据框中 .
如果您想更上一层楼,请尝试我们的 Python 数据科学课程 。祝您学习愉快!
发表评论 取消回复