Может ли Python проверить принадлежность нескольких значений в списке?
Я хочу проверить, если два или более значений имеют членство в списке, но я получаю неожиданный результат:
>>> 'a','b' in ['b', 'a', 'foo', 'bar']
('a', True)
Итак, может ли Python проверить принадлежность нескольких значений сразу в списке? Что означает этот результат?
8 ответов:
это то, что вы хотите, и будет работать почти во всех случаях:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b']) Trueвыражение
'a','b' in ['b', 'a', 'foo', 'bar']не работает должным образом, потому что Python интерпретирует его как кортеж:>>> 'a', 'b' ('a', 'b') >>> 'a', 5 + 2 ('a', 7) >>> 'a', 'x' in 'xerxes' ('a', True)Другие Функции
есть и другие способы выполнения этого теста, но они не будут работать для такого количества различных видов входных данных. Как Kabie указывает, что вы можете решить эту проблему с помощью наборы...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar'])) True >>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'} True...иногда:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'наборы могут быть созданы только с элементами hashable. Но выражение генератора
all(x in container for x in items)может обрабатывать практически любой тип контейнера. Единственное требование-этоcontainerбыть повторной итерации (т. е. не генератор).itemsможет быть любой iterable вообще.>>> container = [['b'], 'a', 'foo', 'bar'] >>> items = (i for i in ('a', ['b'])) >>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items) TrueТесты Скорости
во многих случаях тест подмножества будет быстрее, чем
all, но разница не шокирует-за исключением тех случаев, когда вопрос не имеет значения, потому что наборы не являются опцией. Преобразование списков в наборы только для целей такого теста не всегда будет стоить проблем. И преобразование генераторов в наборы иногда может быть невероятно расточительным,замедляя программы на много порядков.вот несколько критериев для иллюстрации. Самая большая разница приходит, когда оба
containerиitemsотносительно невелики. В этом случае подход подмножества составляет порядка величины быстрее:>>> smallset = set(range(10)) >>> smallsubset = set(range(5)) >>> %timeit smallset >= smallsubset 110 ns ± 0.702 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) >>> %timeit all(x in smallset for x in smallsubset) 951 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)это выглядит как большая разница. Но пока
container- это набор,allпо-прежнему идеально подходит для использования в значительно больших масштабах:>>> bigset = set(range(100000)) >>> bigsubset = set(range(50000)) >>> %timeit bigset >= bigsubset 1.14 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) >>> %timeit all(x in bigset for x in bigsubset) 5.96 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)использование тестирования подмножеств все еще быстрее, но только примерно в 5 раз в этом масштабе. Повышение скорости происходит из-за быстрого питона
c-поддержал реализацииset, но основной алгоритм одинаков в обоих случаях.если
itemsуже сохранены в списке по другим причинам, затем вам нужно будет преобразовать их в набор перед использованием подхода теста подмножества. Затем ускорение падает примерно до 2.5 x:>>> %timeit bigset >= set(bigsubseq) 2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)и если
containerэто последовательность, и ее нужно сначала преобразовать, тогда ускорение еще меньше:>>> %timeit set(bigseq) >= set(bigsubseq) 4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)единственный раз, когда мы получаем катастрофически медленные результаты, когда мы уходим
containerв последовательность:>>> %timeit all(x in bigseq for x in bigsubseq) 184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)и, конечно, мы сделаем это только в случае необходимости. Если все элементы!--26--> это hashable, затем мы сделаем это вместо этого:
>>> %timeit bigset = set(bigseq); all(x in bigset for x in bigsubseq) 7.24 ms ± 78 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)это всего лишь 1,66 x быстрее, чем альтернатива (
set(bigseq) >= set(bigsubseq), приурочено выше в 4.36).таким образом, тестирование подмножества, как правило, быстрее, но не с невероятным запасом. С другой стороны, давайте посмотрим, когда
allбыстрее. А что еслиitemsэто десять миллионов значений длиной, и, вероятно, есть значения, которые не находятся вcontainer?>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); set(bigset) >= set(hugeiter) 13.1 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) >>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); all(x in bigset for x in hugeiter) 2.33 ms ± 65.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)преобразование генератора в набор оказывается невероятно расточительным в этом случай. Элемент
setконструктор должен потреблять весь генератор. Но короткое замыкание поведениеallгарантирует, что только небольшая часть генератора должна быть потреблена, так что это быстрее, чем тест подмножества по четыре порядка.это крайний пример, конечно. Но, как видно, вы не можете предположить, что тот или иной подход будет быстрее во всех случаях.
Развязка
большую часть времени, преобразование
containerнабор стоит того, по крайней мере, если все его элементы хэшируются. Это потому чтоinдля множеств O (1), в то время какinдля последовательностей - O (n).С другой стороны, использование тестирования подмножеств, вероятно, стоит только иногда. Обязательно сделайте это, если ваши тестовые элементы уже хранятся в наборе. В противном случае,
allтолько немного медленнее, и не требует никакого дополнительного хранения. Его можно также использовать с большими генераторами деталей, и иногда обеспечивает a массовое ускорение в этом случае.
я почти уверен
inимеет более высокий приоритет, чем,таким образом, ваше утверждение интерпретируется как 'a', ('b' в ['b' ...]), которая оценивает на "А", правда с " б " в массиве.см. предыдущий ответ о том, как делать то, что вы хотите.
синтаксический анализатор Python оценил этот оператор как кортеж, где первое значение было
'a', а второе значение-это выражение'b' in ['b', 'a', 'foo', 'bar'](что означаетTrue).вы можете написать простую функцию делать то, что вы хотите, хотя:
def all_in(candidates, sequence): for element in candidates: if element not in sequence: return False return Trueи назовем это так:
>>> all_in(('a', 'b'), ['b', 'a', 'foo', 'bar']) True
оба ответа, представленные здесь, не будут обрабатывать повторяющиеся элементы. Например, если вы проверяете, является ли [1,2,2] подсписком [1,2,3,4], оба вернут True. Что может быть то, что вы хотите сделать, но я просто хотел уточнить. Если вы хотите вернуть false для [1,2,2] в [1,2,3,4], вам нужно будет отсортировать оба списка и проверить каждый элемент с движущимся индексом в каждом списке. Просто немного сложнее для цикла.
Я бы сказал, что мы можем даже оставить эти квадратные скобки.
array = ['b', 'a', 'foo', 'bar'] all([i in array for i in 'a', 'b'])ps: Я бы добавил Это в качестве комментария, но у меня недостаточно репутации для этого.
[x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]причина, по которой я думаю, что это лучше, чем выбранный ответ, заключается в том, что вам действительно не нужно вызывать функцию " all ()". Пустой список оценивается как False в операторах IF, непустой список оценивается как True.
if [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]: ...Do something...пример:
>>> [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']] ['a', 'b'] >>> [x for x in ['G','F'] if x in ['b', 'a', 'foo', 'bar']] []
как можно быть данные без лямбды! .. не стоит воспринимать это всерьез .. но этот способ тоже работает:
orig_array = [ ..... ] test_array = [ ... ] filter(lambda x:x in test_array, orig_array) == test_arrayоставьте конечную часть, если вы хотите проверить, если любой значения находятся в массиве:
filter(lambda x:x in test_array, orig_array)