我需要一个函数,它接受一个列表并输出True,如果输入列表中的所有元素使用标准相等运算符计算彼此相等,否则输出False。

我觉得最好是遍历列表,比较相邻的元素,然后与所有结果布尔值。但我不知道最python的方法是什么。


我怀疑这是“最python化的”,但类似于:

>>> 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

会成功的。


出现使用itertools。Groupby(参见itertools食谱):

from itertools import groupby

def all_equal(iterable):
    g = groupby(iterable)
    return next(g, True) and not next(g, False)

或不带groupby:

def all_equal(iterator):
    iterator = iter(iterator)
    try:
        first = next(iterator)
    except StopIteration:
        return True
    return all(first == x for x in iterator)

您可以考虑使用许多其他的一行程序:

Converting the input to a set and checking that it only has one or zero (in case the input is empty) items def all_equal2(iterator): return len(set(iterator)) <= 1 Comparing against the input list without the first item def all_equal3(lst): return lst[:-1] == lst[1:] Counting how many times the first item appears in the list def all_equal_ivo(lst): return not lst or lst.count(lst[0]) == len(lst) Comparing against a list of the first element repeated def all_equal_6502(lst): return not lst or [lst[0]]*len(lst) == lst

但它们也有一些缺点,即:

all_equal and all_equal2 can use any iterators, but the others must take a sequence input, typically concrete containers like a list or tuple. all_equal and all_equal3 stop as soon as a difference is found (what is called "short circuit"), whereas all the alternatives require iterating over the entire list, even if you can tell that the answer is False just by looking at the first two elements. In all_equal2 the content must be hashable. A list of lists will raise a TypeError for example. all_equal2 (in the worst case) and all_equal_6502 create a copy of the list, meaning you need to use double the memory.

在Python 3.9中,使用perfplot,我们得到这些计时(越低的Runtime [s]越好):


您可以将列表转换为集合。集合不能有重复项。所以如果原始列表中的所有元素都是相同的,那么集合就只有一个元素。

if len(set(input_list)) == 1:
    # input_list has all identical elements.

>>> 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"

这是一种简单的方法:

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)

这是另一个选项,对于长列表,比len(set(x))==1快(使用短路)

def constantList(x):
    return x and [x[0]]*len(x) == x

比使用set()处理序列(而不是可迭代对象)更快的解决方案是简单地计算第一个元素。这假设列表是非空的(但这是微不足道的检查,并决定什么结果应该在一个空列表)

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

def allTheSame(i):
    j = itertools.groupby(i)
    for k in j: break
    for k in j: return False
    return True

适用于Python 2.4,它没有“all”。


我想做的事:

not any((x[i] != x[i+1] for i in range(0, len(x)-1)))

as any一旦找到True条件就会停止搜索可迭代对象。


