Python União, Interseção e Diferença
Na aula Conjuntos, você viu que o set guarda valores sem repetir. A outra grande força dele são as contas entre conjuntos, as mesmas dos diagramas de bolinhas da escola: juntar dois grupos, ver quem está nos dois, quem está só em um. A Ana dá aula de violão e de inglês no centro comunitário, e essas contas respondem na hora perguntas como "quem faz as duas aulas?" e "quem faz só violão?".
- A união (
|): todos de um grupo ou do outro. - A interseção (
&): só quem está nos dois. - A diferença (
-): quem está num e não está no outro, e por que a ordem importa. - A diferença simétrica (
^): quem está em um só. - Os métodos com nome (
union,intersection…) e quando preferi-los. - A perguntar se um grupo está dentro do outro (
<=,issubset) e a atualizar no lugar (|=,update). - Os erros reais ao misturar conjunto com lista e ao usar
+ouand.
A ideia: as duas bolinhas
Desenhe duas bolinhas que se cruzam. Na da esquerda, os alunos de violão: Ana, Bia, Caio e Davi. Na da direita, os de inglês: Bia, Davi e Eva. A Bia e o Davi fazem as duas aulas, então ficam no pedaço do meio, onde as bolinhas se cruzam.
| Pergunta | Pedaço do desenho | Nome | Em Python |
|---|---|---|---|
| Quem faz alguma das aulas? | As duas bolinhas inteiras | União | violao | ingles |
| Quem faz as duas? | Só o meio | Interseção | violao & ingles |
| Quem faz só violão? | A esquerda, sem o meio | Diferença | violao - ingles |
| Quem faz só uma das duas? | As pontas, sem o meio | Diferença simétrica | violao ^ ingles |
1. As quatro contas de uma vez
violao = {"Ana", "Bia", "Caio", "Davi"}
ingles = {"Bia", "Davi", "Eva"}
print("União:", sorted(violao | ingles))
print("Interseção:", sorted(violao & ingles))
print("Só violão:", sorted(violao - ingles))
print("Só inglês:", sorted(ingles - violao))
print("Só uma das duas:", sorted(violao ^ ingles))
$ python3 o_turmas.py União: ['Ana', 'Bia', 'Caio', 'Davi', 'Eva'] Interseção: ['Bia', 'Davi'] Só violão: ['Ana', 'Caio'] Só inglês: ['Eva'] Só uma das duas: ['Ana', 'Caio', 'Eva']
- violao | ingles
- União. A barra vertical
|lembra "ou": quem está emviolaoou emingles. A Bia e o Davi aparecem uma vez só, porque o resultado também é um conjunto. - violao & ingles
- Interseção. O
&("e comercial") lembra "e": quem está emviolaoe emingles. - violao - ingles
- Diferença. Como numa subtração: comece com a turma de violão e tire quem também está na de inglês.
- ingles - violao
- A diferença ao contrário dá outro resultado: quem faz só inglês. Na diferença, a ordem importa.
- violao ^ ingles
- Diferença simétrica. O acento circunflexo
^quer dizer "em um dos dois, mas não nos dois". É a união menos a interseção. - sorted( … )
- Cada conta devolve um conjunto novo, e conjunto de textos não tem ordem fixa. O
sorted()põe o resultado em ordem alfabética para oprintsair sempre igual.
Nenhuma das contas muda violao ou ingles: todas criam um conjunto novo, do mesmo jeito que 2 + 3 não muda o 2 nem o 3.
Com números, sem sorted
Com números inteiros pequenos, o print do conjunto sai sempre igual (lembre da aula anterior: a "arrumação" dos números não muda de uma execução para outra), então dá para ver o conjunto direto:
a = {1, 2, 3, 4}
b = {3, 4, 5}
print(a | b)
print(a & b)
print(a - b)
print(b - a)
print(a ^ b)
$ python3 o_numeros.py
{1, 2, 3, 4, 5}
{3, 4}
{1, 2}
{5}
{1, 2, 5}
Missão: a Ana e a Bia fizeram cada uma a sua lista de compras. Mostre, em ordem alfabética, o que está nas duas listas (para não comprar em dobro). Troque o ? pela conta certa entre ana e bia.
ana = {"pão", "leite", "café", "ovos"}
bia = {"leite", "arroz", "pão"}
print(sorted(ana)) # troque ana pela conta: ana ? bia
ana = {"pão", "leite", "café", "ovos"}
bia = {"leite", "arroz", "pão"}
print(sorted(ana & bia))
2. Os mesmos cálculos com nome: os métodos
Cada símbolo tem um método equivalente, com o nome em inglês da conta. O resultado é o mesmo:
violao = {"Ana", "Bia", "Caio", "Davi"}
ingles = {"Bia", "Davi", "Eva"}
print(sorted(violao.union(ingles)))
print(sorted(violao.intersection(ingles)))
print(sorted(violao.difference(ingles)))
print(sorted(violao.symmetric_difference(ingles)))
print(sorted(violao.intersection(["Eva", "Ana", "Ana"])))
$ python3 o_metodos.py ['Ana', 'Bia', 'Caio', 'Davi', 'Eva'] ['Bia', 'Davi'] ['Ana', 'Caio'] ['Ana', 'Caio', 'Eva'] ['Ana']
Repare na última linha: o método aceitou uma lista (com repetido e tudo). Essa é a grande diferença entre os dois jeitos: os símbolos só funcionam entre dois conjuntos; os métodos aceitam qualquer coisa que tenha itens (lista, tupla, texto) e convertem sozinhos.
Mais de dois conjuntos
As contas podem ser encadeadas. Os clientes que vieram na segunda, na quarta e na sexta, por número:
seg = {1, 2, 3}
qua = {2, 3, 4}
sex = {3, 4, 5}
print(seg & qua & sex)
print(seg | qua | sex)
print(seg.union(qua, sex))
$ python3 o_tres.py
{3}
{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}
Só o cliente 3 veio nos três dias. E os métodos aceitam vários conjuntos de uma vez, separados por vírgula: seg.union(qua, sex).
3. Um conjunto dentro do outro
Além de calcular, dá para perguntar sobre dois conjuntos. "Todos desta dupla fazem violão?" é perguntar se a dupla é um subconjunto da turma: se cabe inteira dentro dela.
violao = {"Ana", "Bia", "Caio", "Davi"}
dupla = {"Bia", "Davi"}
print(dupla <= violao)
print(dupla.issubset(violao))
print(violao >= dupla)
print(violao.issuperset(dupla))
print(dupla < violao)
print(violao < violao)
print(violao <= violao)
print(violao.isdisjoint({"Eva", "Fábio"}))
print(violao.isdisjoint({"Ana"}))
$ python3 o_sub.py True True True True True False True True False
| Escrita | Pergunta | Aqui |
|---|---|---|
dupla <= violao ou dupla.issubset(violao) | Todos de dupla estão em violao? (subconjunto) | True |
violao >= dupla ou violao.issuperset(dupla) | A mesma pergunta, vista do outro lado: violao contém dupla? (superconjunto) | True |
dupla < violao | Subconjunto e menor (falta alguém)? (subconjunto próprio) | True |
violao < violao | Um conjunto não é "menor" que ele mesmo | False |
violao <= violao | Mas cabe dentro dele mesmo | True |
violao.isdisjoint(outro) | Os dois não têm ninguém em comum? ("disjuntos") | True com Eva e Fábio; False com Ana |
< não é "tem menos itens"
Com conjuntos, < e <= não comparam tamanhos, comparam conteúdo. {1, 2} < {5, 6, 7} é False, mesmo o primeiro sendo menor, porque o 1 e o 2 não estão no outro. Para comparar tamanhos, compare os len.
4. Atualizando o conjunto no lugar
As contas acima criam um conjunto novo. Quando você quer mudar o próprio conjunto, use a versão com =, como o += dos números (aula Operadores de Atribuição), ou o método update:
estoque = {"arroz", "feijão"}
estoque |= {"café", "arroz"}
print(sorted(estoque))
estoque -= {"feijão"}
print(sorted(estoque))
estoque.update(["leite", "pão"])
print(sorted(estoque))
$ python3 o_update.py ['arroz', 'café', 'feijão'] ['arroz', 'café'] ['arroz', 'café', 'leite', 'pão']
- estoque |= {"café", "arroz"}
- O mesmo que
estoque = estoque | {"café", "arroz"}: junta os itens novos. O arroz já estava e não se repete. - estoque -= {"feijão"}
- Tira do estoque tudo o que está no outro conjunto.
- estoque.update(["leite", "pão"])
- "Atualizar": a versão método do
|=. Como todo método, aceita uma lista.
Existem também &= (fica só com o que está nos dois) e ^=, e os métodos intersection_update, difference_update e symmetric_difference_update, com nomes compridos mas a mesma ideia.
Formas diferentes de fazer a mesma conta
| Conta | Símbolo | Método | Atualizando no lugar |
|---|---|---|---|
| União | a | b | a.union(b) | a |= b / a.update(b) |
| Interseção | a & b | a.intersection(b) | a &= b / a.intersection_update(b) |
| Diferença | a - b | a.difference(b) | a -= b / a.difference_update(b) |
| Diferença simétrica | a ^ b | a.symmetric_difference(b) | a ^= b / a.symmetric_difference_update(b) |
Qual usar? Os símbolos são curtos e se leem como uma conta; use quando os dois lados já são conjuntos. Os métodos são mais fáceis de entender para quem não decorou os símbolos, e aceitam listas e tuplas sem precisar converter: use quando o outro lado não é um conjunto.
Quando dá errado
violao = {"Ana", "Bia"}
novos = ["Caio", "Davi"]
todos = violao | novos
$ python3 o_lista_erro.py
Traceback (most recent call last):
File "/home/ana/o_lista_erro.py", line 3, in <module>
todos = violao | novos
~~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for |: 'set' and 'list'
- line 3
- O erro está na linha 3.
- ~~~~~~~^~~~~~~
- O
^aponta para o operador|, e os tils marcam os dois lados da conta. - TypeError: unsupported operand type(s) for |: 'set' and 'list'
- "Tipos de operando não suportados para
|:setelist". O|não sabe juntar um conjunto com uma lista.
Como consertar: converta a lista (violao | set(novos)) ou use o método, que aceita listas: violao.union(novos).
+
a = {1, 2}
b = {3}
print(a + b)
$ python3 o_mais_erro.py
Traceback (most recent call last):
File "/home/ana/o_mais_erro.py", line 3, in <module>
print(a + b)
~~^~~
TypeError: unsupported operand type(s) for +: 'set' and 'set'
Listas e tuplas se juntam com +, mas conjuntos não: "tipos não suportados para +: set e set". Como consertar: a "soma" de conjuntos é a união: a | b.
and no lugar de &: sem erro, resultado errado
violao = {1, 2, 3}
ingles = {3, 4}
print(violao and ingles)
print(violao & ingles)
$ python3 o_and.py
{3, 4}
{3}
A palavra and ("e") parece a interseção, mas não é. Ela é o "e" lógico da aula and, or e not: como violao não está vazio, violao and ingles devolve simplesmente o segundo conjunto, {3, 4}, sem calcular nada. Nenhum erro aparece, e o resultado está errado. O mesmo vale para or no lugar de |. Como consertar: para contas de conjunto, use os símbolos & e | (ou os métodos).
violao = {"Ana", "Bia"}
xadrez = {"Caio"}
comum = violao & xadrez
print(comum)
print(len(comum))
if not comum:
print("Ninguém faz as duas.")
$ python3 o_vazio.py set() 0 Ninguém faz as duas.
Quando a conta não sobra ninguém, o resultado é o conjunto vazio, set(). Ele conta como "falso" num if, então if not comum: é o jeito de perguntar "ficou vazio?".
Referência rápida
| Escrita | Resultado |
|---|---|
a | b | União: o que está em a ou em b |
a & b | Interseção: o que está nos dois |
a - b | Diferença: o que está em a e não em b |
a ^ b | Diferença simétrica: o que está em um só |
a <= b / a.issubset(b) | True se tudo de a está em b |
a >= b / a.issuperset(b) | True se a contém tudo de b |
a.isdisjoint(b) | True se não há nada em comum |
a |= b, a &= b, a -= b, a ^= b | Fazem a conta e guardam o resultado em a |
a.update(lista) | Acrescenta os itens de qualquer sequência |
| Sintoma | Causa provável |
|---|---|
TypeError: unsupported operand type(s) for |: 'set' and 'list' | Símbolo entre conjunto e lista: use o método ou set() |
TypeError: unsupported operand type(s) for +: 'set' and 'set' | Conjunto não soma: use | |
| Resultado estranho, sem erro | and/or no lugar de &/|, ou a ordem trocada na diferença |
set() | A conta não sobrou ninguém (não é erro) |
Exercícios
1. A interseção
Quanto vale {1, 2, 3} & {2, 3, 4}? Escreva como o Python mostra.
2. A diferença
Quanto vale {1, 2, 3} - {2, 3, 4}?
3. O símbolo da união
Complete o símbolo que junta as duas turmas:
4. Com violao = {"Ana", "Bia"} e ingles = {"Bia", "Eva"}, qual conta dá {'Eva'}?
5. {1, 2} | [3, 4] dá erro. Qual destas linhas funciona?
6. O que {"Bia"} <= {"Ana", "Bia"} responde?
Missão final: a lista de presença da festa
Missão: a Ana tem o conjunto de quem confirmou presença na festa e o de quem apareceu. Troque cada "?" por uma conta de conjuntos: quem confirmou e faltou (em ordem alfabética), quem veio sem confirmar (em ordem alfabética) e quantas pessoas diferentes estão nas duas listas juntas.
confirmados = {"Ana", "Bia", "Caio", "Davi"}
presentes = {"Bia", "Davi", "Eva"}
print("Faltaram:", "?")
print("Sem confirmar:", "?")
print("Total de pessoas:", "?")
confirmados = {"Ana", "Bia", "Caio", "Davi"}
presentes = {"Bia", "Davi", "Eva"}
print("Faltaram:", sorted(confirmados - presentes))
print("Sem confirmar:", sorted(presentes - confirmados))
print("Total de pessoas:", len(confirmados | presentes))
Resumo
|é a união (um ou outro),&a interseção (os dois),-a diferença (este e não aquele) e^a diferença simétrica (só um dos dois).- Toda conta devolve um conjunto novo; os originais não mudam. Na diferença, a ordem importa.
- Os métodos
union,intersection,differenceesymmetric_differencefazem o mesmo e aceitam listas; os símbolos exigem dois conjuntos. <=/issubset,>=/issuperseteisdisjointrespondem perguntas comTrueouFalse.|=,-=,&=,^=eupdatemudam o próprio conjunto.- Conjunto não soma com
+, eand/ornão são&/|. Para mostrar um conjunto de textos sempre igual, usesorted().