Пітон: Знайдіть у списку


584

Я натрапив на це:

item = someSortOfSelection()
if item in myList:
    doMySpecialFunction(item)

але іноді це не працює з усіма моїми елементами, як ніби вони не були розпізнані в списку (коли це список рядків).

Це самий "пітонічний" спосіб пошуку предмета в списку if x in l::?


3
Це абсолютно чудово і повинно працювати, якщо предмет дорівнює одному з елементів всередині myList.
Ніклас Б.

1
ти маєш на увазі, що це був хороший спосіб робити речі? у моїх кількох випробуваннях, можливо, були пробіли та стрічкові канали intereferring ... я просто хотів бути впевненим, що це хороший спосіб здійснити "знайти у списку" (загалом)
Стефан Ролланд

Відповіді:


1173

Що стосується вашого першого питання: цей код ідеально чудовий і повинен працювати, якщо itemдорівнює одному з елементів всередині myList. Може бути , ви намагаєтеся знайти рядок , який не точно відповідати одному з пунктів або , можливо , ви використовуєте значення з плаваючою точкою , яка страждає від неточностей.

Що стосується вашого другого запитання: насправді існує кілька можливих способів, якби "знайти" речі в списках.

Перевірка, чи все є всередині

Це випадок використання, який ви описуєте: Перевірка, чи є щось у списку чи ні. Як відомо, для цього ви можете скористатися inоператором:

3 in [1, 2, 3] # => True

Фільтрування колекції

Тобто знаходження всіх елементів у послідовності, що відповідають певній умові. Для цього ви можете використовувати розуміння списку або генераторні вирази:

matches = [x for x in lst if fulfills_some_condition(x)]
matches = (x for x in lst if x > 6)

Останній поверне генератор, який ви можете уявити як своєрідний лінивий список, який буде побудований лише після того, як ви переглянете його. До речі, перший точно рівнозначний

matches = filter(fulfills_some_condition, lst)

в Python 2. Тут ви можете побачити функції вищого порядку на роботі. У Python 3 filterповертає не список, а об'єкт, подібний до генератора.

Пошук першого явища

Якщо ви хочете лише перше, що відповідає умові (але ви ще не знаєте, що це ще є), добре використовувати цикл (можливо, також за допомогою цього elseпункту, який насправді не відомий). Ви також можете використовувати

next(x for x in lst if ...)

який поверне перший матч або підніме a, StopIterationякщо жоден не знайдеться. Як варіант, ви можете використовувати

next((x for x in lst if ...), [default value])

Пошук місця розташування предмета

Для списків також існує indexметод, який іноді може бути корисним, якщо ви хочете знати, де певний елемент знаходиться у списку:

[1,2,3].index(2) # => 1
[1,2,3].index(4) # => ValueError

Однак зауважте, що якщо у вас є дублікати, .indexзавжди повертається найнижчий індекс: ......

[1,2,3,2].index(2) # => 1

Якщо є дублікати і вам потрібні всі індекси, ви можете використовувати enumerate()замість цього:

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]

10
Стефан: Дозвольте перефразувати: if x in listце не те, на що люди скаржаться, що не є вбудованою функцією. Вони скаржаться на те, що немає явного способу знайти перше виникнення чогось у списку, що відповідає певній умові. Але, як зазначено у моїй відповіді, next()можна (аб) використовувати для цього.
Ніклас Б.

3
@Stephane: Другий не генерує кортеж, а генератор (в основному це ще не створений список). Якщо ви хочете використовувати результат лише один раз, генератор, як правило, кращий. Однак якщо ви хочете кілька разів використовувати створену колекцію, бажано спершу створити явний список. Погляньте на моє оновлення, воно тепер трохи краще структуровано :)
Ніклас Б.

26
Ваш приклад "знаходження першого явища" - золотий. Відчуває себе більш пітонічним, ніж [list comprehension...][0]підхід
acjay

4
Я все більше і більше розчаровуюся в "функціональних" можливостях пітона. У модулі haskell є функція пошуку в модулі Data.List, яка робить саме це. Але в python це не так, і це мало, щоб зробити його бібліотекою, тому вам доведеться знову і знову повторювати ту саму логіку. Що марно ...
користувач1685095

3
Було б непогано, якби index()зателефонували, keyщо працює так, як keyприйнято max(); наприклад: index(list, key=is_prime).
Керт

189

Якщо ви хочете знайти один елемент або Noneвикористовувати типовий параметр у next, він не підвищуватиметься, StopIterationякщо елемент не знайдений у списку:

first_or_default = next((x for x in lst if ...), None)

1
nextприймає ітератор як перший параметр, а список / кортеж НЕ є ітератором. Так воно і повинно бути first_or_default = next(iter([x for x in lst if ...]), None)побачити docs.python.org/3/library/functions.html#next
DEVY

