Bu, istediğinizi yapar ve neredeyse her durumda işe yarar:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
'a','b' in ['b', 'a', 'foo', 'bar']Python bunu bir demet olarak yorumladığı için ifade beklendiği gibi çalışmıyor:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Diğer seçenekler
Bu testi yürütmenin başka yolları da vardır, ancak bunlar pek çok farklı türde girdi için işe yaramayacaktır. Kabie'nin işaret ettiği gibi , bu sorunu setleri kullanarak çözebilirsiniz ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...ara sıra:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Kümeler yalnızca hashable öğelerle oluşturulabilir. Ancak oluşturucu ifadesi all(x in container for x in items)hemen hemen her tür kap türünü işleyebilir. Tek gereksinim, containeryeniden yinelenebilir olmasıdır (yani bir jeneratör değil). itemsherhangi bir yinelenebilir olabilir.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
Hız Testleri
Çoğu durumda, alt küme testi bundan daha hızlı olacaktır all, ancak fark şok edici değildir - sorunun alakasız olduğu durumlar hariç, çünkü kümeler bir seçenek değildir. Listeleri sırf böyle bir test amacıyla kümelere dönüştürmek her zaman zahmete değmeyecektir. Ve jeneratörleri setlere dönüştürmek bazen inanılmaz derecede savurgan olabilir ve programları birçok büyüklükte yavaşlatır.
İşte gösterim için birkaç kriter. En büyük fark, her ikisi de containerve itemsnispeten küçük olduğunda ortaya çıkar . Bu durumda, alt küme yaklaşımı bir kat daha hızlıdır:
>>> 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)
Bu büyük bir fark gibi görünüyor. Ancak containerbir set olduğu sürece all, çok daha büyük ölçeklerde hala mükemmel bir şekilde kullanılabilir:
>>> 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)
Alt küme testini kullanmak hala daha hızlı, ancak bu ölçekte yalnızca yaklaşık 5 kat. Hız artışı, Python'un hızlı cdestekli uygulamasından kaynaklanmaktadır set, ancak temel algoritma her iki durumda da aynıdır.
Eğer senin itemszaten başka nedenlerden dolayı bir listede saklanır, o zaman alt kümesi testi yaklaşımı kullanmadan önce bir dizi dönüştürmek gerekir. Ardından hızlanma yaklaşık 2,5x'e düşer:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Ve eğer sizin containerdiziniz bir diziyse ve önce dönüştürülmesi gerekiyorsa, hızlanma daha da küçüktür:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Son derece yavaş sonuçlar aldığımız tek zaman container, bir dizi olarak ayrıldığımız zamandır :
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Ve tabii ki, bunu sadece mecbur kalırsak yapacağız. bigseqİçindeki tüm öğeler karma hale getirilebilirse, bunun yerine şunu yapacağız:
>>> %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)
Bu, alternatiften sadece 1.66 kat daha hızlıdır ( set(bigseq) >= set(bigsubseq)yukarıda 4.36'da zamanlanmıştır).
Bu nedenle, alt küme testi genellikle daha hızlıdır, ancak inanılmaz bir farkla değil. Öte yandan, ne zaman alldaha hızlı olduğuna bakalım . Ya itemson milyon değer uzunluğundaysa ve içinde olmayan değerlere sahip olma ihtimali varsa 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)
Jeneratörü bir sete dönüştürmek bu durumda inanılmaz derecede israfa dönüşüyor. setYapıcı tüm jeneratör tüketmek zorundadır. Ancak kısa devre davranışı all, jeneratörün yalnızca küçük bir kısmının tüketilmesi gerektiğini garanti eder, bu nedenle, bir alt küme testinden dört kat daha hızlıdır .
Kuşkusuz bu aşırı bir örnek. Ancak görüldüğü gibi, her durumda bir yaklaşımın veya diğerinin daha hızlı olacağını varsayamazsınız.
Upshot
Çoğu zaman, containerbir kümeye dönüştürmek , en azından tüm öğeleri karma hale getirilebilirse, buna değer. Bunun nedeni inkümeler için O (1), indiziler için ise O (n) olmasıdır.
Öte yandan, alt küme testini kullanmak muhtemelen sadece bazen buna değer. Test öğeleriniz zaten bir sette saklanmışsa kesinlikle yapın. Aksi takdirde, allyalnızca biraz daha yavaştır ve herhangi bir ek depolama alanı gerektirmez. Aynı zamanda büyük eşya üreticileri ile de kullanılabilir ve bu durumda bazen büyük bir hızlanma sağlar.
set(['a', 'b']) <= set(['b','a','foo','bar'])aynı şeyi hecelemenin başka bir yolu ve daha "matematiksel" görünüyor.