Tuplas: imutabilidade e quando usar
1. O que são tuplas e como criá-las
Tuplas são estruturas de dados sequenciais em Python que armazenam coleções ordenadas de elementos. Diferentemente das listas, tuplas são imutáveis — uma vez criadas, não podem ser alteradas. A sintaxe básica utiliza parênteses e vírgulas como delimitadores.
# Criando tuplas de diferentes formas
tupla_vazia = ()
tupla_com_um_elemento = (42,) # vírgula obrigatória
tupla_com_varios_elementos = (1, 2, 3, 'Python', True)
tupla_sem_parenteses = 10, 20, 30 # tupla implícita
tupla_com_funcao = tuple([4, 5, 6]) # convertendo lista em tupla
print(type(tupla_sem_parenteses)) # <class 'tuple'>
print(tupla_sem_parenteses) # (10, 20, 30)
A vírgula é o verdadeiro delimitador das tuplas. Os parênteses são opcionais na maioria dos casos, exceto para criar tuplas vazias ou evitar ambiguidades.
2. Imutabilidade: o coração das tuplas
A imutabilidade é a característica que define as tuplas. Enquanto listas podem ser modificadas livremente, tuplas oferecem garantia de que seu conteúdo não mudará.
# Comparação entre lista e tupla
lista_exemplo = [1, 2, 3]
tupla_exemplo = (1, 2, 3)
# Operações permitidas em listas, mas proibidas em tuplas
lista_exemplo[0] = 100 # OK
# tupla_exemplo[0] = 100 # TypeError: 'tuple' object does not support item assignment
lista_exemplo.append(4) # OK
# tupla_exemplo.append(4) # AttributeError: 'tuple' object has no attribute 'append'
del lista_exemplo[0] # OK
# del tupla_exemplo[0] # TypeError: 'tuple' object doesn't support item deletion
No entanto, cuidado com tuplas que contêm objetos mutáveis:
# Tupla com lista interna — falsa imutabilidade
tupla_mista = (1, [2, 3], 4)
tupla_mista[1].append(5) # Isso funciona!
print(tupla_mista) # (1, [2, 3, 5], 4) — a tupla em si não mudou, mas seu conteúdo interno sim
A tupla ainda contém os mesmos objetos, mas a lista interna foi alterada. A imutabilidade se aplica à referência, não ao conteúdo dos objetos mutáveis.
3. Operações permitidas com tuplas
Apesar da imutabilidade, muitas operações úteis são permitidas:
# Acesso por índice e fatiamento
tupla = (10, 20, 30, 40, 50)
print(tupla[0]) # 10
print(tupla[-1]) # 50
print(tupla[1:4]) # (20, 30, 40)
# Concatenação e repetição
tupla1 = (1, 2, 3)
tupla2 = (4, 5, 6)
print(tupla1 + tupla2) # (1, 2, 3, 4, 5, 6)
print(tupla1 * 3) # (1, 2, 3, 1, 2, 3, 1, 2, 3)
# Métodos úteis
numeros = (1, 2, 2, 3, 2, 4)
print(numeros.count(2)) # 3
print(numeros.index(3)) # 3
# Verificação de pertencimento
print(5 in tupla1) # False
print(2 in tupla1) # True
4. Desempacotamento de tuplas
O desempacotamento é uma das características mais elegantes das tuplas:
# Atribuição múltipla
coordenadas = (10, 20)
x, y = coordenadas
print(f"x={x}, y={y}") # x=10, y=20
# Troca de valores (swap) sem variável temporária
a, b = 5, 10
a, b = b, a
print(a, b) # 10 5
# Uso do operador * para capturar múltiplos itens
primeiro, *meio, ultimo = (1, 2, 3, 4, 5)
print(primeiro) # 1
print(meio) # [2, 3, 4]
print(ultimo) # 5
# Desempacotamento em loops
nomes = ('Ana', 'Carlos', 'Maria')
for indice, nome in enumerate(nomes):
print(f"{indice}: {nome}")
# Com zip()
idades = (25, 30, 28)
for nome, idade in zip(nomes, idades):
print(f"{nome} tem {idade} anos")
5. Quando usar tuplas em vez de listas
Tuplas são ideais em situações específicas:
# 1. Dados que não devem ser alterados (constantes)
DIAS_SEMANA = ('Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado', 'Domingo')
CONFIGURACAO_PADRAO = (800, 600, 24) # resolução de tela
# 2. Chaves de dicionários (requisito de hashabilidade)
localizacoes = {
(40.7128, -74.0060): 'Nova York',
(34.0522, -118.2437): 'Los Angeles',
(41.8781, -87.6298): 'Chicago'
}
print(localizacoes[(40.7128, -74.0060)]) # Nova York
# 3. Retorno múltiplo de funções
def dividir_e_resto(dividendo, divisor):
quociente = dividendo // divisor
resto = dividendo % divisor
return quociente, resto # retorna uma tupla
q, r = dividir_e_resto(17, 5)
print(f"Quociente: {q}, Resto: {r}") # Quociente: 3, Resto: 2
# 4. Performance: tuplas são mais leves
import sys
lista_grande = list(range(1000))
tupla_grande = tuple(range(1000))
print(f"Lista: {sys.getsizeof(lista_grande)} bytes") # ~8856 bytes
print(f"Tupla: {sys.getsizeof(tupla_grande)} bytes") # ~8040 bytes
6. Tuplas nomeadas (namedtuple)
O módulo collections oferece uma extensão poderosa das tuplas:
from collections import namedtuple
# Criando uma namedtuple
Ponto = namedtuple('Ponto', ['x', 'y', 'z'])
ponto1 = Ponto(10, 20, 30)
# Acesso por nome (mais legível)
print(ponto1.x) # 10
print(ponto1.y) # 20
# Acesso por índice (ainda funciona)
print(ponto1[0]) # 10
# Comparação com dicionário
ponto_dict = {'x': 10, 'y': 20, 'z': 30}
print(ponto1.x == ponto_dict['x']) # True — mas namedtuple é mais eficiente
# Vantagens: imutabilidade, desempenho, legibilidade
Pessoa = namedtuple('Pessoa', ['nome', 'idade', 'cidade'])
joao = Pessoa('João', 30, 'São Paulo')
print(f"{joao.nome}, {joao.idade} anos, mora em {joao.cidade}")
7. Comparação e ordenação de tuplas
Tuplas suportam comparação lexicográfica elemento a elemento:
# Comparação elemento a elemento
print((1, 2, 3) < (1, 2, 4)) # True — compara até encontrar diferença
print((1, 2, 3) < (1, 3, 0)) # True — 2 < 3
print((1, 2) < (1, 2, 3)) # True — tupla menor é considerada menor
# Ordenação de listas de tuplas
alunos = [
('Ana', 85),
('Carlos', 92),
('Beatriz', 78),
('Daniel', 85)
]
# Ordenar por nota (segundo elemento)
alunos_ordenados = sorted(alunos, key=lambda x: x[1])
print(alunos_ordenados)
# [('Beatriz', 78), ('Ana', 85), ('Daniel', 85), ('Carlos', 92)]
# Ordenação personalizada com tuplas como chave
def chave_ordenacao(aluno):
return (-aluno[1], aluno[0]) # nota decrescente, nome alfabético
alunos_ordenados2 = sorted(alunos, key=chave_ordenacao)
print(alunos_ordenados2)
# [('Carlos', 92), ('Ana', 85), ('Daniel', 85), ('Beatriz', 78)]
8. Boas práticas e armadilhas comuns
# Tuplas como elementos de sets
set_tuplas = {(1, 2), (3, 4), (1, 2)} # OK — tuplas são hasháveis
print(set_tuplas) # {(1, 2), (3, 4)}
# Cuidado: tuplas com listas não podem ser hasháveis
# set_tuplas_mutaveis = {(1, [2, 3]), (4, 5)} # TypeError: unhashable type: 'list'
# Quando converter entre tupla e lista
# Evite conversões desnecessárias — escolha a estrutura certa desde o início
dados = [1, 2, 3]
dados_tupla = tuple(dados) # conversão para proteger dados
dados_lista = list(dados_tupla) # conversão para modificar
# Boa prática: documente quando usar tuplas
# Ruim: usar tupla para dados que precisam ser modificados
# Bom: usar tupla para dados que representam registros imutáveis
def obter_configuracao():
"""Retorna configuração como tupla imutável."""
return ('localhost', 8080, True, 'utf-8') # host, porta, ssl, encoding
# Armadilha: esquecer a vírgula em tuplas de um elemento
nao_e_tupla = (42) # Isso é um inteiro!
eh_tupla = (42,) # Isso é uma tupla
print(type(nao_e_tupla)) # <class 'int'>
print(type(eh_tupla)) # <class 'tuple'>
Referências
- Documentação oficial de Python: Tuples — Tutorial oficial sobre sequências, incluindo tuplas e suas operações básicas
- Python Data Structures: Lists vs Tuples — Artigo do Real Python comparando listas e tuplas com exemplos práticos
- PEP 484 — Type Hints: NamedTuples — Especificação oficial sobre tipagem de namedtuples em Python
- Python's namedtuple: Creating Lightweight Data Classes — Guia completo sobre namedtuple do Real Python
- TimeComplexity: Python Wiki — Tabela de complexidade de tempo para operações com tuplas e outras estruturas
- Effective Python: Item 22 — Prefer Helper Classes Over Bookkeeping with Dictionaries and Tuples — Boas práticas sobre quando usar tuplas versus classes e dicionários
- Python Module of the Week: collections.namedtuple — Exemplos detalhados do módulo collections focado em namedtuple