[编辑:这个答案针对当前投票最多的itertools。Groupby(这是一个很好的答案)稍后回答。

在不重写程序的情况下,最具渐近性能和可读性的方法如下:

all(x==myList[0] for x in myList)

(是的,这甚至适用于空列表!这是因为这是python具有惰性语义的少数情况之一。)

这将在尽可能早的时间失败,因此它是渐近最优的(期望时间大约是O(#惟一)而不是O(N),但最坏情况时间仍然是O(N))。这是假设你之前没有看过这些数据……

(如果你关心性能,但不太关心性能,你可以先做通常的标准优化,比如将myList[0]常量从循环中提升出来,并为边缘情况添加笨拙的逻辑,尽管这是python编译器最终可能会学会如何做的事情,因此除非绝对必要,否则不应该这样做,因为它会破坏最小收益的可读性。)

如果你更关心性能,这是上面速度的两倍,但有点啰嗦:

def allEqual(iterable):
    iterator = iter(iterable)
    
    try:
        firstItem = next(iterator)
    except StopIteration:
        return True
        
    for x in iterator:
        if x!=firstItem:
            return False
    return True

如果你更关心性能(但还不足以重写你的程序),请使用当前投票最多的itertools。它的速度是allEqual的两倍,因为它可能是优化的C代码。(根据文档,它应该(类似于这个答案)没有任何内存开销,因为惰性生成器永远不会被计算到列表中…这可能会让人担心,但伪代码表明,分组的“列表”实际上是惰性生成器。)

如果你更关心性能,请继续阅读…


关于性能的旁注,因为其他答案都在谈论它,因为一些未知的原因:

... if you have seen the data before and are likely using a collection data structure of some sort, and you really care about performance, you can get .isAllEqual() for free O(1) by augmenting your structure with a Counter that is updated with every insert/delete/etc. operation and just checking if it's of the form {something:someCount} i.e. len(counter.keys())==1; alternatively you can keep a Counter on the side in a separate variable. This is provably better than anything else up to constant factor. Perhaps you can also use python's FFI with ctypes with your chosen method, and perhaps with a heuristic (like if it's a sequence with getitem, then checking first element, last element, then elements in-order).

当然,可读性也有好处。


你可以:

reduce(and_, (x==yourList[0] for x in yourList), True)

python让你导入operator.and_这样的操作符是相当烦人的。从python3开始,还需要导入functools.reduce。

(您不应该使用此方法,因为如果它发现不相等的值,它不会中断,而是会继续检查整个列表。这里只是作为完整性的回答。)


如果你对一些更有可读性的东西感兴趣(但当然不是那么高效),你可以尝试:

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 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)))

将您的输入转换为一个集:

len(set(the_list)) <= 1

使用set可以删除所有重复的元素。<= 1使它在输入为空时正确地返回True。

这要求输入中的所有元素都是可哈希的。例如,如果传入一个列表的列表,就会得到一个TypeError。


关于使用reduce()和lambda。这里有一个工作代码,我个人认为比其他一些答案更好。

reduce(lambda x, y: (x[1]==y, y), [2, 2, 2], (True, 2))

返回一个元组,其中第一个值是布尔值,如果所有项都相同或不相同。


不管怎样,这是最近在python想法邮件列表中出现的。事实证明,已经有一个itertools配方可以做到这一点

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)

据说它的性能非常好,有一些不错的属性。

短路:一旦发现第一个不相等的项,它就会停止从可迭代对象中消费项目。 不要求项是可哈希的。 它是懒惰的,只需要O(1)个额外的内存来进行检查。

换句话说,我不能把提出这个解决方案的功劳占为己有,甚至我也不能把找到它的功劳占为己有。


检查是否所有元素都等于第一个。

np。allclose(数组,数组[0])


可以使用map和lambda吗

lst = [1,1,1,1,1,1,1,1,1]

print all(map(lambda x: x == lst[0], lst[1:]))

还有一个纯Python递归选项:

def checkEqual(lst):
    if len(lst)==2 :
        return lst[0]==lst[1]
    else:
        return lst[0]==lst[1] and checkEqual(lst[1:])

然而,由于某些原因,它在某些情况下比其他选项慢两个数量级。从C语言的角度来看,我希望这更快,但事实并非如此!

另一个缺点是Python中有递归限制,在这种情况下需要进行调整。比如用这个。


或者使用numpy的diff方法:

import numpy as np
def allthesame(l):
    return np.all(np.diff(l)==0)

并呼吁:

print(allthesame([1,1,1]))

输出:

True

或者使用numpy的diff方法:

import numpy as np
def allthesame(l):
    return np.unique(l).shape[0]<=1

并呼吁:

print(allthesame([1,1,1]))

输出:

True


您可以使用.nunique()来查找列表中唯一项的数量。

def identical_elements(list):
    series = pd.Series(list)
    if series.nunique() == 1: identical = True
    else:  identical = False
    return identical



identical_elements(['a', 'a'])
Out[427]: True

