检查列表中的所有元素是否相同

Check if all elements in a list are identical

提问人:max 提问时间:10/2/2010 最后编辑:max 更新时间:9/12/2023 访问量:607782

问:

我需要一个函数,如果输入列表中的所有元素都使用标准相等运算符计算结果,则该函数将接受 a 并输出。listTrueFalse

我觉得最好遍历列表,比较相邻元素,然后遍历所有生成的布尔值。但我不确定最 Python 的方法是什么。AND

Python 算法 比较

评论

6赞 kennytm 10/2/2010
等于 in 或与 in 相同?a == ba is b
1赞 Doug 10/2/2010
解决方案是否应该处理空列表?如果是这样,应该退回什么?
2赞 max 10/2/2010
等于 a == b。应处理空列表,并返回 True。
6赞 user2846495 4/6/2020
虽然我知道它比其他一些建议慢,但我很惊讶没有被建议。functools.reduce(operator.eq, a)
3赞 tbrugere 4/19/2021
@ user2846495 将不起作用。例如,对于 list ,它将返回 。而函数应返回 False(因为元素并非全部相等)functools.reduce(operator.eq, a)[True, False, False]((True == False) == False)True

答:

3赞 machineghost 10/2/2010 #1

怀疑这是“最 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

会做这个伎俩。

评论

2赞 musiphil 8/19/2016
你的循环可以更像 Pythonic ,具有完全相同的语义。forif any(item != list[0] for item in list[1:]): return False
607赞 kennytm 10/2/2010 #2

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

您可以考虑使用许多替代的单行代码:

  1. 将输入转换为集合并检查它是否只有一个或零个(如果输入为空)项

    def all_equal2(iterator):
        return len(set(iterator)) <= 1
    
  2. 与没有第一项的输入列表进行比较

    def all_equal3(lst):
        return lst[:-1] == lst[1:]
    
  3. 计算第一项在列表中出现的次数

    def all_equal_ivo(lst):
        return not lst or lst.count(lst[0]) == len(lst)
    
  4. 与重复的第一个元素的列表进行比较

    def all_equal_6502(lst):
        return not lst or [lst[0]]*len(lst) == lst
    

但它们有一些缺点,即:

  1. all_equal并且可以使用任何迭代器,但其他迭代器必须接受序列输入,通常是具体容器,如列表或元组。all_equal2
  2. all_equal一旦发现差异就停止(所谓的“短路”),而所有替代方案都需要遍历整个列表,即使您可以通过查看前两个元素来判断答案。all_equal3False
  3. 在内容中必须是可散列的。例如,列表列表将引发一个。all_equal2TypeError
  4. all_equal2(在最坏的情况下)并创建列表的副本,这意味着您需要使用双倍的内存。all_equal_6502

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

for a list with a difference in the first two elements, groupby is fastestfor a list with no differences, count(l[0]) is fastest

评论

4赞 Glenn Maynard 10/2/2010
不要忘记对非常大的数组进行内存使用分析,这是一种本机解决方案,可以优化对 when 的调用,以及无序优化,以允许更快地缩短排序列表。obj.__eq__lhs is rhs
4赞 teichert 5/20/2020
如果 checkEqual3 的直觉对其他人来说并不明显:如果first == second and second == third and ...
0赞 Yaakov Bressler 12/21/2020
这真是太快了。方法也很聪明。对于那些使用 实现 的人有用的建议,您应该在断言 True/False 之前返回结果。(将此函数包装在另一个函数中。pytest
2赞 ChaimG 1/27/2021
@Boris:这些图表的代码是什么?
4赞 1/27/2021
@ChaimG,如果您单击“编辑”,代码将隐藏在答案文本的注释中。
50赞 codaddict 10/2/2010 #3

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

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

评论

0赞 aaronasterling 10/2/2010
这很好,但它不会短路,您必须计算结果列表的长度。
0赞 codaddict 10/2/2010
@AaronMcSmooth:还是个菜鸟。甚至不知道py中的短路是什么意思:)
3赞 aaronasterling 10/2/2010
@codaddict。这意味着即使前两个元素是不同的,它仍然会完成整个搜索。它还使用 O(k) 额外的空格,其中 k 是列表中不同元素的数量。
1赞 aaronasterling 10/2/2010
@max. 因为构建集合是在 C 语言中进行的,并且你的实现很糟糕。您至少应该在生成器表达式中执行此操作。请参阅 KennyTM 的答案,了解如何在不使用套装的情况下正确执行此操作。
2赞 11/2/2020
如果您的列表为空,则返回 .它还要求您的所有元素都是可散列的。FalseTrue
2赞 pyfunc 10/2/2010 #4
>>> 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"
13赞 Jerub 10/2/2010 #5

