Risposte:
Questo fa quello che vuoi e funzionerà in quasi tutti i casi:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
L'espressione 'a','b' in ['b', 'a', 'foo', 'bar']non funziona come previsto perché Python la interpreta come una tupla:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Ci sono altri modi per eseguire questo test, ma non funzioneranno per tanti tipi diversi di input. Come sottolinea Kabie , puoi risolvere questo problema usando i set ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...qualche volta:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
I set possono essere creati solo con elementi hashable. Ma l'espressione del generatore all(x in container for x in items)può gestire quasi tutti i tipi di contenitori. L'unico requisito è che containersia reiterabile (cioè non un generatore). itemspuò essere qualsiasi iterabile.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
In molti casi, il test del sottoinsieme sarà più veloce di all, ma la differenza non è scioccante, tranne quando la domanda è irrilevante perché i set non sono un'opzione. Convertire elenchi in set solo ai fini di un test come questo non sempre varrà la pena. E convertire i generatori in set a volte può essere incredibilmente dispendioso, rallentando i programmi di molti ordini di grandezza.
Ecco alcuni punti di riferimento per l'illustrazione. La differenza più grande si ha quando entrambi containere itemssono relativamente piccoli. In tal caso, l'approccio del sottoinsieme è di circa un ordine di grandezza più veloce:
>>> 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)
Questa sembra una grande differenza. Ma finché containerè un set, allè ancora perfettamente utilizzabile su scale molto più grandi:
>>> 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)
L'uso del test dei sottoinsiemi è ancora più veloce, ma solo di circa 5 volte su questa scala. L'aumento di velocità è dovuto call'implementazione veloce di Python set, ma l'algoritmo fondamentale è lo stesso in entrambi i casi.
Se itemssei già archiviato in un elenco per altri motivi, dovrai convertirli in un set prima di utilizzare l'approccio del test del sottoinsieme. Quindi l'aumento di velocità scende a circa 2,5 volte:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
E se la tua containerè una sequenza e deve essere prima convertita, la velocità è ancora più piccola:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
L'unica volta che otteniamo risultati disastrosamente lenti è quando usciamo containercome sequenza:
>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
E, naturalmente, lo faremo solo se necessario. Se tutti gli elementi in bigseqsono hashable, allora lo faremo invece:
>>> %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)
Questo è solo 1,66 volte più veloce dell'alternativa ( set(bigseq) >= set(bigsubseq), con un tempo superiore a 4,36).
Quindi il test dei sottoinsiemi è generalmente più veloce, ma non con un margine incredibile. D'altra parte, diamo un'occhiata a quando allè più veloce. E se itemsfosse lungo dieci milioni di valori ed è probabile che abbia valori che non sono presenti 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)
La conversione del generatore in un set risulta essere incredibilmente dispendiosa in questo caso. Il setcostruttore deve consumare l'intero generatore. Ma il comportamento di cortocircuito di allassicura che solo una piccola parte del generatore debba essere consumata, quindi è più veloce di un test di sottoinsieme di quattro ordini di grandezza .
Questo è un esempio estremo, è vero. Ma come mostra, non puoi presumere che un approccio o l'altro sarà più veloce in tutti i casi.
La maggior parte delle volte containervale la pena convertirsi in un set, almeno se tutti i suoi elementi sono modificabili. Questo perché inper gli insiemi è O (1), mentre inper le sequenze è O (n).
D'altra parte, probabilmente vale la pena usare il test dei sottoinsiemi solo a volte. Sicuramente fallo se i tuoi articoli di prova sono già memorizzati in un set. In caso contrario, allè solo leggermente più lento e non richiede spazio di archiviazione aggiuntivo. Può anche essere utilizzato con grandi generatori di oggetti e, in tal caso, a volte fornisce un'enorme accelerazione.
Un altro modo per farlo:
>>> set(['a','b']).issubset( ['b','a','foo','bar'] )
True
{'a', 'b'} <= {'b','a','foo','bar'}
Se desideri controllare tutte le corrispondenze di input ,
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
se vuoi controllare almeno una corrispondenza ,
>>> any(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
Il parser Python ha valutato quell'istruzione come una tupla, dove si trovava il primo valore 'a'e il secondo valore è l'espressione 'b' in ['b', 'a', 'foo', 'bar'](che restituisce True).
Puoi scrivere una semplice funzione fai quello che vuoi, però:
def all_in(candidates, sequence):
for element in candidates:
if element not in sequence:
return False
return True
E chiamalo come:
>>> all_in(('a', 'b'), ['b', 'a', 'foo', 'bar'])
True
[x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]
Il motivo per cui penso che sia migliore della risposta scelta è che non hai davvero bisogno di chiamare la funzione 'all ()'. L'elenco vuoto restituisce False nelle istruzioni IF, l'elenco non vuoto restituisce True.
if [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]:
...Do something...
Esempio:
>>> [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]
['a', 'b']
>>> [x for x in ['G','F'] if x in ['b', 'a', 'foo', 'bar']]
[]
Entrambe le risposte presentate qui non gestiranno elementi ripetuti. Ad esempio, se stai verificando se [1,2,2] è una sottolista di [1,2,3,4], entrambi restituiranno True. Potrebbe essere quello che intendi fare, ma volevo solo chiarire. Se vuoi restituire false per [1,2,2] in [1,2,3,4], dovresti ordinare entrambi gli elenchi e controllare ogni elemento con un indice mobile su ogni elenco. Solo un ciclo for leggermente più complicato.
come puoi essere pitonico senza lambda! .. da non prendere sul serio .. ma funziona anche in questo modo:
orig_array = [ ..... ]
test_array = [ ... ]
filter(lambda x:x in test_array, orig_array) == test_array
tralascia la parte finale se vuoi verificare se uno qualsiasi dei valori è nell'array:
filter(lambda x:x in test_array, orig_array)
filterc'è un generatore. Dovresti racchiuderlo listse desideri effettivamente ottenere un risultato che potresti testare con ==o in un contesto booleano (per vedere se è vuoto). È preferibile utilizzare una comprensione dell'elenco o un'espressione generatore in anyo all.
set(['a', 'b']) <= set(['b','a','foo','bar'])è un altro modo per scrivere la stessa cosa e sembra più "matematico".