Eu tenho uma lista de conjuntos:
setlist = [s1,s2,s3...]
Quero s1 ∩ s2 ∩ s3 ...
Eu posso escrever uma função para fazer isso executando uma série de pares s1.intersection(s2), etc.
Existe uma maneira recomendada, melhor ou integrada?
Eu tenho uma lista de conjuntos:
setlist = [s1,s2,s3...]
Quero s1 ∩ s2 ∩ s3 ...
Eu posso escrever uma função para fazer isso executando uma série de pares s1.intersection(s2), etc.
Existe uma maneira recomendada, melhor ou integrada?
Respostas:
No Python versão 2.6, você pode usar vários argumentos para set.intersection(), como
u = set.intersection(s1, s2, s3)
Se os conjuntos estiverem em uma lista, isso se traduzirá em:
u = set.intersection(*setlist)
onde *a_listestá a expansão da lista
Observe que nãoset.intersection é um método estático, mas isso usa a notação funcional para aplicar a interseção do primeiro conjunto com o restante da lista. Portanto, se a lista de argumentos estiver vazia, isso falhará.
A partir da versão 2.6, set.intersectionleva arbitrariamente muitas iteráveis.
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s3 = set([2, 4, 6])
>>> s1 & s2 & s3
set([2])
>>> s1.intersection(s2, s3)
set([2])
>>> sets = [s1, s2, s3]
>>> set.intersection(*sets)
set([2])
Claramente set.intersectioné o que você quer aqui, mas, se você precisar de uma generalização de "pegue a soma de tudo isso", "pegue o produto de todos esses", "pegue o xor de todos esses", o que você está procurando é o reducefunção:
from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
ou
print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}
Se você não possui o Python 2.6 ou superior, a alternativa é escrever um loop for explícito:
def set_list_intersection(set_list):
if not set_list:
return set()
result = set_list[0]
for s in set_list[1:]:
result &= s
return result
set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print set_list_intersection(set_list)
# Output: set([1])
Você também pode usar reduce:
set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])
No entanto, muitos programadores de Python não gostam disso, incluindo o próprio Guido :
Há cerca de 12 anos, o Python adquiriu lambda, reduza (), filtre () e mapeie (), cortesia de (acredito) um hacker do Lisp que os perdeu e enviou os patches de trabalho. Mas, apesar do valor de RP, acho que esses recursos devem ser cortados do Python 3000.
Então agora reduza (). Na verdade, esse é o que eu mais odiei, porque, além de alguns exemplos envolvendo + ou *, quase sempre que vejo uma chamada de redução () com um argumento de função não trivial, preciso pegar papel e caneta para diagrama o que realmente está sendo alimentado nessa função antes que eu entenda o que o reduzir () deve fazer. Então, na minha opinião, a aplicabilidade de reduzir () é praticamente limitada aos operadores associativos e, em todos os outros casos, é melhor escrever explicitamente o ciclo de acumulação.
resultestiver vazio.
Aqui, estou oferecendo uma função genérica para interseção de vários conjuntos, tentando aproveitar o melhor método disponível:
def multiple_set_intersection(*sets):
"""Return multiple set intersection."""
try:
return set.intersection(*sets)
except TypeError: # this is Python < 2.6 or no arguments
pass
try: a_set= sets[0]
except IndexError: # no arguments
return set() # return empty set
return reduce(a_set.intersection, sets[1:])
Guido pode não gostar reduce, mas eu meio que gosto disso :)
setsvez de tentar acessar sets[0]e capturar o arquivo IndexError.
a_seté usado no retorno final.
return reduce(sets[0], sets[1:]) if sets else set()?
try/ exceptdeve ser evitado, se possível. É um cheiro de código, é ineficiente e pode esconder outros problemas.
reduceé "limitado a operadores associativos", o que é aplicável neste caso.reducemuitas vezes é difícil descobrir, mas&não é tão ruim.