这是一种简单的方法:

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)

评论

0赞 Brendan 1/5/2017
您可以通过使用 来避免此处的冗余比较。毫无疑问,它大大提高了速度,因为我猜,所以口译员可能会很快进行比较。for elem in mylist[1:]elem[0] is elem[0]
11赞 6502 10/2/2010 #6

这是另一种选择,比长列表(使用短路)更快len(set(x))==1

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

评论

0赞 max 10/2/2010
它比我计算机上设置的解决方案慢 3 倍,忽略短路。因此,如果平均在列表的前三分之一找到不相等元素,则平均速度更快。
387赞 Ivo van der Wijk 10/2/2010 #7

比使用 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

评论

8赞 max 10/2/2010
天哪,这比设置的解决方案快 6 倍!(2.8 亿个元素/秒,而我的笔记本电脑为 4500 万个元素/秒)。为什么???有没有办法修改它以使其短路(我想不是......
3赞 Ivo van der Wijk 10/2/2010
我猜 list.count 有一个高度优化的 C 实现,并且列表的长度存储在内部,所以 len() 也很便宜。没有办法使 count() 短路,因为您需要真正检查所有元素才能获得正确的计数。
0赞 max 10/5/2010
我可以将其更改为:以便它适用于任何容器 x 吗?啊..nm,刚刚看到 .count 仅适用于序列。.为什么没有为其他内置容器实现它?在字典中计数本质上不如在列表中计数有意义吗?x.count(next(x)) == len(x)
5赞 Ivo van der Wijk 10/5/2010
迭代器可能没有长度。例如,它可以是无限的,也可以只是动态生成的。您只能通过将其转换为列表来找到它的长度,这会消除迭代器的大部分优势
0赞 max 3/12/2016
对不起,我的意思是为什么没有为可迭代对象实现,而不是为什么没有为迭代器实现。答案可能是这只是一个疏忽。但这对我们来说无关紧要,因为序列的默认值非常慢(纯 python)。您的解决方案之所以如此之快,是因为它依赖于 提供的 C 实现。因此,我认为在 C 中实现方法的任何可迭代都将从您的方法中受益。countlen.count()countlistcount
3赞 itertool 1/10/2012 #8
def allTheSame(i):
    j = itertools.groupby(i)
    for k in j: break
    for k in j: return False
    return True

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

评论

1赞 ninjagecko 4/24/2012
for k in j: break等效于 。如果你不关心效率,你也可以这样做。next(j)def allTheSame(x): return len(list(itertools.groupby(x))<2)
3赞 Robert Rossney 1/14/2012 #9

我会做:

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

一旦找到条件,就会停止搜索可迭代对象。anyTrue

评论

0赞 ninjagecko 4/24/2012
如果生成器表达式是唯一的参数,则不需要在生成器表达式周围加上额外的括号。
0赞 Chen A. 9/4/2017
那么,为什么不用呢?看起来更像 pythonic,应该执行相同的操作all()all(x == seq[0] for x in seq)
233赞 ninjagecko 4/24/2012 #10

[编辑:这个答案解决了目前投票最多的(这是一个很好的答案)答案。itertools.groupby

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

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

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

这将在尽可能早的时间失败,因此它是渐近最优的(预期时间大约是 O(#uniques) 而不是 O(N),但最坏情况下的时间仍然是 O(N))。这是假设您以前没有看过数据......

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

如果您稍微关心性能,这比上面快两倍,但更冗长一些:

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

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

如果您更关心性能,请继续阅读......


关于性能的旁注,因为其他答案出于某种未知原因谈论它:

...如果您以前看过这些数据,并且可能正在使用某种集合数据结构,并且您真的很关心性能,那么您可以通过使用计数器来增强您的结构,该计数器会随着每个插入/删除/等操作而更新,并且只需检查它是否是 {something:someCount}len(counter.keys())==1;或者,您可以将计数器放在单独的变量中。事实证明,这比其他任何常数都要好。也许您也可以将 python 的 FFI 与您选择的方法一起使用,也可以使用启发式方法(例如,如果它是带有 getitem 的序列,然后检查第一个元素、最后一个元素,然后按顺序检查元素)。ctypes

当然,对于可读性,有一些话要说。

评论

0赞 max 4/25/2012
这可行,但比@KennyTM慢一点(1.5 倍)。我不知道为什么。checkEqual1
5赞 ninjagecko 11/17/2015
max:可能是因为我懒得执行优化,也许first=myList[0]all(x==first for x in myList)
0赞 Matt Liberty 1/12/2016
我认为每次迭代都会评估 myList[0]。>>> timeit.timeit('all([y == x[0] for y in x])', 'x=[1] * 4000', number=10000) 2.707076672740641 >>> timeit.timeit('x0 = x[0]; all([y == x0 for y in x])', 'x=[1] * 4000', number=10000) 2.0908854261426484
3赞 ninjagecko 1/13/2016
当然,我应该澄清一下,优化会抛出一个空列表,所以谈论我提到的优化的评论者将不得不处理空列表的边缘情况。但是,原始版本很好(在 中很好,因为如果列表为空,则永远不会对其进行评估)。first=myList[0]IndexErrorx==myList[0]all
1赞 Henry Gomersall 5/7/2016
这显然是正确的方法。如果您希望在每种情况下都具有速度,请使用 numpy 之类的东西。
2赞 ninjagecko 4/24/2012 #11

您可以执行以下操作:

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

python 让你导入像 .从 python3 开始,您还需要导入 .operator.and_functools.reduce

(不应使用此方法,因为如果找到不相等的值,它不会中断,但会继续检查整个列表。它只是作为完整性的答案包含在内。

2赞 Joshua Burns 6/5/2012 #12

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

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

评论

1赞 max 6/6/2012
我实际上是在尝试查看一个列表中的所有元素是否相同;如果两个单独的列表相同,则不会。
1赞 Martijn Pieters 11/16/2020
这也是非常低效的;对于长度为 N 的输入,它需要 N^2 步。**至少**,如果值是可散列的,则使用一组进行遏制测试。
1赞 user3015260 11/21/2013 #13
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)))

评论

0赞 berdario 3/27/2014
你的第一个代码显然是错误的:产量......我编辑了它,但这样它就不再漂亮了reduce(lambda a,b:a==b, [2,2,2])False
76赞 cbalawat 5/2/2014 #14

将您的输入转换为一组

len(set(the_list)) <= 1

using 将删除所有重复的元素。 以便它在输入为空时正确返回。set<= 1True

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

3赞 Marcus Lind 10/21/2015 #15

关于与 .这是一个工作代码,我个人认为它比其他一些答案要好得多。reduce()lambda

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

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

评论

1赞 schot 3/2/2020
编写的代码中有一个小错误(尝试):它没有考虑以前的布尔值。这可以通过将 替换为 来修复。[1, 2, 2]x[1] == yx[0] and x[1] == y
24赞 mgilson 10/7/2016 #16

值得一提的是,这最近出现在 python-ideas 邮件列表上。事实证明,已经有一个 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)

据说它的表现非常好,并且有一些不错的特性。

  1. 短路:一旦找到第一个不相等的项,它就会停止消耗可迭代对象中的项。
  2. 不要求项目是可散列的。
  3. 它是懒惰的,只需要 O(1) 个额外的内存来执行检查。

1換句話說,我不能把提出解決方案的功譽歸負責,也不能負責找到解決方案。

评论

1赞 Chris_Rands 4/26/2020
return next(g, f := next(g, g)) == f(当然来自 py3.8)
0赞 Michel de Ruiter 1/26/2023
要使它使用 ,只需添加一个异常处理程序即可。pd.NA
6赞 Gusev Slava 12/26/2016 #17

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

np.allclose(array, array[0])

评论

0赞 Bachsau 2/28/2019
需要第三方模块。
1赞 SuperNova 4/13/2017 #18

可以使用 map 和 lambda

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

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

评论

0赞 greybeard 1/16/2022
比较是否比切片慢(比 慢)?==1:all(map(lambda x: x == lst[0], lst))
1赞 Foad S. Farimani 11/5/2018 #19

还有一个纯 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 中有递归限制,在这种情况下需要调整。例如,使用这个

2赞 U13-Forward 12/14/2018 #20

或者使用numpy的方法:diff

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

并致电:

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

输出:

True

评论

0赞 GZ0 9/6/2019
我认为可以快一点。not np.any(np.diff(l))
1赞 Luis B 1/12/2019 #21

或者使用 numpy 的 diff 方法:

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

并致电:

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

输出:

评论

2赞 mhwombat 2/28/2019
这个答案与去年U9-Forward的答案相同。
0赞 Luis B 3/2/2019
好眼光!我使用了相同的结构/API,但我的方法使用 np.unique 和 shape。U9 的函数使用 np.all() 和 np.diff() -- 我不使用这两个函数中的任何一个。
0赞 Saeed 7/7/2019 #22

您可以使用它来查找列表中唯一项的数量。.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

评论

0赞 Michel de Ruiter 1/26/2023
是的,但工作太多了。nunique
1赞 Uday Allu 11/25/2020 #23

简单的解决方案是应用列表上的集合

如果所有元素都相同,则 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

评论

0赞 4/29/2021
你的答案和 stackoverflow.com/a/23415761 有什么不同
0赞 Kermit 5/18/2021 #24

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

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

评论

0赞 mykhal 6/26/2021
我不会害怕空名单......你能说没有(零计数)(非)值彼此不同吗?
1赞 Kermit 6/26/2021
你有例子吗?
-2赞 mykhal 6/26/2021 #25

我认为,这是一个具有大量 Pythonicity 的代码,并且在简单性和明显性之间取得了平衡,它也应该适用于非常旧的 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

评论

0赞 greybeard 6/26/2021
(几乎有用,但缺少文档字符串:虽然名称是助记符,但我喜欢检查我的 IDE 中的悬停,例如:?all_eq([])
0赞 mykhal 6/26/2021
@greybeard对不起,这不是官方软件包
0赞 greybeard 6/26/2021
(你写“无文档”代码?对我不起作用。
-1赞 jacktrader 1/16/2022 #26

这是一个有趣的阅读和思考。谢谢大家! 我不认为任何依赖纯计数的东西在所有情况下都是可靠的。此外,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 的简单性和聪明性是我最喜欢的语言之一。再想一想,如果你无论如何都必须修改列表,就像我实际上所做的那样,因为我正在比较地址(并且会删除前导/尾随空格并转换为小写以消除可能的不一致,我的会更适合这项工作)。所以“更好”是主观的,因为我在使用这个词时使用引号来回避!但您也可以事先清理列表。

祝你好运!

评论

0赞 jacktrader 1/17/2022
好抓,我瞥了一眼命令,显然没有想清楚。它拆分列表,然后反转进行比较(我只看到了相反的情况)。所以它可能适用于所有情况,我不确定?但我只是删除了它,直到我可以测试一些用例。但是对于大情况来说,逻辑也很难遵循,所以我不确定我是否会使用它。谢谢!
0赞 Hans Bouwmeester 3/7/2022
-1.例如,这些审议并没有增加太多,例如,@ninjagecko早在2012年的回答: .这里提出的顶级解决方案与此类似,但性能较差且更难理解。all(x==myList[0] for x in myList)
3赞 Raymond Hettinger 4/6/2022 #27

最佳答案

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

给定列表输入,最佳提交是:

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

其他方法

以下是该线程的结果:

  1. 让 groupby() 比较相邻的条目。这有一个不匹配的早期退出,不使用额外的内存,并且它以 C 速度运行。

    g = itertools.groupby(s)
    next(g, True) and not next(g, False)
    
  2. 比较两个切片彼此偏移一个位置。这会占用额外的内存,但以 C 速度运行。

    s[1:] == s[:-1]
    
  3. 切片比较的迭代器版本。它以 C 速度运行,不使用额外的内存;但是,EQ调用很昂贵。

    all(map(operator.eq, s, itertools.islice(s, 1, None)))
    
  4. 比较最低值和最高值。它以 C 速度运行,不使用额外的内存,但每个基准需要两次不等式检验。

    min(s) == max(s)  # s must be non-empty
    
  5. 构建一个集合。它以 C 速度运行,几乎不使用额外的内存,但需要哈希性并且没有提前退出。

    len(set(t))==1.
    
  6. 这花费了巨大的成本,可以处理 NaN 和其他具有奇异相等关系的对象。

    all(itertools.starmap(eq, itertools.product(s, repeat=2)))
    
  7. 拉出第一个元素并将其所有其他元素与它进行比较,在第一个不匹配处停止。唯一的缺点是它不能以 C 速度运行。

     it = iter(s)
     a = next(it, None)
     return all(a == b for b in it)
    
  8. 只需数第一个元素。这是快速、简单、优雅的。它以 C 速度运行,不需要额外的内存,仅使用相等性测试,并且仅对数据进行一次传递。

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

评论

0赞 Kelly Bundy 5/10/2022
大约 12 倍的检查替代方案:productmin(map(s.count, s)) == len(s)
0赞 Lajos 4/14/2022 #28

我最终得到了这个单行字

from itertools import starmap, pairwise
all(starmap(eq, (pairwise(x)))
1赞 Stefan Pochmann 5/11/2022 #29

我发现使用的更多版本比原始版本更清晰(更多内容见下文):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)

请注意,the 始终true(它要么是非空的,要么是 )。这意味着它的价值并不重要。它纯粹是为了推进迭代器而执行的。但是在表达式中包含它会导致读者认为它的价值在那里被使用。既然没有,我觉得这具有误导性和不必要的复杂性。我上面的第二个版本将它视为它的实际用途,作为我们不关心其价值的陈述。next(g, True)tupleTruegroupbyreturnnext(g, True)

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

我的第一个解决方案与我的第三个解决方案基本相同,只是使用 .两种解决方案都读作“所有元素都是平等的......没有第一组,也没有第二组。any

基准测试结果(虽然速度真的不是我在这里的重点,但清晰度是,在实践中,如果有许多相等的值,大部分时间可能会花在本身上,从而减少这些差异的影响):groupby

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

评论

0赞 Michel de Ruiter 1/26/2023
要使用 ,只需添加一个异常处理程序即可。all_equalpd.NA
-1赞 Luca Di Liello 2/14/2023 #30

我建议一个简单的pythonic解决方案:

def all_equal_in_iterable(iterable: Iterable):
    iterable = list(iterable)
    if not iterable:
        return True
    return all(item == iterable[0] for item in iterable)