As tuplas são uma das estruturas de dados fundamentais em Python. Elas são semelhantes às listas, mas com uma diferença crucial: são imutáveis, ou seja, não podem ser alteradas após a criação. Isso as torna ideais para armazenar dados que não devem ser modificados, como coordenadas, configurações ou registros fixos.

Nesta aula, vamos explorar a imutabilidade, técnicas de empacotamento e desempacotamento, situações ideais para uso de tuplas e uma visão geral das named tuples, que combinam a imutabilidade das tuplas com a legibilidade dos dicionários.

Imutabilidade

Imutabilidade significa que, uma vez criada, uma tupla não pode ter seus elementos alterados, adicionados ou removidos. Isso é diferente das listas, que são mutáveis. A imutabilidade traz vantagens como segurança (garantia de que os dados não mudam inesperadamente) e eficiência (tuplas podem ser usadas como chaves de dicionários).

Exemplo de criação e tentativa de modificação:

# Criando uma tupla
coordenadas = (10, 20)
print(coordenadas[0])  # 10

# Tentando alterar um elemento (isso gera erro)
# coordenadas[0] = 15  # TypeError: 'tuple' object does not support item assignment

# Tentando adicionar um elemento
# coordenadas.append(30)  # AttributeError: 'tuple' object has no attribute 'append'

Note que, embora a tupla em si seja imutável, se ela contiver objetos mutáveis (como listas), esses objetos ainda podem ser modificados. Por exemplo:

tupla_com_lista = ([1, 2], 3)
tupla_com_lista[0].append(3)  # Isso é permitido, pois a lista é mutável
print(tupla_com_lista)  # ([1, 2, 3], 3)

Isso é um ponto importante: a imutabilidade da tupla se refere à identidade dos elementos (as referências), não ao conteúdo dos objetos referenciados.

Empacotamento e desempacotamento

Empacotamento é o ato de criar uma tupla a partir de valores separados por vírgulas. Desempacotamento é o inverso: atribuir os elementos de uma tupla a variáveis individuais. Essas operações são muito utilizadas em Python para tornar o código mais legível e conciso.

Exemplo de empacotamento:

# Empacotamento
empacotado = 1, 2, 3
print(type(empacotado))  # 
print(empacotado)        # (1, 2, 3)

Exemplo de desempacotamento:

# Desempacotamento
a, b, c = empacotado
print(a, b, c)  # 1 2 3

O desempacotamento é muito usado em loops e atribuições múltiplas. Por exemplo:

# Atribuição múltipla com tuplas
x, y = 10, 20
print(x, y)  # 10 20

# Trocando valores de forma elegante
x, y = y, x
print(x, y)  # 20 10

Também é possível usar o operador * para desempacotar parte da tupla:

# Desempacotamento com *
primeiro, *resto = (1, 2, 3, 4)
print(primeiro)  # 1
print(resto)     # [2, 3, 4]

Quando usar

Tuplas são recomendadas em situações onde os dados não devem ser modificados, como coordenadas, datas, configurações fixas, ou para representar registros simples. Elas são mais leves que listas e podem ser usadas como chaves em dicionários, ao contrário das listas.

Exemplos de uso comum:

  • Retorno de múltiplos valores em funções: return x, y retorna uma tupla.
  • Armazenar dias da semana, meses ou outras constantes.
  • Chaves de dicionários: dicionario = {(1, 2): 'ponto'}
  • Desempacotamento em loops: for chave, valor in dicionario.items():

Comparação com listas: use tuplas quando a imutabilidade for desejada ou quando você quiser garantir que os dados não serão alterados. Use listas quando precisar modificar a coleção frequentemente.

Named tuples (visão geral)

As named tuples são uma extensão das tuplas, disponíveis no módulo collections. Elas permitem que você crie tuplas com campos nomeados, combinando a imutabilidade das tuplas com a legibilidade dos dicionários. Cada instância de uma named tuple tem atributos acessíveis por nome, além do acesso por índice.

Exemplo de criação e uso:

from collections import namedtuple

# Definindo uma named tuple 'Ponto' com campos x e y
Ponto = namedtuple('Ponto', ['x', 'y'])

# Criando uma instância
p1 = Ponto(10, 20)
print(p1.x)  # 10
print(p1.y)  # 20
print(p1[0]) # 10 (acesso por índice ainda funciona)

# Imutabilidade mantida
# p1.x = 30  # AttributeError: can't set attribute

Named tuples são úteis para representar registros simples, como coordenadas, dados de um banco de dados ou resultados de consultas, pois tornam o código mais legível e auto-documentado.

Boas práticas

Algumas boas práticas ao trabalhar com tuplas:

  • Use tuplas para dados heterogêneos (diferentes tipos) que são fixos.
  • Preferir tuple a list quando a imutabilidade for desejada.
  • Evite tuplas com muitos elementos; considere usar uma named tuple ou uma classe para maior clareza.
  • Ao desempacotar, use nomes de variáveis significativos para melhor legibilidade.

Referências

Exercícios

  1. Crie uma tupla chamada cores contendo três strings: "vermelho", "verde" e "azul". Em seguida, tente alterar o primeiro elemento para "amarelo" e veja o erro. Explique por que ocorre o erro.

    ✓ Resposta:
    cores = ("vermelho", "verde", "azul")
    # cores[0] = "amarelo"  # TypeError: 'tuple' object does not support item assignment
    

    O erro ocorre porque tuplas são imutáveis: não é possível alterar seus elementos após a criação.

  2. Dada a tupla valores = (10, 20, 30, 40, 50), use desempacotamento para atribuir os dois primeiros valores às variáveis a e b, e o restante a uma lista c. Imprima os resultados.

    ✓ Resposta:
    valores = (10, 20, 30, 40, 50)
    a, b, *c = valores
    print(a)  # 10
    print(b)  # 20
    print(c)  # [30, 40, 50]
    
  3. Crie uma função dividir que receba dois números e retorne uma tupla com o quociente e o resto da divisão inteira. Teste com 10 e 3.

    ✓ Resposta:
    def dividir(a, b):
        return a // b, a % b
    
    quociente, resto = dividir(10, 3)
    print(quociente, resto)  # 3 1
    
  4. Use uma named tuple para representar um ponto 3D com campos x, y, z. Crie um ponto (1, 2, 3) e acesse o valor de y pelo nome e pelo índice.

    ✓ Resposta:
    from collections import namedtuple
    
    Ponto3D = namedtuple('Ponto3D', ['x', 'y', 'z'])
    p = Ponto3D(1, 2, 3)
    print(p.y)   # 2
    print(p[1])  # 2
    
  5. Explique por que tuplas podem ser usadas como chaves em dicionários, mas listas não. Dê um exemplo.

    ✓ Resposta:

    Dicionários exigem que as chaves sejam hasháveis. Tuplas são imutáveis, portanto hasháveis, enquanto listas são mutáveis e não podem ser hashadas. Exemplo:

    d = {}
    d[(1, 2)] = "ponto"  # Funciona
    # d[[1, 2]] = "ponto"  # TypeError: unhashable type: 'list'