Python Conjuntos (set)
A Ana anotou numa lista o número de cada cliente que passou na padaria hoje: [3, 1, 3, 2, 1, 3]. Foram seis visitas, mas quantos clientes diferentes? Para perguntas assim o Python tem o conjunto (em inglês, set): uma coleção que não aceita repetidos. Jogue a lista num conjunto e as repetições somem sozinhas.
- A criar um conjunto com chaves
{ }ou comset(), e por que os repetidos desaparecem. - Que o conjunto não tem ordem: com textos, a ordem do
printmuda de uma execução para outra. - A tirar os repetidos de uma lista e a mostrar o resultado em ordem com
sorted(). - A acrescentar e remover itens:
add,remove,discard,clear. - A armadilha:
{}é um dicionário vazio; conjunto vazio éset(). - Os erros reais:
KeyError,not subscriptable,unhashable type.
A ideia: o álbum de figurinhas
Pense no álbum de figurinhas da Ana. Cada figurinha entra no álbum uma vez só: se ela tirar a figurinha 7 de novo, não tem onde colar, porque o espaço da 7 já está ocupado. E o álbum responde muito bem a uma pergunta: "eu já tenho a 7?". Ele não liga para a ordem em que as figurinhas chegaram; só para quais estão lá.
O conjunto do Python é esse álbum: guarda cada valor uma vez, responde rápido se um valor está nele, e não guarda ordem.
| Lista | Tupla | Conjunto | |
|---|---|---|---|
| Como se escreve | [1, 2, 2] | (1, 2, 2) | {1, 2} |
| Aceita repetidos | Sim | Sim | Não |
Tem ordem e índice ([0]) | Sim | Sim | Não |
| Pode mudar | Sim | Não | Sim (acrescentar e remover) |
1. Criando um conjunto
numeros = {3, 1, 2}
print(numeros)
print(type(numeros))
repetidos = {1, 2, 2, 3, 3, 3}
print(repetidos)
print(len(repetidos))
- { … }
- Chaves envolvem os itens do conjunto (lista usa colchetes; tupla, parênteses).
- 3, 1, 2
- Os itens, separados por vírgula.
- type(numeros)
- O tipo se chama
set. - {1, 2, 2, 3, 3, 3}
- Você escreveu seis valores, mas só três são diferentes. O conjunto guarda cada um uma vez.
$ python3 c_criar.py
{1, 2, 3}
<class 'set'>
{1, 2, 3}
3
Os repetidos sumiram sem aviso nenhum: não é erro escrever um valor duas vezes, o conjunto simplesmente ignora a cópia. E o len conta só os diferentes: 3.
2. O conjunto não tem ordem
Repare que {3, 1, 2} foi mostrado como {1, 2, 3}. Não foi o Python "ordenando" para você: o conjunto guarda os itens do jeito que for mais rápido para achar cada um depois, e isso não tem nada a ver com a ordem em que você escreveu. Com números pequenos costuma parecer ordenado, mas é coincidência:
numeros = {10, 3, 7, 1}
print(numeros)
$ python3 c_ordem.py
{1, 10, 3, 7}
Nem a ordem em que foi escrito, nem a ordem crescente. Com textos é ainda mais curioso: a Ana rodou o mesmo programa três vezes seguidas.
frutas = {"manga", "caju", "acerola", "goiaba"}
print(frutas)
$ python3 c_textos.py
{'manga', 'caju', 'goiaba', 'acerola'}
$ python3 c_textos.py
{'caju', 'goiaba', 'acerola', 'manga'}
$ python3 c_textos.py
{'manga', 'goiaba', 'acerola', 'caju'}
Três ordens diferentes, sem mudar uma letra do programa. Por quê? Para achar um texto rápido, o Python transforma cada texto num número (o hash, uma espécie de "impressão digital" do valor), e por segurança essa conta usa um sorteio novo a cada execução. Mudou o sorteio, mudou a arrumação, mudou a ordem do print. Os números inteiros não passam por esse sorteio, por isso {10, 3, 7, 1} sai sempre igual, mas mesmo assim fora de ordem. (No editor do guia, a ordem dos textos também muda, mas pode se repetir entre um Rodar e outro na mesma página. Não conte com ela em nenhum dos dois.)
Se o seu programa precisa mostrar os itens numa ordem certa, use sorted(conjunto): ele devolve uma lista com os itens em ordem crescente (ou alfabética), sempre igual. Se você precisa da ordem em que os itens chegaram, o conjunto não é a ferramenta certa: use uma lista.
3. Tirando os repetidos de uma lista
O uso mais comum do conjunto no dia a dia: transformar uma lista com repetidos em um conjunto com set(lista). Voltando às visitas da padaria:
visitas = [3, 1, 3, 2, 1, 3] unicos = set(visitas) print(unicos) print(len(visitas), "visitas,", len(unicos), "clientes diferentes")
$ python3 c_lista.py
{1, 2, 3}
6 visitas, 3 clientes diferentes
Com textos, junte o set() ao sorted() para ter um resultado arrumado. E o in pergunta se um valor está no conjunto:
cidades = ["Teresina", "Parnaíba", "Teresina", "Picos", "Parnaíba"]
diferentes = set(cidades)
print(len(diferentes))
print(sorted(diferentes))
print("Picos" in diferentes)
print("Floriano" in diferentes)
$ python3 c_sorted.py 3 ['Parnaíba', 'Picos', 'Teresina'] True False
- set(cidades)
- Cria um conjunto com os itens da lista: as repetições somem. A lista
cidadescontinua igual. - len(diferentes)
- Quantas cidades diferentes: 3.
- sorted(diferentes)
- Uma lista com os itens do conjunto em ordem alfabética. Essa linha sai igual em toda execução.
- "Picos" in diferentes
- Pergunta se o valor está no conjunto. É a especialidade dele: mesmo com milhões de itens, responde na hora.
Um texto também pode virar conjunto: set("banana") é o conjunto das letras diferentes da palavra.
print(sorted(set("banana")))
print(len(set("banana")))
$ python3 c_letras.py ['a', 'b', 'n'] 3
Missão: a lista de convidados do aniversário tem nomes repetidos (gente que confirmou duas vezes). Use set() para contar quantas pessoas diferentes vêm. A resposta certa é 4.
lista = ["Ana", "Bia", "Ana", "Caio", "Davi", "Bia"]
print("Convidados:", len(lista))
lista = ["Ana", "Bia", "Ana", "Caio", "Davi", "Bia"]
pessoas = set(lista)
print("Convidados:", len(pessoas))
4. Acrescentando e removendo itens
O conjunto pode mudar: dá para pôr e tirar itens. Como ele não tem ordem, não existe "pôr no fim" (append) nem "pôr na posição 2" (insert); existe só pôr: add.
vistos = {1, 2}
vistos.add(3)
vistos.add(2)
print(vistos)
vistos.remove(1)
print(vistos)
vistos.discard(10)
print(vistos)
vistos.clear()
print(vistos)
$ python3 c_add.py
{1, 2, 3}
{2, 3}
{2, 3}
set()
- vistos.add(3)
- "Adicionar": põe o 3 no conjunto.
- vistos.add(2)
- O 2 já estava lá: não acontece nada, e não é erro.
- vistos.remove(1)
- "Remover": tira o 1. Se o item não estiver no conjunto, dá
KeyError(veja Quando dá errado). - vistos.discard(10)
- "Descartar": tira o item se ele estiver lá; se não estiver, fica quieto. O 10 não estava, e nada aconteceu.
- vistos.clear()
- "Limpar": esvazia o conjunto.
- set()
- É assim que o Python mostra um conjunto vazio. Não é
{}; a próxima seção explica por quê.
Existe também o pop(), que tira e devolve um item qualquer. "Qualquer" mesmo: como não há ordem, você não escolhe qual. Use quando tanto faz qual item sai.
Percorrendo com for
Dá para passar por um conjunto com for, mas a ordem é a do conjunto (ou seja, nenhuma que você controle). Para uma ordem certa, percorra o sorted():
notas = {7, 9, 7, 10}
for nota in sorted(notas):
print(nota)
$ python3 c_for.py 7 9 10
5. {} é dicionário, não conjunto
Esta pega muita gente. As chaves { } são usadas por dois tipos do Python: o conjunto e o dicionário. Com itens dentro, o Python sabe qual é qual. Mas {} vazio, sem nada, é um dicionário, porque o dicionário chegou primeiro na linguagem. Para um conjunto vazio, use set().
a = {}
b = set()
print(type(a))
print(type(b))
print(b)
$ python3 c_vazio.py <class 'dict'> <class 'set'> set()
| Escrita | Tipo |
|---|---|
{1, 2, 3} | Conjunto (set) |
{"pão": 0.5} (com dois-pontos) | Dicionário (dict) |
{} | Dicionário vazio |
set() | Conjunto vazio |
Missão: o programa quer guardar os nomes de quem já foi atendido, sem repetir, mas dá AttributeError: 'dict' object has no attribute 'add'. Conserte a primeira linha.
vistos = {}
for nome in ["Caio", "Bia", "Caio"]:
vistos.add(nome)
print(sorted(vistos))
vistos = set()
for nome in ["Caio", "Bia", "Caio"]:
vistos.add(nome)
print(sorted(vistos))
6. O que pode entrar num conjunto
Só valores que não mudam podem entrar num conjunto: números, textos, True/False, tuplas. Uma lista não pode, porque ela pode mudar depois de entrar, e o conjunto perderia a "impressão digital" dela. Uma tupla pode:
pontos = {(1, 2), (3, 4), (1, 2)}
print(len(pontos))
print((1, 2) in pontos)
$ python3 c_tupla_dentro.py 2 True
A tupla (1, 2) repetida contou uma vez só. Com listas, o resultado está em Quando dá errado.
Existe: o frozenset ("conjunto congelado"). É para o conjunto o que a tupla é para a lista: frozenset({1, 2, 3}) funciona como um conjunto, mas não tem add nem remove. É raro no começo; basta saber que existe.
Formas diferentes de criar um conjunto
| Forma | Resultado | Quando usar |
|---|---|---|
{1, 2, 3} | {1, 2, 3} | Quando você já sabe os itens |
set([1, 2, 2, 3]) | {1, 2, 3} | Para tirar os repetidos de uma lista (ou tupla) |
set("banana") | as letras a, b, n | Para as letras diferentes de um texto |
set() | set() | Conjunto vazio, para encher com add. Nunca {} |
Lista, tupla ou conjunto?
| Situação | Use |
|---|---|
| A ordem importa, ou os repetidos importam (cada visita conta) | Lista |
| Valores fixos, que andam juntos | Tupla |
| Só importa quais valores existem, sem repetir | Conjunto |
| Você vai perguntar muitas vezes "isto está aqui?" | Conjunto |
Conjuntos também sabem fazer contas de conjunto, como na escola: juntar dois, ver o que eles têm em comum, o que um tem e o outro não. É a próxima aula: União, Interseção e Diferença.
Quando dá errado
numeros = {3, 1, 2}
print(numeros[0])
$ python3 c_indice.py
Traceback (most recent call last):
File "/home/ana/c_indice.py", line 2, in <module>
print(numeros[0])
~~~~~~~^^^
TypeError: 'set' object is not subscriptable
- line 2
- O erro está na linha 2, no
numeros[0](o pedaço marcado com~~~^^^). - TypeError
- "Erro de tipo": essa operação não existe para esse tipo.
- 'set' object is not subscriptable
- "Um objeto
setnão aceita índice" (os colchetes depois do nome). Sem ordem, não existe "o item 0".
Como consertar: se você precisa de posições, converta: sorted(numeros)[0] dá o menor item. Para saber se um item está lá, use in.
vistos = {1, 2, 3}
vistos.remove(10)
$ python3 c_remove_erro.py
Traceback (most recent call last):
File "/home/ana/c_remove_erro.py", line 2, in <module>
vistos.remove(10)
~~~~~~~~~~~~~^^^^
KeyError: 10
KeyError: 10: "erro de chave: 10". O 10 não está no conjunto, e o remove avisa com um erro. Como consertar: use discard(10), que não reclama, ou pergunte antes: if 10 in vistos: vistos.remove(10).
{} no lugar de set()
convidados = {}
convidados.add("Ana")
$ python3 c_vazio_erro.py
Traceback (most recent call last):
File "/home/ana/c_vazio_erro.py", line 2, in <module>
convidados.add("Ana")
^^^^^^^^^^^^^^
AttributeError: 'dict' object has no attribute 'add'
A mensagem fala de 'dict', e não de set: é a pista. {} criou um dicionário, e dicionário não tem add. Como consertar: convidados = set().
pares = {[1, 2], [3, 4]}
$ python3 c_lista_dentro.py
Traceback (most recent call last):
File "/home/ana/c_lista_dentro.py", line 1, in <module>
pares = {[1, 2], [3, 4]}
^^^^^^^^^^^^^^^^
TypeError: cannot use 'list' as a set element (unhashable type: 'list')
"Não é possível usar list como item de um conjunto (tipo sem hash: list)". Unhashable quer dizer "não dá para tirar a impressão digital": como a lista pode mudar, ela não pode entrar no conjunto. Como consertar: use tuplas: {(1, 2), (3, 4)}.
A primeira parte da mensagem (cannot use 'list' as a set element) é novidade do Python 3.14. No Python 3.13 (o do Debian 13) aparece só TypeError: unhashable type: 'list'.
Referência rápida
| Escrita | O que faz |
|---|---|
s = {1, 2, 3} | Cria um conjunto |
s = set() | Conjunto vazio ({} é dicionário) |
set(lista) | Conjunto com os itens da lista, sem repetidos |
len(s) | Quantos itens diferentes |
x in s | True se x está no conjunto |
s.add(x) | Põe x (se já estava, nada muda) |
s.remove(x) | Tira x; KeyError se não estiver |
s.discard(x) | Tira x se estiver; senão, nada |
s.pop() | Tira e devolve um item qualquer |
s.clear() | Esvazia |
sorted(s) | Lista com os itens em ordem (o jeito de mostrar sempre igual) |
| Sintoma | Causa provável |
|---|---|
TypeError: 'set' object is not subscriptable | Usou índice (s[0]): conjunto não tem posição |
KeyError: 10 | remove de um item que não está lá |
AttributeError: 'dict' object has no attribute 'add' | Criou com {}, que é dicionário |
TypeError: cannot use 'list' as a set element (unhashable type: 'list') | Lista dentro do conjunto: use tupla |
A ordem do print muda a cada execução | Normal: conjunto não tem ordem. Use sorted() |
Exercícios
1. Conjunto vazio
Complete para criar um conjunto vazio:
2. Quantos diferentes?
Quanto vale len({5, 5, 6, 5, 7})?
3. Sem reclamar
Complete o método que tira o 4 do conjunto sem dar erro se ele não estiver lá:
4. O que type({}) mostra?
5. A Ana rodou duas vezes print({"pão", "leite", "café"}) e a ordem saiu diferente. O que aconteceu?
6. Qual destes valores não pode ser item de um conjunto?
Missão final: as cidades dos clientes
Missão: a lista tem a cidade de cada cliente, com repetições. Mostre quantas cidades diferentes são (Cidades diferentes: 3) e, na linha seguinte, a lista delas em ordem alfabética, que sai igual em toda execução.
clientes = ["Teresina", "Picos", "Teresina", "Parnaíba", "Picos", "Teresina"] print(clientes)
clientes = ["Teresina", "Picos", "Teresina", "Parnaíba", "Picos", "Teresina"]
cidades = set(clientes)
print("Cidades diferentes:", len(cidades))
print(sorted(cidades))
Resumo
- O conjunto (
set) guarda valores sem repetir:{1, 2, 2}vira{1, 2}. - Ele não tem ordem nem índice. Com textos, a ordem do
printmuda de uma execução para outra; para mostrar em ordem, usesorted(). set(lista)tira os repetidos de uma lista;lenconta os diferentes;inpergunta se um valor está lá.addpõe;removetira (comKeyErrorse não houver);discardtira sem reclamar;clearesvazia.{}é dicionário vazio; conjunto vazio éset().- Só valores que não mudam entram num conjunto: tupla sim, lista não (
unhashable type: 'list').