identical_elements(['a', 'b'])
Out[428]: False

简单的解决方案是应用set on list

如果所有元素都相同,len将为1,否则大于1

lst = [1,1,1,1,1,1,1,1,1]
len_lst = len(list(set(lst)))

print(len_lst)

1


lst = [1,2,1,1,1,1,1,1,1]
len_lst = len(list(set(lst)))
print(len_lst)

2

也许我低估了问题的严重性?检查列表中唯一值的长度。

lzt = [1,1,1,1,1,2]

if (len(set(lzt)) > 1):
    uniform = False
elif (len(set(lzt)) == 1):
    uniform = True
elif (not lzt):
    raise ValueError("List empty, get wrecked")

这是一段具有良好的Python性的代码,并且平衡了简单性和明显性,我认为,这应该也适用于相当老的Python版本。

def all_eq(lst):
    for idx, itm in enumerate(lst):
        if not idx:   # == 0
            prev = itm
        if itm != prev:
            return False
        prev = itm
    return True

这是一个有趣的阅读和思考。谢谢大家! 我不认为任何依赖于纯计数的方法对所有情况都是可靠的。sum也可以工作,但只适用于数字或长度(再次导致计数场景)。

但我喜欢简单,所以我想出了这个:

all(i==lst[c-1] for c, i in enumerate(lst))

或者,我确实认为@kennytm的这个聪明的方法也适用于所有情况(有趣的是,它可能是最快的)。所以我承认它可能比我的好:

[lst[0]]*len(lst) == lst

我认为一个聪明的小奖励也会起作用,因为set消除了重复(聪明是有趣的,但通常不是维护代码的最佳实践)。我认为@kennytm的方法仍然会更快,但只适用于大型列表:

len(set(lst)) == 1

但是Python的简单和聪明是我最喜欢的语言之一。再想一下,如果你必须修改列表,就像我实际上做的那样,因为我正在比较地址(并将删除开头/结尾空格并转换为小写以消除可能的不一致,我的将更适合这项工作)。所以“更好”是主观的,因为我在使用这个词时使用了引号!但是你也可以事先清理列表。

祝你好运!


最佳答案

Twitter上有一个不错的帖子,介绍了实现all_equal()函数的各种方法。

给定一个列表输入,最好的提交是:

 t.count(t[0]) == len(t)  

其他方法

下面是线程的结果:

Have groupby() compare adjacent entries. This has an early-out for a mismatch, does not use extra memory, and it runs at C speed. g = itertools.groupby(s) next(g, True) and not next(g, False) Compare two slices offset from one another by one position. This uses extra memory but runs at C speed. s[1:] == s[:-1] Iterator version of slice comparison. It runs at C speed and does not use extra memory; however, the eq calls are expensive. all(map(operator.eq, s, itertools.islice(s, 1, None))) Compare the lowest and highest values. This runs at C speed, doesn't use extra memory, but does cost two inequality tests per datum. min(s) == max(s) # s must be non-empty Build a set. This runs at C speed and uses little extra memory but requires hashability and does not have an early-out. len(set(t))==1. At great cost, this handles NaNs and other objects with exotic equality relations. all(itertools.starmap(eq, itertools.product(s, repeat=2))) Pull out the first element and compare all the others to it, stopping at the first mismatch. Only disadvantage is that this doesn't run at C speed. it = iter(s) a = next(it, None) return all(a == b for b in it) Just count the first element. This is fast, simple, elegant. It runs at C speed, requires no additional memory, uses only equality tests, and makes only a single pass over the data. t.count(t[0]) == len(t)


我最后写了这样一句话

