为什么list.sort()不起作用,但sorted()在这种情况下对于给定列表工作正常

时间:2018-05-16 09:03:54

标签: python list

我试图根据list2 = ['four','six','two','three','one','five']list1 = [4,6,2,3,1,5]进行排序,以便我的代码更新list2
['one', 'two', 'three', 'four', 'five', 'six']

我尝试了以下代码:

list2.sort(key = lambda x: list1[list2.index(x)])

但它会出现以下错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
ValueError: 'four' is not in list

但是,使用sorted()函数执行相同操作并将list2更新为已排序列表可提供所需的结果。为什么呢?

2 个答案:

答案 0 :(得分:1)

为什么list.sort失败?

list.sort期间,实例的行为就像它不包含任何内容一样。这意味着您不应在key函数中使用该列表实例。

您可以使用以下脚本验证:

a = [3,2,1]

def printlist(key):
    print(a)
    return key

print('before sorting', a)
a.sort(key=printlist)
print('after sorting', a)

打印哪些:

before sorting [3, 2, 1]
[]
[]
[]
after sorting [1, 2, 3]

在CPython中可以看到相应的代码here。我在这里复制了该部分之前的评论,因为它包含了这个理由:

/* The list is temporarily made empty, so that mutations performed
 * by comparison functions can't affect the slice of memory we're
 * sorting (allowing mutations during sorting is a core-dump
 * factory, since ob_item may change).
 */

基本上它表示列表中的任何突变都可能导致段错误,并且因为key函数可以执行任意代码,列表将被清空。

这也是直接修改导致错误的原因:

a = [3,2,1]
def appendelement(item):
    a.append(item)
    return item
a.sort(key=appendelement)
  

ValueError:在排序

期间修改的列表

为什么它适用于排序?

sorted只能解决该问题,因为它会立即复制列表(related CPython code),因此“副本”会被排序。这意味着原始的list2仍然完整无缺,可以使用。

更好的方法

请注意,因为list.index你的函数O(n)基本上变成N ** 2而不是N * log(N)。

Python wiki包含一个关于decorate-sort-undecorate方法的部分,通常用于这些情况:

list2 = ['four','six','two','three','one','five']
list1 = [4,6,2,3,1,5]

decorated = [(num, i, name) for i, (num, name) in enumerate(zip(list1, list2))]
decorated.sort()
undecorated = [name for _, _, name in decorated]

print(undecorated)
# ['one', 'two', 'three', 'four', 'five', 'six']

来自i的{​​{1}}主要用于避免每次比较名称 - 因为enumerate总是不同的。在这种情况下可以省略它。

另一种替代方法

此处您还有一个独特的映射,因此您也可以使用翻译词典:

i

答案 1 :(得分:0)

使用标准库zip函数逐个元素地组合两个列表。对组合列表进行排序,这将有效地导致第一个列表成为排序字段。然后理解恢复现在排序的list2。

list1 = [4,6,2,3,1,5]
list2 = ['four','six','two','three','one','five']

list2 = [a[1] for a in sorted(zip(list1, list2))]
print(list2)

>>> ['one', 'two', 'three', 'four', 'five', 'six']