Isso faz o que você deseja e funcionará em quase todos os casos:
>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True
A expressão 'a','b' in ['b', 'a', 'foo', 'bar']
não funciona conforme o esperado porque Python a interpreta como uma tupla:
>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)
Outras opções
Existem outras maneiras de executar este teste, mas elas não funcionarão para tantos tipos diferentes de entradas. Como Kabie aponta, você pode resolver este problema usando conjuntos ...
>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True
...as vezes:
>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Os conjuntos só podem ser criados com elementos hashable. Mas a expressão do gerador all(x in container for x in items)
pode lidar com quase qualquer tipo de contêiner. O único requisito é que container
seja reiterável (ou seja, não seja um gerador). items
pode ser qualquer iterável.
>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True
Testes de Velocidade
Em muitos casos, o teste de subconjunto será mais rápido do que all
, mas a diferença não é chocante - exceto quando a questão é irrelevante porque os conjuntos não são uma opção. Converter listas em conjuntos apenas para fins de um teste como esse nem sempre valerá a pena. E converter geradores em conjuntos pode às vezes ser um desperdício incrível, reduzindo a velocidade dos programas em muitas ordens de magnitude.
Aqui estão alguns benchmarks para ilustração. A maior diferença surge quando container
e items
são relativamente pequenos. Nesse caso, a abordagem de subconjunto é cerca de uma ordem de magnitude mais rápida:
>>> 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)
Isso parece uma grande diferença. Mas, enquanto container
for um conjunto, all
ainda é perfeitamente utilizável em escalas muito maiores:
>>> 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)
Usar o teste de subconjunto ainda é mais rápido, mas apenas cerca de 5x nesta escala. O aumento de velocidade é devido à c
implementação de apoio rápido do Python deset
, mas o algoritmo fundamental é o mesmo em ambos os casos.
Se items
já estiverem armazenados em uma lista por outros motivos, você terá que convertê-los em um conjunto antes de usar a abordagem de teste de subconjunto. Então, a aceleração cai para cerca de 2,5x:
>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
E se você container
for uma sequência e precisar ser convertido primeiro, a aceleração será ainda menor:
>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
A única vez em que obtemos resultados desastrosamente lentos é quando partimos container
em uma sequência:
>>> %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, claro, só faremos isso se for necessário. Se todos os itens bigseq
forem hashable, então faremos isso em vez disso:
>>> %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)
Isso é apenas 1,66x mais rápido do que a alternativa (set(bigseq) >= set(bigsubseq)
, cronometrado acima em 4,36).
Portanto, o teste de subconjunto é geralmente mais rápido, mas não por uma margem incrível. Por outro lado, vamos ver quando all
é mais rápido. E se items
tiver dez milhões de valores e for provável que tenha valores que não estão 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)
Converter o gerador em um conjunto acaba sendo um desperdício incrível neste caso. O set
construtor deve consumir todo o gerador. Mas o comportamento de curto-circuito de all
garante que apenas uma pequena parte do gerador precise ser consumida, então é mais rápido do que um teste de subconjunto em quatro ordens de magnitude .
Este é um exemplo extremo, admito. Mas, como mostra, você não pode presumir que uma abordagem ou outra será mais rápida em todos os casos.
The Upshot
Na maioria das vezes, container
vale a pena converter para um conjunto, pelo menos se todos os seus elementos forem hashable. Isso porque in
para conjuntos é O (1), enquanto in
para sequências é O (n).
Por outro lado, usar o teste de subconjunto provavelmente só vale a pena algumas vezes. Definitivamente faça isso se seus itens de teste já estiverem armazenados em um conjunto. Caso contrário, all
é apenas um pouco mais lento e não requer nenhum armazenamento adicional. Ele também pode ser usado com grandes geradores de itens e, às vezes, fornece uma grande aceleração nesse caso.
set(['a', 'b']) <= set(['b','a','foo','bar'])
é outra maneira de escrever a mesma coisa, e parece mais "matemática".{'a', 'b'} <= {'b','a','foo','bar'}
Tenho certeza de que
in
tem precedência mais alta do que,
a sua declaração está sendo interpretada como'a', ('b' in ['b' ...])
, que então avalia como,'a', True
visto que'b'
está na matriz.Veja a resposta anterior para saber como fazer o que deseja.
fonte
Se você quiser verificar todas as correspondências de entrada ,
se você quiser verificar pelo menos uma correspondência ,
fonte
O analisador Python avaliou essa instrução como uma tupla, onde o primeiro valor era
'a'
, e o segundo valor é a expressão'b' in ['b', 'a', 'foo', 'bar']
(que avalia comoTrue
).Você pode escrever uma função simples, faça o que quiser:
E chamá-lo assim:
fonte
Acho que isso é melhor do que a resposta escolhida porque você realmente não precisa chamar a função 'all ()'. A lista vazia é avaliada como False em instruções IF, a lista não vazia é avaliada como True.
Exemplo:
fonte
Eu diria que podemos até mesmo deixar esses colchetes de fora.
fonte
Ambas as respostas apresentadas aqui não lidarão com elementos repetidos. Por exemplo, se você estiver testando se [1,2,2] é uma sublista de [1,2,3,4], ambos retornarão True. Pode ser isso que você pretende fazer, mas eu só queria esclarecer. Se você quiser retornar falso para [1,2,2] em [1,2,3,4], você precisará classificar as duas listas e verificar cada item com um índice móvel em cada lista. Apenas um loop for ligeiramente mais complicado.
fonte
como você pode ser pitônico sem lambdas! .. não deve ser levado a sério .. mas desta forma também funciona:
deixe de fora a parte final se quiser testar se algum dos valores está na matriz:
fonte
filter
há um gerador. Você precisaria envolvê-lolist
se quisesse realmente obter um resultado que pudesse testar com==
ou em um contexto booleano (para ver se está vazio). Usar uma compreensão de lista ou uma expressão geradora emany
ouall
é preferível.Veja como eu fiz:
fonte