从列表中删除冗余元素的最佳方法是将其转换为一个集合,并且由于 sorted 接受任何可迭代并返回一个列表,这比分段执行要高效得多。
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
def sorted_set(a_list):
return sorted(set(a_list))
new_list = sorted_set(my_list)
new_list 是:
['a', 'b', 'c', 'd', 'e', 'f']
这种方法的缺点是给 set 的元素必须是可散列的,所以如果元素不可散列,你会得到一个错误:
>>> my_list = [['a'], ['a'], ['b'], ['c']]
>>> sorted(set(my_list))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
这种简单的情况可以通过将子列表转换为元组来解决,这可能比答案中的解决方案性能更高,这可能意味着更昂贵的相等性测试:
>>> my_list = [tuple(i) for i in my_list]
>>> sorted(set(my_list))
[('a',), ('b',), ('c',)]
但其他情况需要找到不同的解决方法。对于其他解决方案,这不是必需的(但同样,计算成本可能要高得多):
def remove_extras_and_sort(my_list):
for item in my_list[:]:
count = my_list.count(item)
if count > 1:
for _ in range(count-1):
my_list.remove(item)
my_list.sort()
return my_list
这适用于子列表:
>>> my_list = [['a'], ['a'], ['b'], ['c']]
>>> remove_extras_and_sort(my_list)
[['a'], ['b'], ['c']]
比较性能:
import timeit
setup = '''
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
def remove_extras_and_sort(my_list):
for item in my_list[:]:
count = my_list.count(item)
if count > 1:
for _ in range(count-1):
my_list.remove(item)
my_list.sort()
return my_list
def sorted_set(a_list):
return sorted(set(a_list))
'''
timeit.timeit('sorted_set(my_list[:])', setup=setup)
timeit.timeit('remove_extras_and_sort(my_list[:])', setup=setup)
当我在我的系统上分别测量它们时,它会返回时间:
1.5562372207641602
4.558010101318359
这意味着考虑到每次复制列表的必要开销(如果我们不复制列表,我们只会对一个已经排序的列表,因为设置只运行一次)。
我们可以反汇编每个函数:
import dis
def remove_extras_and_sort(my_list):
for item in my_list[:]:
count = my_list.count(item)
if count > 1:
for _ in range(count-1):
my_list.remove(item)
my_list.sort()
return my_list
def sorted_set(a_list):
return sorted(set(a_list))
并且仅仅通过查看输出,我们看到第一个函数的字节码长度是原来的六倍多:
>>> dis.dis(remove_extras_and_sort)
2 0 SETUP_LOOP 85 (to 88)
3 LOAD_FAST 0 (my_list)
6 SLICE+0
7 GET_ITER
>> 8 FOR_ITER 76 (to 87)
11 STORE_FAST 1 (item)
3 14 LOAD_FAST 0 (my_list)
17 LOAD_ATTR 0 (count)
20 LOAD_FAST 1 (item)
23 CALL_FUNCTION 1
26 STORE_FAST 2 (count)
4 29 LOAD_FAST 2 (count)
32 LOAD_CONST 1 (1)
35 COMPARE_OP 4 (>)
38 POP_JUMP_IF_FALSE 8
5 41 SETUP_LOOP 40 (to 84)
44 LOAD_GLOBAL 1 (range)
47 LOAD_FAST 2 (count)
50 LOAD_CONST 1 (1)
53 BINARY_SUBTRACT
54 CALL_FUNCTION 1
57 GET_ITER
>> 58 FOR_ITER 19 (to 80)
61 STORE_FAST 3 (_)
6 64 LOAD_FAST 0 (my_list)
67 LOAD_ATTR 2 (remove)
70 LOAD_FAST 1 (item)
73 CALL_FUNCTION 1
76 POP_TOP
77 JUMP_ABSOLUTE 58
>> 80 POP_BLOCK
81 JUMP_ABSOLUTE 8
>> 84 JUMP_ABSOLUTE 8
>> 87 POP_BLOCK
7 >> 88 LOAD_FAST 0 (my_list)
91 LOAD_ATTR 3 (sort)
94 CALL_FUNCTION 0
97 POP_TOP
8 98 LOAD_FAST 0 (my_list)
101 RETURN_VALUE
而且推荐的方式有更短的字节码:
>>> dis.dis(sorted_set)
2 0 LOAD_GLOBAL 0 (sorted)
3 LOAD_GLOBAL 1 (set)
6 LOAD_FAST 0 (a_list)
9 CALL_FUNCTION 1
12 CALL_FUNCTION 1
15 RETURN_VALUE
因此,我们看到使用 Python 的内置功能比尝试重新发明轮子更有效和高效。
附录:需要更充分探索的其他选项:
def groupby_sorted(my_list):
"""if items in my_list are unhashable"""
from itertools import groupby
return [e for e, g in groupby(sorted(my_list))]
def preserve_order_encountered(my_list):
"""elements in argument must be hashable - preserves order encountered"""
from collections import OrderedDict
return list(OrderedDict.fromkeys(my_list))