Проверьте, все ли элементы в списке идентичны
Мне нужна функция, которая принимает list
и выводитTrue
, если все элементы во входном списке оцениваются как равные друг другу с использованием стандартного оператора равенства и False
в противном случае.
Я чувствую, что было бы лучше выполнить итерацию по списку, сравнивая соседние элементы, а затем AND
все результирующие логические значения. Но я не уверен, какой наиболее питонический способ сделать это.
Переведено автоматически
Ответ 1
Использование itertools.groupby
(см. the 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)
Существует ряд альтернативных однострочников, которые вы могли бы рассмотреть:
Преобразуем входные данные в set и проверяем, содержит ли он только один или ноль (в случае, если входные данные пусты) элементов
def all_equal2(iterator):
return len(set(iterator)) <= 1Сравнение с входным списком без первого элемента
def all_equal3(lst):
return lst[:-1] == lst[1:]Подсчет количества раз, когда первый элемент появляется в списке
def all_equal_ivo(lst):
return not lst or lst.count(lst[0]) == len(lst)Сравнение со списком первого элемента повторяется
def all_equal_6502(lst):
return not lst or [lst[0]]*len(lst) == lst
Но у них есть некоторые недостатки, а именно:
all_equal
иall_equal2
могут использовать любые итераторы, но остальные должны принимать ввод последовательности, обычно это конкретные контейнеры, такие как список или кортеж.all_equal
иall_equal3
остановитесь, как только будет найдено различие (то, что называется "короткое замыкание"), тогда как все альтернативы требуют повторения всего списка, даже если вы можете сказать, что ответ есть,False
просто взглянув на первые два элемента.- В
all_equal2
содержимое должно быть хэшируемым. Например, список списков вызоветTypeError
. all_equal2
(в худшем случае) иall_equal_6502
создайте копию списка, что означает, что вам нужно использовать вдвое больше памяти.
В Python 3.9, используя perfplot
, мы получаем эти тайминги (чем меньше Runtime [s]
, тем лучше):
Ответ 2
Решение, более быстрое, чем использование 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
Ответ 3
[редактировать: этот ответ касается текущего ответа, набравшего наибольшее количество голосов itertools.groupby
(что является хорошим ответом), позже.]
Без перезаписи программы наиболее асимптотически производительный и наиболее читаемый способ заключается в следующем:
all(x==myList[0] for x in myList)
(Да, это работает даже с пустым списком! Это потому, что это один из немногих случаев, когда python имеет ленивую семантику.)
Это приведет к сбою в кратчайшие возможные сроки, поэтому это асимптотически оптимально (ожидаемое время составляет приблизительно O (# uniques), а не 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.groupby
ответ, который в два раза быстрее, чем allEqual
потому что он, вероятно, оптимизирован для кода C. (Согласно документам, это не должно (аналогично этому ответу) иметь каких-либо накладных расходов на память, потому что отложенный генератор никогда не преобразуется в список ... о чем можно беспокоиться, но псевдокод показывает, что сгруппированные "списки" на самом деле являются отложенными генераторами.)
Если вас еще больше волнует производительность, читайте дальше...
примечания относительно производительности, потому что в других ответах говорится об этом по какой-то неизвестной причине:
... если вы видели данные раньше и, вероятно, используете какую-то структуру данных коллекции, и вас действительно волнует производительность, вы можете получить .isAllEqual()
бесплатно O(1), дополнив свою структуру Counter
, Которая обновляется при каждой операции вставки / удаления / и т.д. И просто проверяя, имеет ли она вид {something:someCount}
т.Е. len(counter.keys())==1
; в качестве альтернативы вы можете сохранить счетчик сбоку в отдельной переменной. Это доказуемо лучше, чем что-либо еще с точностью до constant factor. Возможно, вы также можете использовать FFI в python с ctypes
выбранным вами методом и, возможно, с эвристикой (например, если это последовательность с getitem , затем проверяйте первый элемент, последний элемент, затем элементы по порядку).
Конечно, есть кое-что, что нужно сказать для удобства чтения.
Ответ 4
Преобразуйте ваши входные данные в set
:
len(set(the_list)) <= 1
Использование set
удаляет все повторяющиеся элементы. <= 1
это значит, что он правильно возвращает, True
когда входные данные пусты.
Для этого требуется, чтобы все элементы во входных данных были хэшируемыми. Вы получитеTypeError
, например, если передадите список списков.