Перевірте, чи є щось (не) у списку в Python


314

У мене є список кортежів у Python , і я маю умовне місце, де я хочу взяти гілку ТІЛЬКИ, якщо кортежу немає в списку (якщо він є в списку, то я не хочу брати гілку if)

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

    # Do Something

Це не дуже працює для мене. Що я зробив неправильно?


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 відгуків? Наведіть, будь ласка, мінімальний відтворюваний приклад .
Герріт

Відповіді:


502

Помилка, ймовірно, десь у вашому коді, тому що вона повинна добре працювати:

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

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
О, о, я міг би присягнути, що в Python було щось подібне __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групувати щільно до А, можна було б очікувати, що результатом буде те, для "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може використовуватися для отримання індексу елемента. Якщо цього елемента не існує, ValueErrorпіднімається a .
  • list.count можна використовувати, якщо ви хочете порахувати події.

Проблема XY: Ви вважали, що sets?

Задайте собі ці питання:

  • чи потрібно перевіряти, чи є предмет у списку більше одного разу?
  • Чи виконується ця перевірка всередині циклу чи функція, яка викликається повторно?
  • Чи є предметами, які ви зберігаєте у своєму списку, доступними? IOW, ти можеш подзвонити hashна них?

Якщо ви відповіли "так" на ці запитання, вам слід скористатись setзамість цього. inПеревірка приналежності на listе = О (п) складність. Це означає, що 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)

Нагадування, це підходящий варіант, якщо елементи, які ви зберігаєте та шукаєте, є придатними. IOW, вони повинні або бути незмінними типами, або об'єктами, які реалізують __hash__.


2
Набори - це не завжди варіант (наприклад, у списку елементів, що змінюються). Для великих колекцій: створення набору для пошуку в будь-якому разі є O (n) часом і може подвоїти використання вашої пам'яті. Якщо у вас ще немає пошуку, не завжди це найкращий вибір зробити / підтримувати.
Вім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.