7
@Devy: це правильно, але (x for x in lst if ...)це генератор списку lst(який є ітератором). Якщо ви це зробите next(iter([x for x in lst if ...]), None), вам доведеться скласти список [x for x in lst if ...], що буде набагато дорожчою операцією.
Ерленд Графф

1
Тут є абстракція для визначення функції пошуку. Просто інкапсулюйте булеву експансію ifв лямбда & ви можете писати find(fn,list)зазвичай замість обдумуючого коду генератора.
семіомант

22

Хоча відповідь від Niklas B. досить вичерпна, коли ми хочемо знайти предмет у списку, іноді корисно отримати його індекс:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])

11

Пошук першого явища

Рецепт цього в itertools:

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

Наприклад, наступний код знаходить перше непарне число у списку:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3  

6

Інша альтернатива: ви можете перевірити, чи є елемент у списку if item in list:, але це порядок O (n). Якщо ви маєте справу з великими списками елементів, і все, що вам потрібно знати, - чи є щось у вашому списку, ви можете спочатку перетворити його в набір і скористатися постійним пошуком встановленого часу :

my_set = set(my_list)
if item in my_set:  # much faster on average than using a list
    # do something

У кожному випадку це не буде правильним рішенням, але в деяких випадках це може забезпечити кращі результати роботи.

Зауважте, що створення набору за допомогою set(my_list)також є O (n), тому якщо вам потрібно зробити це лише один раз, тоді це не швидше зробити це таким чином. Якщо вам потрібно неодноразово перевіряти членство, то це буде O (1) для кожного пошуку після цього початкового створення набору.


4

Ви можете скористатися одним із двох можливих пошуків під час роботи зі списком рядків:

  1. якщо елемент списку дорівнює елементу ('example' знаходиться в ['one', 'example', 'two']):

    if item in your_list: some_function_on_true()

    'ex' в ['one', 'ex', 'two'] => Істинно

    'ex_1' у ['one', 'ex', 'two'] => Неправдиво

  2. якщо елемент списку схожий на елемент ('ex' є в ['one,' example ',' two '] або' example_1 'є в [' one ',' example ',' two ']):

    matches = [el for el in your_list if item in el]

    або

    matches = [el for el in your_list if el in item]

    то просто перевірте len(matches)чи прочитайте їх у разі потреби.


3

Визначення та використання

count()метод повертає кількість елементів із заданим значенням.

Синтаксис

list.count(value)

приклад:

fruits = ['apple', 'banana', 'cherry']

x = fruits.count("cherry")

Приклад запитання:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)

2
Це ефективно в дуже довгому списку? Скажіть список мільйона?
3kstc

1
Я не впевнений !!!
Йозеф

1

Замість використання list.index(x)якого повертає індекс x, якщо він знайдений у списку, або повертає #ValueErrorповідомлення, якщо x не знайдено, ви можете використовувати list.count(x)яке повертає кількість зустрічань x у списку (перевірка того, що x справді є у списку) або це повертає 0 в іншому випадку (за відсутності x). Холодна річ у count()тому, що він не порушує ваш код і не вимагає викинути виняток, коли x не знайдено


і погано те, що він рахує елементи. Він не зупиняється, коли елемент знайдений. тому продуктивність погана у великих списках
Жан-Франсуа Фабре

1

Якщо ви збираєтесь перевірити, чи є значення в колекціонованому один раз, використовуючи оператор "in", це нормально. Однак якщо ви збираєтеся перевіряти не один раз, то рекомендую використовувати модуль бісект. Майте на увазі, що за допомогою даних бісект-модуля необхідно сортувати дані. Так ви сортуєте дані один раз, а потім можете використовувати бісект. Використання модуля бісектії на моїй машині приблизно в 12 разів швидше, ніж використання оператора "in".

Ось приклад коду з використанням синтаксису Python 3.8 та вище:

import bisect
from timeit import timeit

def bisect_search(container, value):
    return (
      (index := bisect.bisect_left(container, value)) < len(container) 
      and container[index] == value
    )

data = list(range(1000))
# value to search
true_value = 666
false_value = 66666

# times to test
ttt = 1000

print(f"{bisect_search(data, true_value)=} {bisect_search(data, false_value)=}")

t1 = timeit(lambda: true_value in data, number=ttt)
t2 = timeit(lambda: bisect_search(data, true_value), number=ttt)

print("Performance:", f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

Вихід:

bisect_search(data, true_value)=True bisect_search(data, false_value)=False
Performance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71

0

Перевірте, чи немає додаткового / небажаного білого простору в елементах списку рядків. Це причина, яка може заважати поясненню предметів не знайти.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.