`sorted(list)` 和 `list.sort()` 有什么区别?

293

list.sort()排序并替换原列表,而sorted(list)返回一个已排序的列表副本,不会更改原始列表。

  • 什么情况下优先使用其中之一?
  • 哪个更有效?效果如何?
  • list.sort()执行后,列表是否可以恢复到未排序状态?

请使用Why do these list operations (methods) return None, rather than the resulting list?关闭那些错误地将结果赋给.sort()而不是使用sorted或另外一个语句的问题。正确的调试会显示.sort()返回了None,此时“为什么?”就是需要解答的问题。


14
如果你(无意中)在字符串参数上调用sorted()但认为它是一个列表,请注意,你会得到一个列表结果,而不是一个字符串。例如,sorted("abcd", reverse=True)会得到['d', 'c', 'b', 'a']而不是"dcba" - smci
寻找重复问题的注意事项:许多与list.sort()返回None而不是新list相关的问题都被归为了这里,但它们最好被归为更具体的问题为什么“return list.sort()”返回None而不是列表? - ShadowRanger
7个回答

432

sorted() 返回一个新的已排序列表,不影响原始列表。 list.sort() 对列表进行就地排序,改变列表索引,并返回None(与所有就地操作一样)。

sorted() 可以用于任何可迭代对象,而不仅仅是列表。字符串、元组、字典(将返回键)、生成器等都可以使用这个函数,返回包含所有元素的已排序列表。

  • 当您想要更改列表时,请使用list.sort(),当您想要返回新的已排序对象时,请使用sorted()。当您想要对一个可迭代对象进行排序时,请使用sorted(),而不是列表(暂时)。

  • 对于列表,list.sort()sorted() 更快,因为它不需要创建一个副本。对于任何其他可迭代对象,您别无选择。

  • 不,您无法恢复原始位置。一旦调用了list.sort(),原始顺序将消失。


15
通常情况下,当一个Python函数返回None时,表示操作是就地(in place)进行的。这就是为什么当您想要打印 list.sort()时会返回None的原因。请注意,这里的“就地”指的是在原列表上进行,并非创建一个新列表。 - user1767754

67

sorted(list)list.sort()的区别是什么?

  • list.sort会就地修改列表并返回None
  • sorted接受任何可迭代对象并返回一个已排序的新列表。

sorted相当于这个Python实现,但CPython内置函数应该会运行得更快,因为它是用C编写的:

def sorted(iterable, key=None):
    new_list = list(iterable)    # make a new list
    new_list.sort(key=key)       # sort it
    return new_list              # return it

何时使用哪种方法?

  • 当您不希望保留原始排序顺序(因此您将能够在内存中就地重用列表)且您是该列表的唯一所有者时,请使用list.sort(如果列表由其他代码共享并且您对其进行了更改,则可能会引入使用该列表的错误。)
  • 当您想要保留原始排序顺序或者想要创建一个仅由本地代码拥有的新列表时,请使用sorted

在 list.sort() 后是否可以检索到列表的原始位置?

不行-除非您自己复制了该信息,否则该信息将因原地排序而丢失。

"哪个更快?速度有多快?"

为了说明创建新列表的惩罚,使用timeit模块,这是我们的设置:

import timeit
setup = """
import random
lists = [list(range(10000)) for _ in range(1000)]  # list of lists
for l in lists:
    random.shuffle(l) # shuffle each list
shuffled_iter = iter(lists) # wrap as iterator so next() yields one at a time
"""

以下是我们对一个随机排列的10000个整数列表进行测试的结果,如您所见,我们已经证明了一个旧的列表创建费用谬论是错误的:

Python 2.7

>>> timeit.repeat("next(shuffled_iter).sort()", setup=setup, number = 1000)
[3.75168503401801, 3.7473005310166627, 3.753129180986434]
>>> timeit.repeat("sorted(next(shuffled_iter))", setup=setup, number = 1000)
[3.702025591977872, 3.709248117986135, 3.71071034099441]

Python 3

>>> timeit.repeat("next(shuffled_iter).sort()", setup=setup, number = 1000)
[2.797430992126465, 2.796825885772705, 2.7744789123535156]
>>> timeit.repeat("sorted(next(shuffled_iter))", setup=setup, number = 1000)
[2.675589084625244, 2.8019039630889893, 2.849375009536743]

经过一些反馈,我决定进行另一个测试,具有不同的特征。在这里,我为每次迭代提供了相同的长度为100,000的随机排序列表,共进行了1,000次。

import timeit
setup = """
import random
random.seed(0)
lst = list(range(100000))
random.shuffle(lst)
"""

我认为这种更大的排序方式的差异来自Martijn提到的复制,但它并没有像旧的、更流行的答案中所述那样占据主导地位,在这里,时间增加只有约10%。
>>> timeit.repeat("lst[:].sort()", setup=setup, number = 10000)
[572.919036605, 573.1384446719999, 568.5923951]
>>> timeit.repeat("sorted(lst[:])", setup=setup, number = 10000)
[647.0584738299999, 653.4040515829997, 657.9457361929999]

我还在更小的排序上运行了上述代码,并发现新的sorted副本版本在长度为1000的排序中仍需要大约2%的更长运行时间。

Poke也运行了自己的代码,以下是代码:

setup = '''
import random
random.seed(12122353453462456)
lst = list(range({length}))
random.shuffle(lst)
lists = [lst[:] for _ in range({repeats})]
it = iter(lists)
'''
t1 = 'l = next(it); l.sort()'
t2 = 'l = next(it); sorted(l)'
length = 10 ** 7
repeats = 10 ** 2
print(length, repeats)
for t in t1, t2:
    print(t)
    print(timeit(t, setup=setup.format(length=length, repeats=repeats), number=repeats))

他进行了1000000长度排序(运行了100次),结果相似,但时间只增加了约5%,以下是输出结果:
10000000 100
l = next(it); l.sort()
610.5015971539542
l = next(it); sorted(l)
646.7786222379655

结论:

使用 sorted 对大型列表进行排序并复制会占据主导地位,但是排序本身才是操作的主导因素,围绕这些差异组织代码将是过早的优化。当我需要一个新的已排序数据列表时,我会使用 sorted,而当我需要原地排序列表时,我会使用 list.sort,并让它决定我的用法。


6
发电机的设置很好,但我不会轻易得出你打破了一个神话的结论。事实仍然是sorted()必须分配一个新的列表对象并复制引用;其余的代码路径是相同的。试着用更大的列表运行相同的测试。将其与仅创建列表副本进行比较,并查看是否可以复制你发现的差异等。 - Martijn Pieters

16
主要区别在于sorted(some_list)返回一个新的list
a = [3, 2, 1]
print sorted(a) # new list
print a         # is not modified

some_list.sort()原地对列表进行排序:

a = [3, 2, 1]
print a.sort() # in place
print a         # it's modified

请注意,由于a.sort()不返回任何内容,因此print a.sort()会打印None


在使用list.sort()方法后还能够获取列表原始位置吗?

不能,因为它修改了原始列表。


2
print a.sort() 不会打印任何东西。 - Burhan Khalid
1
它将打印“None”,我会澄清一下。 - Christian Tapia

5
以下是一些简单的示例,以了解它们的区别:
请查看此处的数字列表:
nums = [1, 9, -3, 4, 8, 5, 7, 14]

调用sorted函数时,它将对该列表进行复制。(也就是说,原始列表将保持不变。)让我们看看。
sorted(nums)

返回值

[-3, 1, 4, 5, 7, 8, 9, 14]

再次查看 nums

nums

我们看到原始列表(未更改且未排序)。`sorted`没有改变原始列表。
[1, 2, -3, 4, 8, 5, 7, 14]

应用sort函数对相同的nums列表进行操作,将改变实际列表。
让我们来看看。
从我们的nums列表开始,以确保内容仍然相同。
nums

[-3, 1, 4, 5, 7, 8, 9, 14]

nums.sort()

现在,原始的nums列表已经发生了改变。通过查看nums列表,我们可以看到我们的原始列表已经被排序。

nums
[-3, 1, 2, 4, 5, 7, 8, 14]

1
感谢您更深入地展示原始版本与副本。 - Brendan Metcalfe

5

注意:sort()和sorted()之间最简单的区别是:sort()不返回任何值,而sorted()返回一个可迭代的列表。

sort()不返回任何值。

sort()方法只是按照指定的顺序(升序或降序)对给定列表的元素进行排序,而不返回任何值。

sort()方法的语法如下:

list.sort(key=..., reverse=...)

或者,你也可以使用Python内置的sorted()函数来实现相同的目的。sorted()函数返回排序后的列表。

 list=sorted(list, key=..., reverse=...)

1
列表是一种类型,这可能会让人感到困惑,列表在你的示例中应该有另一个名称(例如,my_list)。 - Jean Bouvattier

1

使用 list.sort() 可以改变列表变量,但使用 sorted(list) 不会改变变量本身。

使用 sort:

list = [4, 5, 20, 1, 3, 2]
list.sort()
print(list)
print(type(list))
print(type(list.sort())

应该返回这个:
[1, 2, 3, 4, 5, 20]
<class 'NoneType'>

但是使用sorted():

list = [4, 5, 20, 1, 3, 2]
print(sorted(list))
print(list)
print(type(sorted(list)))

应该返回这个:
[1, 2, 3, 4, 5, 20]
[4, 5, 20, 1, 3, 2]
<class 'list'>

1
.sort()函数直接将新列表的值存储在列表变量中,因此您第三个问题的答案是NO。如果使用sorted(list)进行操作,则可以使用它,因为它未存储在列表变量中。有时.sort()方法作为函数或者说它在其中带有参数。您必须显式地将sorted(list)的值存储在一个变量中。对于短数据处理,速度没有区别;但对于长列表,您应该直接使用.sort()方法以快速工作;但再次面临不可逆转的操作。

“.sort()”函数会直接将新列表的值存储在列表变量中。什么新列表?根本没有新列表。list.sort()方法会就地对列表对象进行排序。 - PM 2Ring
还有,这句话是什么意思呢?“有时.sort()方法充当函数,或者说它带有参数。” - PM 2Ring
我所指的新列表是修改后的列表,而 .sort() 只是将该修改后的列表存储到同一变量中。 - Vicrobot
是的,.sort() 方法有时需要参数并且像函数一样使用。我们也称其为方法,因为它是列表数据类型的属性。 - Vicrobot
如果我的概念有任何错误,请告诉我,我会查找并改进我的概念和答案。谢谢。 - Vicrobot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接