Python: Temukan dalam daftar

586

Saya telah menemukan ini:

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

tetapi kadang-kadang itu tidak bekerja dengan semua item saya, seolah-olah mereka tidak dikenali dalam daftar (ketika itu adalah daftar string).

Apakah ini cara yang paling 'pythonic' untuk menemukan item dalam daftar if x in l::?

Stephane Rolland
sumber
3
Itu sangat baik dan harus bekerja jika item sama dengan salah satu elemen di dalamnya myList.
Niklas B.
1
maksud Anda itu cara yang baik untuk melakukan sesuatu? dalam beberapa percobaan saya, mungkin ada spasi putih, dan umpan baris mengganggu ... saya hanya ingin memastikan itu adalah cara yang baik untuk menerapkan "temukan dalam daftar" (secara umum)
Stephane Rolland

Jawaban:

1174

Adapun pertanyaan pertama Anda: kode itu baik-baik saja dan harus berfungsi jika itemsama dengan salah satu elemen di dalamnya myList. Mungkin Anda mencoba menemukan string yang tidak sama persis dengan salah satu item atau mungkin Anda menggunakan nilai float yang menderita ketidaktepatan.

Adapun pertanyaan kedua Anda: Sebenarnya ada beberapa cara yang mungkin jika "menemukan" hal-hal dalam daftar.

Memeriksa apakah ada sesuatu di dalam

Ini adalah use case yang Anda gambarkan: Memeriksa apakah ada sesuatu di dalam daftar atau tidak. Seperti yang Anda ketahui, Anda dapat menggunakan inoperator untuk itu:

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

Memfilter koleksi

Artinya, menemukan semua elemen dalam urutan yang memenuhi kondisi tertentu. Anda dapat menggunakan daftar pemahaman atau ekspresi generator untuk itu:

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

Yang terakhir akan mengembalikan generator yang dapat Anda bayangkan sebagai semacam daftar malas yang hanya akan dibangun segera setelah Anda mengulanginya. Omong-omong, yang pertama persis sama dengan

matches = filter(fulfills_some_condition, lst)

di Python 2. Di sini Anda dapat melihat fungsi tingkat tinggi di tempat kerja. Dalam Python 3, filtertidak mengembalikan daftar, tetapi objek seperti generator.

Menemukan kejadian pertama

Jika Anda hanya menginginkan hal pertama yang cocok dengan suatu kondisi (tetapi Anda belum tahu apa itu), boleh saja menggunakan for for (mungkin menggunakan elseklausa juga, yang sebenarnya tidak terlalu terkenal). Anda juga bisa menggunakan

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

yang akan mengembalikan pertandingan pertama atau menaikkan StopIterationjika tidak ada yang ditemukan. Atau, Anda dapat menggunakan

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

Menemukan lokasi suatu barang

Untuk daftar, ada juga indexmetode yang kadang-kadang bisa berguna jika Anda ingin tahu di mana elemen tertentu ada dalam daftar:

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

Namun, perhatikan bahwa jika Anda memiliki duplikat, .indexselalu kembalikan indeks terendah: ......

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

Jika ada duplikat dan Anda ingin semua indeks maka Anda dapat menggunakannya enumerate():

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]
Niklas B.
sumber
10
Stephane: Biarkan saya ulangi itu: if x in listadalah tidak hal yang orang-orang mengeluh tidak menjadi built-in fungsi. Mereka mengeluh tentang fakta bahwa tidak ada cara eksplisit untuk menemukan kemunculan pertama sesuatu dalam daftar yang cocok dengan kondisi tertentu. Tetapi seperti yang dinyatakan dalam jawaban saya, next()dapat (ab) digunakan untuk itu.
Niklas B.
3
@Stephane: Yang kedua tidak menghasilkan tupel, tetapi generator (yang pada dasarnya adalah daftar yang belum dibangun). Jika Anda ingin menggunakan hasilnya hanya sekali, generator biasanya lebih disukai. Namun, jika Anda ingin menggunakan koleksi yang dibuat beberapa kali sesudahnya, disarankan untuk membuat daftar eksplisit di tempat pertama. Lihat pembaruan saya, sekarang terstruktur lebih baik :)
Niklas B.
26
Contoh "menemukan kejadian pertama" Anda berwarna emas. Terasa lebih pythonic daripada [list comprehension...][0]pendekatan
acjay
4
Saya semakin dissiapointed dengan kemampuan 'fungsional' python. Di haskell terdapat fungsi dalam modul Data.List yang melakukan hal itu. Tetapi dalam python itu tidak dan itu kecil untuk menjadikannya perpustakaan sehingga Anda harus menerapkan kembali logika yang sama berulang-ulang.
Sayang sekali
3
Alangkah baiknya jika ada kwarg untuk index()menelepon keyyang berfungsi seperti yang keyditerima oleh max(); misalnya: index(list, key=is_prime).
Curt
189

Jika Anda ingin menemukan satu elemen atau Nonemenggunakan bawaan next, itu tidak akan naik StopIterationjika item tidak ditemukan dalam daftar:

