Sprawdź, czy coś jest (nie) na liście w Pythonie

314

Mam listę krotek w Pythonie i mam warunek, w którym chcę wziąć gałąź TYLKO, jeśli krotki nie ma na liście (jeśli jest na liście, to nie chcę brać gałęzi if)

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

    # Do Something

Jednak tak naprawdę to dla mnie nie działa. Co zrobiłem źle?

Zack
źródło
1
Zauważ, że 3 -1 > 0 and (4-1 , 5) not in []Truedlatego błąd nie jest priorytetem operatora.
Dan D.
6
Co masz na myśli mówiąc „tak naprawdę dla mnie nie działa”? Czego się spodziewasz Co się właściwie dzieje? Jaka dokładnie zawartość listy powoduje problem?
Karl Knechtel
Dlaczego nie spróbować myList.count((curr_x, curr_y)), jeśli go (curr_x, curr_y)nie ma myList, wynik będzie0
LittleLittleQ
2
W jaki sposób pytanie „mój kod naprawdę nie działa dla mnie” uzyskało 297 głosów pozytywnych? Podaj nam minimalny, powtarzalny przykład .
gerrit

Odpowiedzi:

502

Błąd jest prawdopodobnie gdzie indziej w twoim kodzie, ponieważ powinien działać dobrze:

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

Lub z krotkami:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True
orlp
źródło
11
@Zack: gdybyś nie wiedział o tym, mógłbyś to zrobićif not ELEMENT in COLLECTION:
ninjagecko
@ ninjagecko: w zależności od typu pojemnika, który może być mniej wydajny lub nawet nieprawidłowy. Zobacz na przykład filtry Bloom .
orlp
14
@ nightcracker To nie ma sensu, ponieważ A not in Bsprowadza się do robienia not B.__contains__(A)tego, co not A in Bjest zredukowane do tego, co jest not B.__contains__(A).
Dan D.
1
Och wow, mógłbym przysiąc, że Python miał coś takiego __notcontains__. Przepraszam, to co powiedziałem, to bzdury.
lub
2
@ std''OrgnlDave Jedyny sposób, który może się zdarzyć, to notmieć wyższy priorytet niż ten, inktóry nie ma. Zastanów się, ast.dump(ast.parse("not A in B").body[0])którego rezultatem w "Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))"przypadku notścisłego pogrupowania do A można się spodziewać, że wynik będzie "Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))"analizowany "(not A) in B".
Dan D.
20

Jak sprawdzić, czy coś jest (nie) na liście w Pythonie?

Najtańszym i najbardziej czytelnym rozwiązaniem jest użycie inoperatora (lub w konkretnym przypadku not in). Jak wspomniano w dokumentacji,

Operatorzy ini not intest na członkostwo. x in socenia, Trueczy xjest członkiem si w Falseinny sposób. x not in szwraca negację x in s.

Dodatkowo,

Operator not inma zdefiniowaną odwrotną wartość rzeczywistą wynoszącą in.

y not in xjest logicznie taki sam jak not y in x.

Oto kilka przykładów:

'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

Działa to również z krotkami, ponieważ krotki są haszowalne (w wyniku tego, że są również niezmienne):

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

Jeśli obiekt w RHS definiuje __contains__()metodę, inwywoła ją wewnętrznie, jak zauważono w ostatnim akapicie rozdziału Porównania dokumentów.

... ini not insą obsługiwane przez typy, które są iterowalne lub implementują __contains__()metodę. Na przykład możesz (ale nie powinieneś) to zrobić:

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

inzwiera, więc jeśli twój element znajduje się na początku listy, inocenia szybciej:

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)

Jeśli chcesz zrobić coś więcej niż tylko sprawdzić, czy element znajduje się na liście, istnieją opcje:

  • list.indexmożna użyć do pobrania indeksu elementu. Jeśli ten element nie istnieje, powstaje a ValueError.
  • list.count można użyć, jeśli chcesz policzyć zdarzenia.

Problem XY: Czy rozważałeś sets?

Zadaj sobie następujące pytania:

  • czy musisz sprawdzić, czy element jest na liście więcej niż jeden raz?
  • Czy to sprawdzenie jest wykonywane wewnątrz pętli lub funkcji wywoływanej wielokrotnie?
  • Czy elementy, które przechowujesz na liście, można przechować? IOW, możesz do hashnich zadzwonić ?

Jeśli odpowiedziałeś „tak” na te pytania, powinieneś użyć setzamiast tego. inTest członków o lists O (n) Złożoność. Oznacza to, że python musi wykonać liniowy skan listy, odwiedzając każdy element i porównując go z wyszukiwanym elementem. Jeśli robisz to wielokrotnie lub jeśli listy są duże, ta operacja spowoduje narzut.

setobiekty, z drugiej strony, mieszają swoje wartości dla stałej kontroli członkostwa w czasie. Sprawdzanie odbywa się również przy użyciu in:

1 in {1, 2, 3} 
# True

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

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

Jeśli na tyle nieszczęście, że element, którego szukasz / nie szukasz, znajduje się na końcu listy, python przeskanuje listę do końca. Jest to widoczne z poniższych czasów:

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)

Przypominamy, że jest to odpowiednia opcja, o ile elementy, które przechowujesz i przeglądasz, są haszowalne. IOW musiałyby to być niezmienne typy lub obiekty, które implementują __hash__.

cs95
źródło
2
Zestawy nie zawsze są opcją (na przykład, gdy ma się listę zmiennych elementów). W przypadku dużych kolekcji: zbudowanie zestawu do wyszukiwania jest czasem O (n) i może podwoić zużycie pamięci. Jeśli jeszcze nie rozglądasz się, nie zawsze jest to najlepszy wybór, aby je wykonać / utrzymać.
wim