2.10. Conjuntos¶
Um conjunto (set) é uma coleção não ordenada de itens únicos. Adicionar um valor que já está presente não tem efeito; a iteração produz cada valor exatamente uma vez. Os conjuntos são a ferramenta certa quando pertinência e deduplicação importam, e a ordenação não.
2.10.1. Criando um conjunto¶
Use chaves para um conjunto não vazio, ou set() para um vazio:
colours = {"red", "green", "blue"}
empty = set()
As chaves se parecem com um literal de dict; {} por si só é um dict vazio, não um conjunto vazio – um dos acidentes históricos do Python. Use set() para o caso vazio.
set() também constrói um conjunto a partir de qualquer iterável, que é a maneira padrão de eliminar duplicatas de uma sequência:
nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)
Saída:
{1, 2, 3, 4}
A ordem de impressão pode variar – conjuntos não prometem iterar em nenhuma ordem específica.
2.10.2. Set vs dict¶
Tanto conjuntos quanto dicts armazenam itens únicos em uma tabela hash. O que cada item carrega consigo é a diferença:
Um
dictarmazena pares chave-valor. Consultar uma chave retorna seu valor.Um
setarmazena apenas os itens. Consultar um item informa se ele está lá.
A escolha entre os dois é sobre se o valor ao lado de cada item significa algo:
Recorra a um set quando nenhum valor pertence ao lado de cada item – você só se importa se o item está presente, ou está combinando grupos de itens únicos com união / interseção.
Recorra a um dict quando cada item está emparelhado com dados que a consulta deve recuperar – um mapa de configuração, um cache, um contador indexado por nome.
Os dois tipos compartilham muita sintaxe superficial, que é de onde vem a maior parte da confusão. As diferenças em um único bloco:
set |
dict |
|
|---|---|---|
contém |
itens únicos |
chaves únicas, cada uma com um valor |
literal preenchido |
|
|
literal vazio |
|
|
teste de pertinência |
|
|
buscar um valor |
n/d |
|
adicionar um item |
|
|
iterar |
produz itens |
produz chaves (use |
A assimetria entre os literais preenchido e vazio é a pegadinha que vale a pena destacar:
Chaves com itens dentro –
{1, 2, 3}– são um literal de conjunto; chaves com pares chave-valor –{"a": 1}– são um literal de dict. O parser os distingue pelo que há dentro.Chaves com nada dentro –
{}– são um dict vazio, não um conjunto vazio. Os dicts vieram primeiro; o literal vazio pertence a eles. Um conjunto vazio não tem literal com chaves e deve ser escrito comoset().
Um padrão comum quando apenas as chaves de um dict são lidas é trocar por um conjunto – isso torna a intenção óbvia e elimina os valores não utilizados da memória.
2.10.3. Adicionando e removendo¶
set.add()– insere um item.set.discard()– remove um item se ele estiver presente, não faz nada se não estiver.set.remove()– remove um item; geraKeyErrorse ele não existir.set.clear()– esvazia o conjunto.
s = {1, 2, 3}
s.add(4)
s.discard(99) # silent: 99 not in s
s.remove(2)
print(s)
Saída:
{1, 3, 4}
2.10.4. Pertinência¶
O operador in testa a pertinência. Em um conjunto, ele é aproximadamente de tempo constante independentemente do tamanho – o que é a principal razão para escolher um conjunto em vez de uma list quando você só precisa perguntar “este valor está aí”:
if "red" in colours:
print("colour is allowed")
Uma list com o mesmo conteúdo varreria a partir do início a cada vez, o que é aceitável para dez itens mas lento para dez mil.
2.10.5. Operações de conjunto¶
Dois conjuntos podem ser combinados com as operações matemáticas usuais. Cada uma tem tanto uma forma de operador quanto uma forma de método:
a | boua.union(b)– tudo em qualquer um dos conjuntos.a & boua.intersection(b)– apenas o que aparece em ambos.a - boua.difference(b)– o que está emamas não emb.a ^ boua.symmetric_difference(b)– o que está em um mas não em ambos.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)
Saída:
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
As formas de operador são somente leitura; as formas de método aceitam qualquer iterável à direita, não apenas outro conjunto (a.union([5, 6])). Escolha a que ler melhor no contexto.
2.10.6. O que pode entrar em um conjunto¶
Os elementos de um conjunto devem ser hashable – a mesma restrição das chaves de dict. int, float, str, bool, bytes e tuple (quando seu conteúdo é, por sua vez, hashable) todos funcionam. list e dict não; tentar adicionar um deles gera TypeError.
2.10.7. frozenset¶
Um set comum é mutável: toda chamada a add / remove / discard altera o objeto no próprio local. Essa mutabilidade o desqualifica de ser hashable, então um conjunto não pode ser usado como chave de dict nem como membro de outro conjunto.
frozenset é o equivalente imutável. Ele tem as mesmas consultas e operadores (in, |, &, -, ^) que set, mas não tem add / remove nem métodos que mutam. Como nada pode jamais alterar seu conteúdo, o hash de um frozenset é bem definido – então ele é hashable:
primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})
palettes = {
primary: "RGB",
secondary: "mixed",
}
print(palettes[primary])
Saída:
RGB
Construa um frozenset a partir de qualquer iterável – frozenset() para o caso vazio, frozenset(some_set) para tirar um snapshot imutável de um conjunto existente:
snapshot = frozenset(s) # immutable copy of s
s.add("new") # snapshot does not change
Duas razões comuns para recorrer a ele:
Uso como chave de dict ou membro de conjunto. Em qualquer lugar em que um único valor não consegue capturar o que você precisa, um
frozensetde valores consegue – “o conjunto de características suportadas por este driver”, “o conjunto de pinos que este perfil usa”.Travar uma constante. Um
frozensetde nomes permitidos em nível de módulo não pode ser mutado acidentalmente por um chamador; umsetcomum pode. Prefirafrozensetpara qualquer coisa que deva ser somente leitura após a construção.