first_or_default = next((x for x in lst if ...), None)
Janusz Skonieczny
sumber
1
nextmengambil iterator sebagai parameter pertama dan daftar / tuple BUKAN iterator. Jadi harus first_or_default = next(iter([x for x in lst if ...]), None)melihat docs.python.org/3/library/functions.html#next
Devy
7
@ Evy: itu benar, tetapi (x for x in lst if ...)generator atas daftar lst(yang merupakan iterator). Jika ya next(iter([x for x in lst if ...]), None), Anda harus membuat daftar [x for x in lst if ...], yang akan menjadi operasi yang jauh lebih mahal.
Erlend Graff
1
Ada abstraksi di sini untuk mendefinisikan fungsi find. Cukup enkapsulasi expresi boolean dari ifdalam lambda & Anda dapat menulis find(fn,list)biasanya alih-alih mengaburkan kode generator.
semiomant
22

Meskipun jawaban dari Niklas B. cukup komprehensif, ketika kami ingin menemukan item dalam daftar, terkadang berguna untuk mendapatkan indeksnya:

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

Menemukan kejadian pertama

Ada resep untuk itu di 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)

Misalnya, kode berikut ini menemukan nomor ganjil pertama dalam daftar:

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

Alternatif lain: Anda dapat memeriksa apakah suatu item ada dalam daftar if item in list:, tetapi ini adalah pesanan O (n). Jika Anda berurusan dengan daftar besar item dan yang perlu Anda ketahui adalah apakah sesuatu adalah anggota daftar Anda, Anda dapat mengonversi daftar menjadi satu set pertama dan mengambil keuntungan dari pencarian waktu yang konstan :

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

Tidak akan menjadi solusi yang tepat dalam setiap kasus, tetapi untuk beberapa kasus ini mungkin memberi Anda kinerja yang lebih baik.

Perhatikan bahwa membuat set dengan set(my_list)juga O (n), jadi jika Anda hanya perlu melakukan ini sekali maka tidak lebih cepat untuk melakukannya dengan cara ini. Jika Anda perlu memeriksa keanggotaan berulang kali, maka ini akan menjadi O (1) untuk setiap pencarian setelah pembuatan set awal.

Engineero
sumber
4

Anda mungkin ingin menggunakan salah satu dari dua pencarian yang mungkin saat bekerja dengan daftar string:

  1. jika elemen daftar sama dengan item ('contoh' ada di ['satu', 'contoh', 'dua']):

    if item in your_list: some_function_on_true()

    'ex' di ['one', 'ex', 'two'] => Benar

    'ex_1' di ['one', 'ex', 'two'] => Salah

  2. jika elemen daftar seperti item ('ex' ada di ['satu,' contoh ',' dua '] atau' example_1 'ada di [' satu ',' contoh ',' dua ']):

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

    atau

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

    maka cukup periksa len(matches)atau baca jika perlu.

Alexey Antonenko
sumber
3

Definisi dan Penggunaan

yang count()metode mengembalikan jumlah elemen dengan nilai yang ditentukan.

Sintaksis

list.count(value)

contoh:

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

x = fruits.count("cherry")

Contoh pertanyaan:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)
Josef
sumber
2
Apakah ini efisien dalam daftar yang sangat panjang? Katakan daftar sejuta?
3kstc
1
Saya tidak yakin !!!
josef
1

Alih-alih menggunakan list.index(x)yang mengembalikan indeks x jika ditemukan dalam daftar atau mengembalikan #ValueErrorpesan jika x tidak ditemukan, Anda bisa menggunakan list.count(x)yang mengembalikan jumlah kemunculan x dalam daftar (validasi bahwa x memang ada dalam daftar) atau mengembalikan 0 sebaliknya (dengan tidak adanya x). Yang keren tentang itu count()adalah tidak merusak kode Anda atau mengharuskan Anda untuk melemparkan pengecualian ketika x tidak ditemukan

Taylor
sumber
dan yang buruk adalah ia menghitung elemen. Itu tidak berhenti ketika elemen ditemukan. jadi kinerjanya buruk pada daftar besar
Jean-François Fabre
1

Jika Anda akan memeriksa apakah nilai ada dalam koleksi sekali maka menggunakan operator 'dalam' baik-baik saja. Namun, jika Anda akan memeriksa lebih dari sekali maka saya sarankan menggunakan modul bisect. Perlu diingat bahwa menggunakan data modul dua bagian harus diurutkan. Jadi Anda mengurutkan data sekali dan kemudian Anda dapat menggunakan dua bagian. Menggunakan modul bisect pada mesin saya sekitar 12 kali lebih cepat daripada menggunakan operator 'dalam'.

Berikut adalah contoh kode menggunakan sintaksis Python 3.8 dan di atas:

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}")

Keluaran:

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

Periksa tidak ada ruang putih tambahan / yang tidak diinginkan dalam item dari daftar string. Itu alasan yang dapat mengganggu menjelaskan item tidak dapat ditemukan.

Stephane Rolland
sumber