可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need the following function:
Input: a list
Output:
True
if all elements in the input list evaluate as equal to each other using the standard equality operator;
False
otherwise.
Performance: of course, I prefer not to incur any unnecessary overhead.
I feel it would be best to:
- iterate through the list
- compare adjacent elements
- and
AND
all the resulting Boolean values
But I\'m not sure what\'s the most Pythonic way to do that.
EDIT:
Thank you for all the great answers. I rated up several, and it was really hard to choose between @KennyTM and @Ivo van der Wijk solutions.
The lack of short-circuit feature only hurts on a long input (over ~50 elements) that have unequal elements early on. If this occurs often enough (how often depends on how long the lists might be), the short-circuit is required. The best short-circuit algorithm seems to be @KennyTM checkEqual1
. It pays, however, a significant cost for this:
- up to 20x in performance nearly-identical lists
- up to 2.5x in performance on short lists
If the long inputs with early unequal elements don\'t happen (or happen sufficiently rarely), short-circuit isn\'t required. Then, by far the fastest is @Ivo van der Wijk solution.
回答1:
General method:
def checkEqual1(iterator):
iterator = iter(iterator)
try:
first = next(iterator)
except StopIteration:
return True
return all(first == rest for rest in iterator)
One-liner:
def checkEqual2(iterator):
return len(set(iterator)) <= 1
Also one-liner:
def checkEqual3(lst):
return lst[1:] == lst[:-1]
The difference between the 3 versions are that:
- In
checkEqual2
the content must be hashable.
checkEqual1
and checkEqual2
can use any iterators, but checkEqual3
must take a sequence input, typically concrete containers like a list or tuple.
checkEqual1
stops as soon as a difference is found.
- Since
checkEqual1
contains more Python code, it is less efficient when many of the items are equal in the beginning.
- Since
checkEqual2
and checkEqual3
always perform O(N) copying operations, they will take longer if most of your input will return False.
- For
checkEqual2
and checkEqual3
it\'s harder to adapt comparison from a == b
to a is b
.
timeit
result, for Python 2.7 and (only s1, s4, s7, s9 should return 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 = []
we get
| 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 |
Note:
# 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
回答2:
A solution faster than using set() that works on sequences (not iterables) is to simply count the first element. This assumes the list is non-empty (but that\'s trivial to check, and decide yourself what the outcome should be on an empty list)
x.count(x[0]) == len(x)
some simple benchmarks:
>>> 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
回答3:
The simplest and most elegant way is as follows:
all(x==myList[0] for x in myList)
(Yes, this even works with the null list! This is because this is one of the few cases where python has lazy semantics.)
Regarding performance, this will fail at the earliest possible time, so it is asymptotically optimal.
回答4:
A set comparison work:
len(set(the_list)) == 1
Using set
removes all duplicate elements.
回答5:
You can convert the list to a set. A set cannot have duplicates. So if all the elements in the original list are identical, the set will have just one element.
if len(sets.Set(input_list)) == 1
// input_list has all identical elements.
回答6:
For what it\'s worth, this came up on the python-ideas mailing list recently. It turns out that there is an itertools recipe for doing this already: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)
Supposedly it performs very nicely and has a few nice properties.
- Short-circuits: It will stop consuming items from the iterable as soon as it finds the first non-equal item.
- Doesn\'t require items to be hashable.
- It is lazy and only requires O(1) additional memory to do the check.
1In other words, I can\'t take the credit for coming up with the solution -- nor can I take credit for even finding it.
回答7:
This is another option, faster than len(set(x))==1
for long lists (uses short circuit)
def constantList(x):
return x and [x[0]]*len(x) == x
回答8:
Here are two simple ways of doing this
using set()
When converting the list to a set, duplicate elements are removed. So if the length of the converted set is 1, then this implies that all the elements are the same.
len(set(input_list))==1
Here is an example
>>> a = [\'not\', \'the\', \'same\']
>>> b = [\'same\', \'same\', \'same\']
>>> len(set(a))==1 # == 3
False
>>> len(set(b))==1 # == 1
True
using all()
This will compare (equivalence) the first element of the input list to every other element in the list. If all are equivalent True will be returned, otherwise False will be returned.
all(element==input_list[0] for element in input_list)
Here is an example
>>> a = [1, 2, 3, 4, 5]
>>> b = [1, 1, 1, 1, 1]
>>> all(number==a[0] for number in a)
False
>>> all(number==b[0] for number in b)
True
P.S If you are checking to see if the whole list is equivalent to a certain value, you can suibstitue the value in for input_list[0].
回答9:
This is a simple way of doing it:
result = mylist and all(mylist[0] == elem for elem in mylist)
This is slightly more complicated, it incurs function call overhead, but the semantics are more clearly spelled out:
def all_identical(seq):
if not seq:
# empty list is False.
return False
first = seq[0]
return all(first == elem for elem in seq)
回答10:
Doubt this is the \"most Pythonic\", but something like:
>>> 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
would do the trick.
回答11:
If you\'re interested in something a little more readable (but of course not as efficient,) you could try:
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
回答12:
I\'d do:
not any((x[i] != x[i+1] for i in range(0, len(x)-1)))
as any
stops searching the iterable as soon as it finds a True
condition.
回答13:
Regarding using reduce()
with lambda
. Here is a working code that I personally think is way nicer than some of the other answers.
reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))
Returns a truple where the first value is the boolean if all items are same or not.
回答14:
Check if all elements equal to the first.
np.allclose(array, array[0])
回答15:
Convert the list into the set and then find the number of elements in the set. If the result is 1, it has identical elements and if not, then the elements in the list are not identical.
list1 = [1,1,1]
len(set(list1))
>1
list1 = [1,2,3]
len(set(list1)
>3
回答16:
>>> 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\"
回答17:
def allTheSame(i):
j = itertools.groupby(i)
for k in j: break
for k in j: return False
return True
Works in Python 2.4, which doesn\'t have \"all\".
回答18:
Can use map and lambda
lst = [1,1,1,1,1,1,1,1,1]
print all(map(lambda x: x == lst[0], lst[1:]))
回答19:
You can do:
reduce(and_, (x==yourList[0] for x in yourList), True)
It is fairly annoying that python makes you import the operators like operator.and_
. As of python3, you will need to also import functools.reduce
.
(You should not use this method because it will not break if it finds non-equal values, but will continue examining the entire list. It is just included here as an answer for completeness.)
回答20:
lambda lst: reduce(lambda a,b:(b,b==a[0] and a[1]), lst, (lst[0], True))[1]
The next one will short short circuit:
all(itertools.imap(lambda i:yourlist[i]==yourlist[i+1], xrange(len(yourlist)-1)))
回答21:
Change the list to a set. Then if the size of the set is only 1, they must have been the same.
if len(set(my_list)) == 1:
回答22:
There is also a pure Python recursive option:
def checkEqual(lst):
if len(lst)==2 :
return lst[0]==lst[1]
else:
return lst[0]==lst[1] and checkEqual(lst[1:])
However for some reason it is in some cases two orders of magnitude slower than other options. Coming from C language mentality, I expected this to be faster, but it is not!
The other disadvantage is that there is recursion limit in Python which needs to be adjusted in this case. For example using this.
回答23:
Or use diff
method of numpy:
import numpy as np
def allthesame(l):
return np.all(np.diff(l)==0)
And to call:
print(allthesame([1,1,1]))
Output:
True