Conjuntos (sets) em Python


Um set em Python é uma coleção de itens únicos (distintos).

Python provê formas eficientes e convenientes para criação e manipulação de sets. Nesta seção aprenderemos as principais características e funcionalidades dessa poderosa estrutura de dados.

Como criar um set em Python

Python nos permite criar sets de várias formas. Uma das mais formas mais frequentemente usadas é criar um set a partir de uma lista de elementos.

# Exemplo de criação de sets.
numeros = [1, 2, 2, 3, 3, 3]
numeros_distintos = set(numeros)
print("Números: ", numeros)
print("Números distintos: ", numeros_distintos)
Números: [1, 2, 2, 3, 3, 3]
Números distintos: {1, 2, 3}

Outra forma de criarmos sets em Python é criar um conjunto vazio e inserir elementos nele à medida que desejarmos. Vejamos um exemplo.

# Exemplo de criação de sets.

numeros = [1, 2, 2, 3, 3, 3]
numeros_distintos = set() # Cria um conjunto vazio
for num in numeros:
    numeros_distintos.add(num)
print("Números: ", numeros)
print("Números distintos: ", numeros_distintos)
Números: [1, 2, 2, 3, 3, 3]
Números distintos: {1, 2, 3}

Pontos importantes sobre o código acima:

  • numeros_distintos = set() cria um conjunto vazio.
  • numeros_distintos.add(num) adiciona um elemento ao conjunto criado anteriormente. Se o elemento não existir no conjunto, ele é adicionado. Caso contrário, o elemento é simplesmente descartado (não é inserido, uma vez que já está presente no conjunto).

Um outro ponto importante a ser discutido é a diferença de inserção de elementos em listas e conjuntos.

Para inserir um elemento em uma lista, podemos usar a função insert ou a função append, mas para inserir um elemento em um set podemos usar somente a função add. Essa diferença decorre do fato de que em uma lista temos um controle da posição dos elementos: insert nos permite inserir um elemento em uma posição específica da lista e append adiciona um elemento ao final da lista. Mas em um set não temos controle sobre a ordem em que os elementos são armazenados. A única garantia que temos é que elementos duplicados não serão inseridos.

Além disso, ao percorrer uma lista, sabemos que os elementos serão percorridos na ordem em que foram armazenados nela, mas em um set não temos esse controle: dados dois sets com os mesmos elementos, ao percorrê-los, pode ser que a ordem dos elementos seja diferente nos dois.

Como remover elementos de um conjunto

Para remover um elemento de um conjunto em Python, podemos usar a função remove ou a função discard.

Os exemplos abaixo mostram o uso dessas funções.

nums = set([1, 2, 2, 3, 3, 3])
nums.remove(2)
print("Números: ", nums)
Números: {1, 3}

A função remove deve ser usada somente se tivermos certeza que o elemento está presente no conjunto, pois se o elemento não estiver presente, a função remove causa uma exceção, como mostramos abaixo.

nums = set([1, 2, 2, 3, 3, 3])
nums.remove(4)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
KeyError: 4

Uma alternativa à função remove é a função discard, que remove um elemento do conjunto se o elemento estiver presente mas não faz nada caso o elemento não pertença ao conjunto.

Vejamos um exemplo.

nums = set([1, 2, 2, 3, 3, 3])
nums.discard(4)
nums.discard(2)
print(nums)
{1, 3}

Note que, apesar de 4 não estar presente no conjunto, nenhum erro é retornado ao tentarmos removê-lo.

Além disso, Python nos permite remover todos os elementos de um conjunto de uma vez. Para isso precisamos usar a função clear, como mostrado no exemplo abaixo.

nums = set([1, 2, 2, 3, 3, 3])
print("Números: ", nums)

nums.clear()
print("Números: ", nums)
Números: {1, 2, 3}
Números: set()
⚠️ CUIDADO

A notação set() é a forma de Python indicar um conjunto vazio. Talvez você estivesse esperando ver {} impresso aqui, mas {} é a forma que Python usa para indicar um dicionário vazio. Por isso, para evitar confusão, quando imprimimos um conjunto vazio, Python imprime set() ao invés de {}. Porém, quando o conjunto possui elementos, Python imprime {elem1, elem2, ...}. Isso é um pouco confuso, mas é importante ter essas nuances em mente.

Operações matemáticas com sets

Sets em Python podem parecer restritos, mas eles são estruturas de dados incrívelmente úteis e são muito bons naquilo que se propõem a fazer: armazenar elementos distintos.

Um set em Python pode ser visto como uma representação de um conjunto na matemática. E assim como na matemática, em que temos união, interseção e diferença de conjuntos (além de outras operações), em Python podemos realizar essas mesmas operações em sets de forma muito eficiente.

Como exemplo, considere a operação de união de conjuntos na matemática. Suponha que tenhamos dois conjuntos $A = {0, 1, 3, 5, 7, 9}$ e $B = {0, 2, 4, 6, 8}$ e desejamos construir um conjunto $C$ que é a união dos conjuntos $A$ e $B$. Matematicamente, temos que:

$C = A \cup B = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}$.

Note que o $0$ aparece em ambos conjuntos, mas aparece uma única vez no conjunto $C$.

Em Python, podemos realizar a operação acima da seguinte forma:

