检查列表中的所有元素是否相同
我需要以下function:
input :一个list
输出 :
- 如果input列表中的所有元素使用标准相等运算符评估为相等,则为
True
; - 否则为
False
。
性能 :当然,我宁愿不招致任何不必要的开销。
我觉得最好是:
- 遍历列表
- 比较相邻的元素
- 和所有的结果布尔值
但我不确定什么是最Python的方式来做到这一点。
编辑 :
谢谢你所有的好的答案。 我评价了几个,在@KennyTM和@Ivan van der Wijk解决scheme之间select真的很困难。
短路特性的缺乏只会影响早期具有不相等元素的长input(超过50个元素)。 如果这种情况经常发生(多长时间取决于列表可能会多长时间),则需要进行短路。 最好的短路algorithm似乎是@KennyTM checkEqual1
。 但是,这付出了巨大的代价:
- 性能几乎相同的列表中高达20倍
- 在短名单上的performance高达2.5倍
如果具有早期不相等元素的长input不会发生(或者很less发生),则不需要短路。 那么,迄今为止最快的是@Ivo van der Wijk解决scheme。
一般方法:
def checkEqual1(iterator): iterator = iter(iterator) try: first = next(iterator) except StopIteration: return True return all(first == rest for rest in iterator)
一内胆:
def checkEqual2(iterator): return len(set(iterator)) <= 1
另外一行:
def checkEqual3(lst): return lst[1:] == lst[:-1]
这三个版本的区别在于:
- 在
checkEqual2
,内容必须是可散列的。 -
checkEqual1
和checkEqual2
可以使用任何迭代器,但checkEqual3
必须采用序列input,通常是像列表或元组这样的具体容器。 - 一旦发现差异,
checkEqual1
停止。 - 由于
checkEqual1
包含更多的Python代码,因此在开始时许多项目相等时效率较低。 - 由于
checkEqual2
和checkEqual3
总是执行O(N)复制操作,如果大部分input将返回False,则它们将花费更长的时间。 -
checkEqual2
和checkEqual3
不能轻易改为采用比较a is b
而不是a == b
。
timeit
结果,对于Python 2.7和(只有s1,s4,s7,s9应该返回True)
s1 = [1] * 5000 s2 = [1] * 4999 + [2] s3 = [2] + [1]*4999 s4 = [set([9])] * 5000 s5 = [set([9])] * 4999 + [set([10])] s6 = [set([10])] + [set([9])] * 4999 s7 = [1,1] s8 = [1,2] s9 = []
我们得到
| checkEqual1 | checkEqual2 | checkEqual3 | checkEqualIvo | checkEqual6502 | |-----|-------------|-------------|--------------|---------------|----------------| | s1 | 1.19 msec | 348 usec | 183 usec | 51.6 usec | 121 usec | | s2 | 1.17 msec | 376 usec | 185 usec | 50.9 usec | 118 usec | | s3 | 4.17 usec | 348 usec | 120 usec | 264 usec | 61.3 usec | | | | | | | | | s4 | 1.73 msec | | 182 usec | 50.5 usec | 121 usec | | s5 | 1.71 msec | | 181 usec | 50.6 usec | 125 usec | | s6 | 4.29 usec | | 122 usec | 423 usec | 61.1 usec | | | | | | | | | s7 | 3.1 usec | 1.4 usec | 1.24 usec | 0.932 usec | 1.92 usec | | s8 | 4.07 usec | 1.54 usec | 1.28 usec | 0.997 usec | 1.79 usec | | s9 | 5.91 usec | 1.25 usec | 0.749 usec | 0.407 usec | 0.386 usec |
注意:
# http://stackoverflow.com/q/3844948/ def checkEqualIvo(lst): return not lst or lst.count(lst[0]) == len(lst) # http://stackoverflow.com/q/3844931/ def checkEqual6502(lst): return not lst or [lst[0]]*len(lst) == lst
比使用set()对序列(而不是可迭代)起作用的解决scheme更快的方法是简单地计算第一个元素。 这假定列表是非空的(但这是微不足道的检查,并决定自己的结果应该在一个空的列表)
x.count(x[0]) == len(x)
一些简单的基准:
>>> timeit.timeit('len(set(s1))<=1', 's1=[1]*5000', number=10000) 1.4383411407470703 >>> timeit.timeit('len(set(s1))<=1', 's1=[1]*4999+[2]', number=10000) 1.4765670299530029 >>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*5000', number=10000) 0.26274609565734863 >>> timeit.timeit('s1.count(s1[0])==len(s1)', 's1=[1]*4999+[2]', number=10000) 0.25654196739196777
最简单最优雅的方式如下:
all(x==myList[0] for x in myList)
(是的,这甚至与空列表一起工作!这是因为这是less数python有惰性语义的情况之一)。
关于性能,这将在最早的时间失败,所以它是渐近最佳的。
您可以将列表转换为一个集合。 一套不能有重复。 所以如果原始列表中的所有元素都是相同的,那么这个集合将只有一个元素。
if len(sets.Set(input_list)) == 1 // input_list has all identical elements.
一套比较工作:
len(set(the_list)) == 1
使用set
删除所有重复的元素。
这是另一种select,对于长列表来说比len(set(x))==1
更快(使用短路)
def constantList(x): return x and [x[0]]*len(x) == x
对于它的价值,最近在python-ideas邮件列表上出现了。 事实certificate,这已经是一个itertools配方 : 1
def all_equal(iterable): "Returns True if all the elements are equal to each other" g = groupby(iterable) return next(g, True) and not next(g, False)
据说,它performance非常好,并有一些不错的属性。
- 短路:一旦发现第一个不相等的项目,就会停止从迭代中消耗项目。
- 不要求物品可以被排除。
- 这是懒惰的,只需要O(1)额外的内存来做检查。
1换句话说,我不能拿出解决scheme的功劳 – 我也不能因为find解决scheme而信任。
怀疑这是“最Pythonic”,但是是这样的:
>>> falseList = [1,2,3,4] >>> trueList = [1, 1, 1] >>> >>> def testList(list): ... for item in list[1:]: ... if item != list[0]: ... return False ... return True ... >>> testList(falseList) False >>> testList(trueList) True
会做的伎俩。
这是一个简单的方法:
result = mylist and all(mylist[0] == elem for elem in mylist)
这稍微复杂一点,它会引起函数调用的开销,但是语义更加清晰:
def all_identical(seq): if not seq: # empty list is False. return False first = seq[0] return all(first == elem for elem in seq)
我会做:
not any((x[i] != x[i+1] for i in range(0, len(x)-1)))
因为any
一旦停止search可迭代,只要它发现一个True
条件。
如果你对一些更可读的东西感兴趣(当然,效率不高),你可以尝试:
def compare_lists(list1, list2): if len(list1) != len(list2): # Weed out unequal length lists. return False for item in list1: if item not in list2: return False return True a_list_1 = ['apple', 'orange', 'grape', 'pear'] a_list_2 = ['pear', 'orange', 'grape', 'apple'] b_list_1 = ['apple', 'orange', 'grape', 'pear'] b_list_2 = ['apple', 'orange', 'banana', 'pear'] c_list_1 = ['apple', 'orange', 'grape'] c_list_2 = ['grape', 'orange'] print compare_lists(a_list_1, a_list_2) # Returns True print compare_lists(b_list_1, b_list_2) # Returns False print compare_lists(c_list_1, c_list_2) # Returns False
关于使用lambda
使用reduce()
。 这是一个工作代码,我个人认为比其他答案更好。
reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))
如果所有项目都相同,则返回第一个值为布尔值的真值。
def allTheSame(i): j = itertools.groupby(i) for k in j: break for k in j: return False return True
在Python 2.4中工作,没有“全部”。
将列表转换为集合,然后查找集合中元素的数量。 如果结果是1,则它具有相同的元素,如果不是,则列表中的元素不相同。
list1 = [1,1,1] len(set(list1)) = 1 list1 = [1,2,3] len(set)
检查是否所有元素都等于第一个。
np.allclose(array, array[0])
可以使用地图和lambda
lst = [1,1,1,1,1,1,1,1,1] print all(map(lambda x: x == lst[0], lst[1:]))
>>> a = [1, 2, 3, 4, 5, 6] >>> z = [(a[x], a[x+1]) for x in range(0, len(a)-1)] >>> z [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)] # Replacing it with the test >>> z = [(a[x] == a[x+1]) for x in range(0, len(a)-1)] >>> z [False, False, False, False, False] >>> if False in z : Print "All elements are not equal"
你可以做:
reduce(and_, (x==yourList[0] for x in yourList), True)
python让你导入operator.and_这样的operator.and_
是相当麻烦的。 从python3开始,你还需要导入functools.reduce
。
(你不应该使用这个方法,因为如果它发现不相等的值,它不会中断,但是会继续检查整个列表,这里只是作为完整性的一个答案。
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]
下一个会短路:
all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))