Periksa apakah ada sesuatu (tidak) dalam daftar dengan Python

314

Saya memiliki daftar tupel dengan Python , dan saya memiliki persyaratan di mana saya ingin mengambil cabang HANYA jika tupel tidak ada dalam daftar (jika ada dalam daftar, maka saya tidak ingin mengambil cabang if)

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

    # Do Something

Ini tidak benar-benar bekerja untuk saya. Apa yang telah saya lakukan salah?

Zack
sumber
1
Perhatikan bahwa 3 -1 > 0 and (4-1 , 5) not in []Truekarena itu kesalahannya bukan merupakan prioritas operator.
Dan D.
6
Apa yang Anda maksud dengan "tidak benar-benar bekerja untuk saya"? Apa yang Anda harapkan terjadi? Apa yang sebenarnya terjadi? Apa isi daftar persis yang memicu masalah?
Karl Knechtel
Mengapa tidak mencoba myList.count((curr_x, curr_y)), jika (curr_x, curr_y)tidak ada myList, hasilnya akan0
LittleLittleQ
2
Bagaimana pertanyaan "kode saya tidak benar-benar berfungsi untuk saya" mendapatkan 297 upvotes? Tolong beri kami contoh minimal yang dapat direproduksi .
gerrit

Jawaban:

503

Bug itu mungkin ada di tempat lain dalam kode Anda, karena itu akan berfungsi dengan baik:

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

Atau dengan tupel:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True
orlp
sumber
11
@Zack: jika Anda tidak tahu tentang ini, Anda bisa melakukannyaif not ELEMENT in COLLECTION:
ninjagecko
@ninjagecko: tergantung pada jenis wadah yang bisa kurang efisien, atau bahkan salah. Lihat misalnya filter mekar .
orlp
14
@ nightcracker Tidak masuk akal karena A not in Bdireduksi menjadi melakukan not B.__contains__(A)yang sama dengan apa not A in Byang direduksi menjadi apa not B.__contains__(A).
Dan D.
1
Oh wow, aku berani bersumpah Python punya sesuatu seperti itu __notcontains__. Maaf, lalu yang saya katakan hanyalah omong kosong.
orlp
2
@ std''OrgnlDave Satu-satunya cara yang bisa terjadi adalah jika notmemiliki prioritas lebih tinggi daripada inyang tidak. Pertimbangkan hasil ast.dump(ast.parse("not A in B").body[0])yang menghasilkan "Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))"Jika notdikelompokkan erat ke A, orang akan mengharapkan hasil "Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))"yang merupakan parse untuk "(not A) in B".
Dan D.
20

Bagaimana cara saya memeriksa apakah ada sesuatu (tidak) dalam daftar dengan Python?

Solusi termurah dan paling mudah dibaca adalah menggunakan inoperator (atau dalam kasus khusus Anda, not in). Seperti disebutkan dalam dokumentasi,

Operator indan not inuji keanggotaan. x in smengevaluasi Trueapakah xadalah anggota s, dan Falsesebaliknya. x not in smengembalikan negasi dari x in s.

Selain itu,

Operator not indidefinisikan memiliki nilai true kebalikan dari in.

y not in xsecara logis sama dengan not y in x.

Berikut ini beberapa contoh:

'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

Ini juga berfungsi dengan tupel, karena tupel dapat hashable (sebagai konsekuensi dari fakta bahwa tuple juga tidak berubah):

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

Jika objek pada RHS mendefinisikan suatu __contains__()metode, insecara internal akan memanggilnya, seperti yang disebutkan dalam paragraf terakhir dari bagian Perbandingan dari dokumen.

... indan not in, didukung oleh tipe yang dapat diterapkan atau mengimplementasikan __contains__()metode. Misalnya, Anda dapat (tetapi tidak seharusnya) melakukan ini:

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

insirkuit pendek, jadi jika elemen Anda berada di awal daftar, inevaluasi lebih cepat:

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)

Jika Anda ingin melakukan lebih dari sekadar memeriksa apakah suatu item ada dalam daftar, ada beberapa opsi:

  • list.indexdapat digunakan untuk mengambil indeks suatu item. Jika elemen itu tidak ada, a ValueErrordinaikkan.
  • list.count dapat digunakan jika Anda ingin menghitung kejadian.

Masalah XY: Sudahkah Anda mempertimbangkan sets?

Tanyakan pada diri Anda pertanyaan-pertanyaan ini:

  • apakah Anda perlu memeriksa apakah suatu item ada dalam daftar lebih dari satu kali?
  • Apakah pemeriksaan ini dilakukan di dalam loop, atau fungsi yang dipanggil berulang kali?
  • Apakah barang-barang yang Anda simpan di daftar Anda bisa dipecahkan? TKI, bisakah Anda memanggil hashmereka?

Jika Anda menjawab "ya" untuk pertanyaan ini, Anda seharusnya menggunakan kata setganti. Tes inkeanggotaan pada lists adalah O (n) kompleksitas waktu. Ini berarti bahwa python harus melakukan pemindaian linear daftar Anda, mengunjungi setiap elemen dan membandingkannya dengan item pencarian. Jika Anda melakukan ini berulang kali, atau jika daftarnya besar, operasi ini akan menimbulkan biaya tambahan.

setobjek, di sisi lain, hash nilainya untuk cek keanggotaan waktu konstan. Pemeriksaan juga dilakukan dengan menggunakan in:

1 in {1, 2, 3} 
# True

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

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

Jika Anda cukup disayangkan bahwa elemen yang Anda cari / tidak cari ada di akhir daftar Anda, python akan memindai daftar hingga akhir. Ini terbukti dari timing di bawah ini:

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)

Sebagai pengingat, ini adalah opsi yang cocok selama elemen yang Anda simpan dan cari dapat di-hashable. TKI, mereka harus berupa tipe yang tidak berubah, atau objek yang mengimplementasikan __hash__.

cs95
sumber
2
Set tidak selalu merupakan opsi (misalnya, ketika memiliki daftar item yang bisa diubah). Untuk koleksi besar: membangun set untuk pencarian adalah O (n) dan dapat menggandakan penggunaan memori Anda. Jika Anda belum melihat-lihat, itu tidak selalu merupakan pilihan terbaik untuk membuat / mempertahankannya.
wim