2.10. Conjuntos¶
Um conjunto é 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 importa a pertença e a eliminação de duplicados, e a ordem não interessa.
2.10.1. Criar um conjunto¶
Use chavetas para um conjunto não vazio, ou set() para um vazio:
colours = {"red", "green", "blue"}
empty = set()
As chavetas parecem um literal de dict; {} por si só é um dicionário 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 forma padrão de eliminar duplicados de uma sequência:
nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)
Resultado:
{1, 2, 3, 4}
A ordem de impressão pode variar – os conjuntos não garantem iterar em nenhuma ordem particular.
2.10.2. Conjunto vs dicionário¶
Os conjuntos e os dicionários armazenam ambos itens únicos numa tabela de hash. A diferença está no que cada item carrega consigo:
Um
dictarmazena pares chave-valor. Procurar uma chave devolve o seu valor.Um
setarmazena apenas os itens. Procurar um item diz-lhe se está presente.
A escolha entre os dois prende-se com saber se o valor ao lado de cada item tem significado:
Opte por um conjunto quando nenhum valor pertence a cada item – apenas quer saber se o item está presente, ou está a combinar grupos de itens únicos com união / interseção.
Opte por um dicionário quando cada item está emparelhado com dados que a consulta deve devolver – um mapa de configuração, uma cache, um contador indexado por nome.
Os dois tipos partilham muita sintaxe de superfície, que é de onde vem grande parte da confusão. As diferenças num bloco:
conjunto |
dicionário |
|
|---|---|---|
contém |
itens únicos |
chaves únicas, cada uma com um valor |
literal preenchido |
|
|
literal vazio |
|
|
teste de pertença |
|
|
obter um valor |
n/d |
|
adicionar um item |
|
|
iterar |
produz itens |
produz chaves (use |
A assimetria entre os literais preenchido e vazio é o ponto que vale a pena realçar:
Chavetas com itens –
{1, 2, 3}– são um literal de conjunto; chavetas com pares chave-valor –{"a": 1}– são um literal de dicionário. O interpretador distingue-os pelo que está no interior.Chavetas sem nada –
{}– são um dicionário vazio, não um conjunto vazio. Os dicionários vieram primeiro; o literal vazio pertence-lhes. Um conjunto vazio não tem nenhum literal com chavetas e tem de ser escrito comoset().
Um padrão comum, quando apenas as chaves de um dicionário são alguma vez lidas, é mudar para um conjunto – torna a intenção óbvia e elimina da memória os valores não utilizados.
2.10.3. Adicionar e remover¶
set.add()– inserir um item.set.discard()– remover um item se estiver presente, não fazer nada se não estiver.set.remove()– remover um item; lançarKeyErrorse não existir.set.clear()– esvaziar o conjunto.
s = {1, 2, 3}
s.add(4)
s.discard(99) # silent: 99 not in s
s.remove(2)
print(s)
Resultado:
{1, 3, 4}
2.10.4. Pertença¶
O operador in testa a pertença. Num conjunto é aproximadamente de tempo constante independentemente do tamanho – que é a principal razão para escolher um conjunto em vez de uma list quando só se precisa de perguntar «este valor está aqui»:
if "red" in colours:
print("colour is allowed")
Uma list com o mesmo conteúdo percorreria desde o início de cada vez, o que está bem para dez itens mas é lento para dez mil.
2.10.5. Operações sobre conjuntos¶
Dois conjuntos podem ser combinados com as operações matemáticas habituais. Cada uma tem tanto uma forma operador como uma forma método:
a | boua.union(b)– tudo o que está em qualquer dos conjuntos.a & boua.intersection(b)– apenas o que aparece em ambos.a - boua.difference(b)– emamas não emb.a ^ boua.symmetric_difference(b)– num 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)
Resultado:
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
As formas de operador são de leitura apenas; as formas de método aceitam qualquer iterável à direita, não apenas outro conjunto (a.union([5, 6])). Escolha a que se lê melhor no contexto.
2.10.6. O que pode estar num conjunto¶
Os elementos de um conjunto têm de ser hasháveis – a mesma restrição que as chaves de dict. int, float, str, bool, bytes e tuple (quando o seu conteúdo é ele próprio hashável) funcionam todos. list e dict não; tentar adicionar um deles levanta TypeError.
2.10.7. frozenset¶
Um set regular é mutável: cada chamada a add / remove / discard altera o objeto no próprio lugar. Essa mutabilidade impede-o de ser hashável, pelo que um conjunto não pode ser usado como chave de dict nem como membro de outro conjunto.
frozenset é a contraparte imutável. Tem as mesmas consultas e operadores (in, |, &, -, ^) que set, mas sem add / remove nem métodos que mutam. Como nada pode alguma vez alterar o seu conteúdo, o hash de um frozenset está bem definido – pelo que é hashável:
primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})
palettes = {
primary: "RGB",
secondary: "mixed",
}
print(palettes[primary])
Resultado:
RGB
Construa um frozenset a partir de qualquer iterável – frozenset() para o caso vazio, frozenset(some_set) para tirar uma fotografia 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 o usar:
Usar como chave de dicionário ou membro de conjunto. Em qualquer lugar em que um único valor não consegue capturar o que se precisa, um
frozensetde valores consegue – «o conjunto de funcionalidades suportadas por este controlador», «o conjunto de pinos que este perfil utiliza».Fixar uma constante. Um
frozensetde nomes permitidos ao nível do módulo não pode ser acidentalmente mutado por quem o chama; umsetregular pode. Prefirafrozensetpara tudo o que se destina a ser apenas de leitura após a construção.