A = {0, 1, 3, 5, 7, 9}
B = {0, 2, 4, 6, 8}
C = A.union(B)
print(C)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Alternativamente, podemos realizar a operação acima de forma mais concisa, fazendo como mostrado no exemplo abaixo.

A = {0, 1, 3, 5, 7, 9}
B = {0, 2, 4, 6, 8}
C = A | B
print(C)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Outra operação comum em conjuntos é a interseção, que nos retorna os elementos que aparecem em ambos conjuntos. O exemplo abaixo ilustra essa operação.

A = {0, 1, 3, 5, 7, 9}
B = {0, 2, 4, 6, 8}
C = A & B # Equivalente a C = A.intersect(B)
print(C)
{0}

Python nos permite realizar muitas outras operações com conjuntos. Na tabela abaixo resumimos as principais.

Operações matemáticas em sets

Símbolo matemático Operador Python Descrição
$e \in S$ in elemento $e$ é membro de $S$
$A \subseteq B$ <= $A$ é um subconjunto de $B$
$A \subset B$ < $A$ é um subconjunto próprio de $B$
$A \cup B$ | $A$ união com $B$
$A \cap B$ & $A$ interseção com $B$
$A \setminus B$ - Diferença entre $A$ e $B$

Set comprehensions

Assim como as list comprehensions, as set comprehensions fornecem uma forma eficiente e legível de gerar conjuntos de elementos com base em uma expressão e, opcionalmente, uma ou mais condições.

A sintaxe básica de uma set comprehension é semelhante à de uma list comprehension, mas utiliza chaves ({}) em vez de colchetes ([]). A estrutura geral é a seguinte:

conjunto = {expressao for elemento in sequencia if condicao}

A condicao acima é opcional. Ela é usada para filtrar elementos ao construir o conjunto. Vejamos um exemplo sem o uso da condição e um exemplo com o uso dela.

Nosso primeiro exemplo consiste da identificação de palavras únicas em uma frase.

palavras = ["mundo", "mundo", "vasto", "mundo"]
palavras_unicas = {p for p in palavras}
print(palavras_unicas)
{'vasto', 'mundo'}

Vejamos agora alguns exemplos para ilustrar o uso de set comprehensions com condições de filtragem dos elementos:

numeros = [1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 10, 10]

# Números pares usando list comprehensions
numeros_pares_lista = [x for x in numeros if x % 2 == 0]
print(numeros_pares_lista)

# Números pares usando set comprehensions
numeros_pares_set = {x for x in numeros if x % 2 == 0}
print(numeros_pares_set)
{2, 4, 4, 6, 8, 10, 10, 10}
{2, 4, 6, 8, 10}

Note que a notação de set comprehensions nos permite criar o conjunto já descartando elementos duplicados, o que não acontece com as listas.

Exercícios

  1. Escreva uma função que recebe uma lista de elementos e retorne a quantidade de elementos únicos (distintos) na lista.
Clique para ver a solução
def num_elementos_distintos(elementos):
    return len(set(elementos))

num_elementos_distintos([1, 2, 2, 3, 3, 3, 4, 4, 4, 4,])
4
  1. Escreva uma função que recebe uma lista de elementos e retorne a quantidade de elementos duplicados na lista.
Clique para ver a solução
def num_elementos_duplicados(elementos):
    return len(elementos) - len(set(elementos))

num_elementos_duplicados([1, 2, 2, 3, 3, 3, 4, 4, 4, 4,])
6
  1. Escreva uma função que recebe uma string e retorna True se é possível converter a string para um número e obter um número válido. Por exemplo, ao convertermos a string “-123” para um número, obtemos $-123$, que é um número válido. Por outro lado, não é possível converter a string “12 3” para um número e obter um número válido, por causa do caractere de espaço presente na string. Para efeitos deste exercício, strings que começam com zero não geram números válidos, exceto se a string for simplesmente “0”.
Clique para ver a solução

Este exercício pode ser bastante complicado se tentarmos fazer uma solução avaliando todos os casos possíveis. Porém, a solução fica bem simples se tivermos em mente que quando escrevemos um número válido, existe um conjunto de caracteres que são permitidos. A ideia então é simplesmente verificar se a string de entrada possui somente caracteres permitidos.

def possivel_converter_versao_simples(s):
    if s.startswith("0") and s != "0":
        return False

    chars_permitidos = set("0123456789-.")
    for c in set(s):
        if not c in chars_permitidos:
            return False
    return True

possivel_converter_versao_simples("0")
possivel_converter_versao_simples("01")
possivel_converter_versao_simples("12.2")
possivel_converter_versao_simples("12 3")
possivel_converter_versao_simples("+432")
possivel_converter_versao_simples("432 ")
True
False
True
False
False
False

Podemos também usar as operações de conjunto que aprendemos nesta seção para escrever uma versão mais concisa do código. A ideia aqui é que a string de entrada precisa ser um subconjunto dos caracteres permitidos para que seja possível convertê-la em um número válido.

def possivel_converter(s):
    if s.startswith("0") and s != "0":
        return False

    chars_permitidos = set("0123456789-.")
    return set(s) <= chars_permitidos

possivel_converter("0")
possivel_converter("01")
possivel_converter("12.2")
possivel_converter("12 3")
possivel_converter("+432")
possivel_converter("432 ")
True
False
True
False
False
False