Ini melakukan apa yang Anda inginkan, dan akan berhasil di hampir semua kasus:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
Ekspresi 'a','b' in ['b', 'a', 'foo', 'bar']tidak berfungsi seperti yang diharapkan karena Python menafsirkannya sebagai tupel:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Pilihan lain
Ada cara lain untuk menjalankan pengujian ini, tetapi cara tersebut tidak akan berfungsi untuk berbagai jenis masukan. Seperti yang dikatakan Kabie , Anda dapat menyelesaikan masalah ini menggunakan set ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...terkadang:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Set hanya dapat dibuat dengan elemen yang dapat dicirikan. Tetapi ekspresi generator all(x in container for x in items)dapat menangani hampir semua jenis penampung. Satu-satunya persyaratan adalah yang containerdapat diulang (yaitu bukan generator). itemsbisa menjadi iterable sama sekali.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
Tes Kecepatan
Dalam banyak kasus, pengujian subset akan lebih cepat daripada all, tetapi perbedaannya tidak mengejutkan - kecuali jika pertanyaannya tidak relevan karena rangkaian bukanlah pilihan. Mengubah daftar menjadi set hanya untuk tujuan pengujian seperti ini tidak selalu sepadan dengan masalahnya. Dan mengubah generator menjadi set terkadang bisa sangat boros, memperlambat program dengan banyak kali lipat.
Berikut adalah beberapa tolok ukur untuk ilustrasi. Perbedaan terbesar muncul jika keduanya containerdan itemsrelatif kecil. Dalam hal ini, pendekatan subset adalah tentang urutan besarnya lebih cepat:
>>> smallset = set(range(10))
>>> smallsubset = set(range(5))
>>> %timeit smallset >= smallsubset
110 ns ± 0.702 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit all(x in smallset for x in smallsubset)
951 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Ini terlihat seperti perbedaan besar. Tapi selama containersatu set, allmasih bisa digunakan dengan sempurna pada skala yang jauh lebih besar:
>>> bigset = set(range(100000))
>>> bigsubset = set(range(50000))
>>> %timeit bigset >= bigsubset
1.14 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit all(x in bigset for x in bigsubset)
5.96 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Menggunakan pengujian subset masih lebih cepat, tetapi hanya sekitar 5x pada skala ini. Peningkatan kecepatan ini disebabkan oleh cimplementasi fast- backed Python set, tetapi algoritme dasarnya sama dalam kedua kasus.
Jika Anda itemssudah disimpan dalam daftar karena alasan lain, maka Anda harus mengubahnya menjadi satu set sebelum menggunakan pendekatan pengujian subset. Kemudian speedup turun menjadi sekitar 2,5x:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Dan jika Anda containeradalah urutan, dan perlu dikonversi terlebih dahulu, maka percepatannya bahkan lebih kecil:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Satu-satunya saat kita mendapatkan hasil yang sangat lambat adalah ketika kita pergi containersecara berurutan:
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Dan tentu saja, kami hanya akan melakukannya jika kami harus. Jika semua item di bigseqhashable, maka kami akan melakukan ini sebagai gantinya:
>>> %timeit bigset = set(bigseq); all(x in bigset for x in bigsubseq)
7.24 ms ± 78 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Itu hanya 1,66x lebih cepat dari alternatif ( set(bigseq) >= set(bigsubseq), waktunya di atas pada 4,36).
Jadi pengujian subset umumnya lebih cepat, tetapi tidak dengan margin yang luar biasa. Di sisi lain, mari kita lihat kapan alllebih cepat. Bagaimana jika itemssepuluh juta nilai panjangnya, dan cenderung memiliki nilai yang tidak ada container?
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); set(bigset) >= set(hugeiter)
13.1 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); all(x in bigset for x in hugeiter)
2.33 ms ± 65.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Mengubah generator menjadi satu set ternyata sangat boros dalam hal ini. The setkonstruktor harus mengkonsumsi seluruh pembangkit. Tetapi perilaku hubung singkat allmemastikan bahwa hanya sebagian kecil dari generator yang perlu dikonsumsi, jadi ini lebih cepat daripada pengujian subset dengan empat kali lipat .
Ini adalah contoh ekstrim, harus diakui. Tetapi seperti yang ditunjukkan, Anda tidak dapat berasumsi bahwa satu pendekatan atau yang lain akan lebih cepat dalam semua kasus.
Hasilnya
Sebagian besar waktu, mengonversi containerke satu set sangat berharga, setidaknya jika semua elemennya dapat di-hash. Itu karena inuntuk himpunan adalah O (1), sedangkan inuntuk sekuens adalah O (n).
Di sisi lain, menggunakan pengujian subset mungkin hanya sesekali bermanfaat. Pasti lakukan jika item pengujian Anda sudah disimpan dalam satu set. Jika tidak, allhanya sedikit lebih lambat, dan tidak memerlukan penyimpanan tambahan. Ini juga dapat digunakan dengan generator item yang besar, dan terkadang memberikan percepatan besar dalam kasus itu.
set(['a', 'b']) <= set(['b','a','foo','bar'])adalah cara lain untuk mengeja hal yang sama, dan terlihat "lebih matematis".