from itertools import starmap, pairwise
all(starmap(eq, (pairwise(x)))

使用itertools的更多版本。groupby,我发现它比原来的更清晰(下面有更多关于它的信息):

def all_equal(iterable):
    g = groupby(iterable)
    return not any(g) or not any(g)

def all_equal(iterable):
    g = groupby(iterable)
    next(g, None)
    return not next(g, False)

def all_equal(iterable):
    g = groupby(iterable)
    return not next(g, False) or not next(g, False)

以下是来自Itertools Recipes的原始版本:

def all_equal(iterable):
    g = groupby(iterable)
    return next(g, True) and not next(g, False)

注意,下一个(g, True)总是True(它不是一个非空元组就是True)。这意味着它的值不重要。它的执行纯粹是为了推进groupby迭代器。但是在返回表达式中包含它会导致读者认为它的值在那里被使用。因为它没有,我发现这是误导和不必要的复杂。我上面的第二个版本将next(g, True)视为它的实际用途,作为一个我们不关心其值的语句。

我的第三个版本走了一个不同的方向,并使用了第一个next的值(g, False)。如果根本没有第一个组(即,如果给定的可迭代对象为“空”),则该解决方案立即返回结果,甚至不检查是否有第二个组。

我的第一个解决方案基本上和第三个一样,只是使用任何一个。两种解决方案都读作“所有元素都是相等的……”没有第一组,也没有第二组。”

基准测试结果(虽然速度并不是我在这里的重点,但清晰才是重点,在实践中,如果有许多相等的值,大多数时间可能由组自己花费,减少了这些差异的影响):

Python 3.10.4 on my Windows laptop:

iterable = ()
 914 ns   914 ns   916 ns  use_first_any
 917 ns   925 ns   925 ns  use_first_next
1074 ns  1075 ns  1075 ns  next_as_statement
1081 ns  1083 ns  1084 ns  original

iterable = (1,)
1290 ns  1290 ns  1291 ns  next_as_statement
1303 ns  1307 ns  1307 ns  use_first_next
1306 ns  1307 ns  1309 ns  use_first_any
1318 ns  1319 ns  1320 ns  original

iterable = (1, 2)
1463 ns  1464 ns  1467 ns  use_first_any
1463 ns  1463 ns  1467 ns  next_as_statement
1477 ns  1479 ns  1481 ns  use_first_next
1487 ns  1489 ns  1492 ns  original
Python 3.10.4 on a Debian Google Compute Engine instance:

iterable = ()
 234 ns   234 ns   234 ns  use_first_any
 234 ns   235 ns   235 ns  use_first_next
 264 ns   264 ns   264 ns  next_as_statement
 265 ns   265 ns   265 ns  original

iterable = (1,)
 308 ns   308 ns   308 ns  next_as_statement
 315 ns   315 ns   315 ns  original
 316 ns   316 ns   317 ns  use_first_any
 317 ns   317 ns   317 ns  use_first_next

iterable = (1, 2)
 361 ns   361 ns   361 ns  next_as_statement
 367 ns   367 ns   367 ns  original
 384 ns   385 ns   385 ns  use_first_next
 386 ns   387 ns   387 ns  use_first_any

基准测试代码:

from timeit import timeit
from random import shuffle
from bisect import insort
from itertools import groupby

def original(iterable):
    g = groupby(iterable)
    return next(g, True) and not next(g, False)

def use_first_any(iterable):
    g = groupby(iterable)
    return not any(g) or not any(g)

def next_as_statement(iterable):
    g = groupby(iterable)
    next(g, None)
    return not next(g, False)

def use_first_next(iterable):
    g = groupby(iterable)
    return not next(g, False) or not next(g, False)

funcs = [original, use_first_any, next_as_statement, use_first_next]

for iterable in (), (1,), (1, 2):
    print(f'{iterable = }')
    times = {func: [] for func in funcs}
    for _ in range(1000):
        shuffle(funcs)
        for func in funcs:
            number = 1000
            t = timeit(lambda: func(iterable), number=number) / number
            insort(times[func], t)
    for func in sorted(funcs, key=times.get):
        print(*('%4d ns ' % round(t * 1e9) for t in times[func][:3]), func.__name__)
    print()