Проверьте, есть ли что-то (нет) в списке в Python

315

У меня есть список кортежей в Python , и у меня есть условие, где я хочу взять ветвь ТОЛЬКО если кортежа нет в списке (если он в списке, то я не хочу брать ветку if)

if curr_x -1 > 0 and (curr_x-1 , curr_y) not in myList: 

    # Do Something

Это на самом деле не работает для меня, хотя. Что я сделал не так?

Zack
источник
1
Обратите внимание , что 3 -1 > 0 and (4-1 , 5) not in []Trueпоэтому ошибка не является одним из приоритетов операторов.
Дэн Д.
6
Что ты имеешь в виду под "не совсем работает для меня"? Что вы ожидаете случиться? Что на самом деле происходит? Какой точный список содержимого вызывает проблему?
Карл Кнехтель
Почему бы не попробовать myList.count((curr_x, curr_y)), если (curr_x, curr_y)не в myList, результат будет0
LittleLittleQ
2
Как этот вопрос «мой код на самом деле не работает для меня» получил 297 голосов? Пожалуйста, дайте нам минимальный воспроизводимый пример .
Gerrit

Ответы:

503

Ошибка, вероятно, где-то еще в вашем коде, потому что она должна работать нормально:

>>> 3 not in [2, 3, 4]
False
>>> 3 not in [4, 5, 6]
True

Или с кортежами:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True
orlp
источник
11
@Zack: если бы вы не знали об этом, вы могли бы просто сделатьif not ELEMENT in COLLECTION:
ninjagecko
@ninjagecko: в зависимости от типа контейнера, который может быть менее эффективным или даже неправильным. Смотрите, например, фильтры Блума .
orlp
14
@ nightcracker Это не имеет смысла, поскольку A not in Bсводится к тому, что делать, not B.__contains__(A)то же самое, что not A in Bсводится к тому, что есть not B.__contains__(A).
Дэн Д.
1
Ого, я мог бы поклясться, что у Питона было что-то подобное __notcontains__. Извините, тогда то, что я сказал, это просто фигня.
orlp
2
@ std''OrgnlDave Единственный способ, которым это может случиться, - это notиметь более высокий приоритет, чем тот, inкоторого нет. Рассмотрим результат, ast.dump(ast.parse("not A in B").body[0])который приводит к тому, что "Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))"если notсгруппировать тесно в A, можно было бы ожидать, что результатом будет "Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))"синтаксический анализ "(not A) in B".
Дэн Д.
20

Как проверить, есть ли что-то (нет) в списке в Python?

Самое дешевое и удобочитаемое решение - использование inоператора (или в вашем конкретном случае not in). Как указано в документации,

Операторы inи not inтест на членство. x in sоценивается, Trueесли xявляется членом s, и в Falseпротивном случае. x not in sвозвращает отрицание x in s.

Дополнительно,

Оператор not inопределен, чтобы иметь обратное истинное значение in.

y not in xлогически так же, как not y in x.

Вот несколько примеров:

'a' in [1, 2, 3]
# False

'c' in ['a', 'b', 'c']
# True

'a' not in [1, 2, 3]
# True

'c' not in ['a', 'b', 'c']
# False

Это также работает с кортежами, так как кортежи являются хэшируемыми (вследствие того, что они также являются неизменяемыми):

(1, 2) in [(3, 4), (1, 2)]
#  True

Если объект в RHS определяет __contains__()метод, он inбудет внутренне вызывать его, как указано в последнем абзаце раздела « Сравнения » документации.

... inи not inподдерживаются типами, которые являются итеративными или реализуют __contains__()метод. Например, вы можете (но не должны) сделать это:

[3, 2, 1].__contains__(1)
# True

inкороткие замыкания, поэтому, если ваш элемент находится в начале списка, inвычисляется быстрее:

lst = list(range(10001))
%timeit 1 in lst
%timeit 10000 in lst  # Expected to take longer time.

68.9 ns ± 0.613 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
178 µs ± 5.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Если вы хотите сделать больше, чем просто проверить, есть ли элемент в списке, есть варианты:

  • list.indexможет быть использован для получения индекса элемента. Если этот элемент не существует, a ValueErrorповышается.
  • list.count может быть использован, если вы хотите подсчитать вхождения.

Проблема XY: Вы рассматривали setс?

Задайте себе эти вопросы:

  • Вам нужно проверить, есть ли элемент в списке более одного раза?
  • Эта проверка выполняется внутри цикла или функция вызывается повторно?
  • Элементы, которые вы храните в своем списке, можно изменить? IOW, вы можете позвонить hashим?

Если вы ответили «да» на эти вопросы, вы должны использовать setвместо этого. Проверка inчленства на lists - это O (n) сложность времени. Это означает, что python должен выполнить линейное сканирование вашего списка, посещая каждый элемент и сравнивая его с элементом поиска. Если вы делаете это неоднократно или если списки велики, эта операция повлечет за собой дополнительные затраты.

setобъекты, с другой стороны, хэшируют свои значения для проверки членства в постоянное время. Проверка также выполняется с использованием in:

1 in {1, 2, 3} 
# True

'a' not in {'a', 'b', 'c'}
# False

(1, 2) in {('a', 'c'), (1, 2)}
# True

Если вам не повезло, что элемент, который вы ищете / не ищете, находится в конце вашего списка, python просканирует список до конца. Это видно из времени ниже:

l = list(range(100001))
s = set(l)

%timeit 100000 in l
%timeit 100000 in s

2.58 ms ± 58.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
101 ns ± 9.53 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Напоминаем, что это подходящий вариант, если элементы, которые вы храните и просматриваете, являются хэшируемыми. Таким образом, они должны быть неизменяемыми типами или объектами, которые реализуют __hash__.

cs95
источник
2
Наборы не всегда являются опцией (например, при наличии списка изменяемых элементов). Для больших коллекций: создание набора для поиска в любом случае занимает O (n) время и может удвоить использование вашей памяти. Если у вас еще нет поиска, это не всегда лучший выбор для создания / поддержки.
Вим