37. Funções Embutidas e Ferramentas Úteis
O Python oferece uma coleção rica de funções embutidas que estão sempre disponíveis sem precisar importar nenhum módulo. Essas funções formam a base da programação Python do dia a dia, ajudando você a trabalhar de forma eficiente com dados, sequências e coleções. Neste capítulo, vamos explorar as ferramentas embutidas mais úteis do Python e aprender como aproveitá-las para escrever código mais limpo e expressivo.
Entendendo o Sistema de Tipos do Python
Antes de mergulhar em funções embutidas específicas, é útil entender como o Python organiza seus tipos de dados. Esse conhecimento vai ajudar você a prever quais operações funcionam com quais tipos e a entender mensagens de erro quando elas ocorrerem.
Os tipos de dados do Python podem ser entendidos a partir de duas perspectivas complementares:
Hierarquia de Tipos: Como os Tipos se Relacionam
Isso mostra como o Python organiza tipos em famílias com base no que eles SÃO.
Visão Baseada em Capacidades: O Que os Tipos Conseguem Fazer
Para funções embutidas, o que importa mais é o que os tipos conseguem FAZER:
Principais capacidades:
- Iterável (iterable): Pode ser usado em loops
for→ Funciona comsum(),any(),all(),sorted() - Coleção (collection): Iterável com
len()→ Funciona comlen()e com o operadorin - Sequência (sequence): Coleção com indexação → Suporta
[index]e fatiamento[start:end]
Por Que Isso Importa
Funções embutidas exigem capacidades específicas:
| Função | Requer | Funciona com |
|---|---|---|
len() | Coleção | str, list, dict, set, tuple |
sum() | Iterável de números | list, tuple, set, range, generator |
sorted() | Iterável | str, list, dict, set, tuple |
[index] | Sequência | str, list, tuple, range |
Entender essas categorias ajuda você a:
- Prever quais funções funcionam com quais tipos
- Entender mensagens de erro como "object is not iterable"
- Saber quando você pode indexar (
[0]) vs quando você só pode iterar (for)
37.1) Funções Embutidas Comuns (len, sum, min, max, abs, round)
As funções embutidas mais usadas do Python ajudam você a realizar operações comuns sobre dados sem escrever loops ou lógica complexa. Essas funções são otimizadas, legíveis e formam a base do código Pythonic.
37.1.1) Medindo Comprimento com len()
A função len() retorna o número de itens em uma coleção. Ela funciona com strings, listas(list), tuplas(tuple), dicionários(dict), conjuntos(set) e qualquer outro tipo de coleção.
# Contando caracteres em uma string
message = "Hello, World!"
print(len(message)) # Output: 13
# Contando elementos em uma lista
scores = [85, 92, 78, 90, 88]
print(len(scores)) # Output: 5
# Contando pares chave-valor em um dicionário
student = {"name": "Bob", "age": 21, "major": "CS"}
print(len(student)) # Output: 3
# Contando itens únicos em um conjunto
unique_ids = {101, 102, 103, 101, 102} # Duplicatas removidas
print(len(unique_ids)) # Output: 3A função len() é particularmente útil quando você precisa saber o tamanho dos dados antes de processá-los:
# Processando dados com base no tamanho
data = [12, 45, 23, 67, 89, 34]
if len(data) < 5:
print("Not enough data for analysis")
else:
print(f"Analyzing {len(data)} data points") # Output: Analyzing 6 data points
average = sum(data) / len(data)
print(f"Average: {average}") # Output: Average: 45.037.1.2) Calculando Totais com sum()
A função sum() soma todos os números em um iterável(iterable). Ela é muito mais limpa do que escrever um loop para acumular valores.
# Somando uma lista de números
prices = [19.99, 24.50, 15.75, 32.00]
total = sum(prices)
print(f"Total: ${total}") # Output: Total: $92.24
# Somando uma tupla
daily_steps = (8500, 10200, 7800, 9500, 11000)
weekly_total = sum(daily_steps)
print(f"Total steps this week: {weekly_total}") # Output: Total steps this week: 47000
# Somando um range
total_1_to_100 = sum(range(1, 101))
print(total_1_to_100) # Output: 5050Um exemplo prático combinando sum() e len() para calcular médias:
# Calcular a média da pontuação do teste
test_scores = [88, 92, 79, 85, 90, 87]
total_score = sum(test_scores)
num_tests = len(test_scores)
average_score = total_score / num_tests
print(f"Average score: {average_score:.1f}") # Output: Average score: 86.8Limitação importante: sum() só funciona com números. Você não pode usá-la para concatenar strings ou combinar listas:
# Isto gera TypeError
words = ["Hello", " ", "World"]
# sentence = sum(words) # TypeError: unsupported operand type(s)37.1.3) Encontrando Extremos com min() e max()
As funções min() e max() encontram os menores e maiores valores em um iterável(iterable). Elas funcionam com números, strings e quaisquer objetos que possam ser comparados.
# Encontrando os números mínimo e máximo
temperatures = [72, 68, 75, 70, 73, 69]
coldest = min(temperatures)
warmest = max(temperatures)
print(f"Temperature range: {coldest}°F to {warmest}°F")
# Output: Temperature range: 68°F to 75°F
# Encontrando as strings mínima e máxima (alfabeticamente)
names = ["Zoe", "Alice", "Bob", "Charlie"]
first_alphabetically = min(names)
last_alphabetically = max(names)
print(f"First: {first_alphabetically}, Last: {last_alphabetically}")
# Output: First: Alice, Last: ZoeVocê também pode passar vários argumentos diretamente em vez de uma coleção:
# Comparando valores individuais
lowest = min(45, 23, 67, 12, 89)
highest = max(45, 23, 67, 12, 89)
print(f"Lowest: {lowest}, Highest: {highest}")
# Output: Lowest: 12, Highest: 89
# Útil para comparar alguns valores específicos
price1 = 19.99
price2 = 24.50
price3 = 15.75
cheapest = min(price1, price2, price3)
print(f"Cheapest option: ${cheapest}") # Output: Cheapest option: $15.7537.1.4) Obtendo Valores Absolutos com abs()
A função abs() retorna o valor absoluto de um número — a distância dele até o zero, sempre positiva. Isso é útil quando você se importa com a magnitude, mas não com a direção.
# Valor absoluto de números negativos
print(abs(-42)) # Output: 42
print(abs(-3.14)) # Output: 3.14
# Valor absoluto de números positivos (sem mudanças)
print(abs(42)) # Output: 42
print(abs(3.14)) # Output: 3.14
# Valor absoluto de zero
print(abs(0)) # Output: 0Um caso de uso comum é calcular diferenças onde a direção não importa:
# Calcular mudança de temperatura (apenas magnitude)
morning_temp = 65
evening_temp = 72
temperature_change = abs(evening_temp - morning_temp)
print(f"Temperature changed by {temperature_change}°F")
# Output: Temperature changed by 7°F37.1.5) Arredondando Números com round()
A função round() arredonda um número para uma quantidade especificada de casas decimais. Sem o segundo argumento, ela arredonda para o inteiro mais próximo.
# Arredondando para o inteiro mais próximo
print(round(3.7)) # Output: 4
print(round(3.2)) # Output: 3
print(round(3.5)) # Output: 4
print(round(4.5)) # Output: 4 (arredonda para o par mais próximo)
# Arredondando para casas decimais específicas
price = 19.876
print(round(price, 2)) # Output: 19.88 (2 casas decimais)
print(round(price, 1)) # Output: 19.9 (1 casa decimal)
# Arredondando para casas decimais negativas (arredonda para dezenas, centenas, etc.)
population = 1234567
print(round(population, -3)) # Output: 1235000 (milhar mais próximo)
print(round(population, -4)) # Output: 1230000 (dezena de milhar mais próxima)Observação sobre valores “no meio”: Ao arredondar um número que está exatamente no meio do caminho entre dois inteiros, o Python tem uma regra especial. Por exemplo, 2.5 está exatamente no meio entre 2 e 3. Você pode esperar que ele arredonde para cima, para 3, mas o Python arredonda para o vizinho que for um número par — neste caso, 2.
Isso é chamado de "banker's rounding" ou "round half to even". Faz parte do padrão IEEE 754 e ajuda a reduzir viés ao longo de muitas operações de arredondamento.
# Valores no meio arredondam para o par mais próximo
print(round(0.5)) # Output: 0 (0 is even)
print(round(1.5)) # Output: 2 (2 is even)
print(round(2.5)) # Output: 2 (2 is even)
print(round(3.5)) # Output: 4 (4 is even)
print(round(4.5)) # Output: 4 (4 is even)37.2) Enumerando Sequências com enumerate()
Ao iterar por uma sequência, você muitas vezes precisa tanto do elemento quanto da posição dele. A função enumerate() fornece os dois, eliminando a necessidade de variáveis contadoras manuais.
37.2.1) O Problema com Contadores Manuais
Antes de aprender sobre enumerate(), programadores frequentemente usam uma variável contadora para acompanhar a posição:
# Abordagem com contador manual (funciona, mas não é ideal)
fruits = ["apple", "banana", "cherry", "date"]
index = 0
for fruit in fruits:
print(f"{index}: {fruit}")
index += 1
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: dateEssa abordagem tem várias desvantagens:
- Variável extra para gerenciar (
index) - É fácil esquecer de incrementar o contador
37.2.2) Usando enumerate() para Posição e Valor
A função enumerate() resolve esse problema de forma elegante. Ela recebe um iterável(iterable) e retorna pares de (índice, elemento):
# Usando enumerate() - mais limpo e mais Pythonic
fruits = ["apple", "banana", "cherry", "date"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# Output:
# 0: apple
# 1: banana
# 2: cherry
# 3: dateA sintaxe for index, fruit in enumerate(fruits) usa desempacotamento de tupla (tuple unpacking) (como vimos no Capítulo 15). A cada iteração, enumerate() fornece uma tupla como (0, "apple"), que é desempacotada nas variáveis index e fruit.
Aqui está o que enumerate() realmente produz:
# Vendo a saída de enumerate() diretamente
fruits = ["apple", "banana", "cherry"]
enumerated = list(enumerate(fruits))
print(enumerated)
# Output: [(0, 'apple'), (1, 'banana'), (2, 'cherry')]37.2.3) Iniciando a Enumeração a Partir de um Número Diferente
Por padrão, enumerate() começa a contar a partir de 0. Você pode especificar um número inicial diferente com o parâmetro start:
# Começar a contar a partir de 1 (útil para exibição)
tasks = ["Write code", "Test code", "Deploy code"]
for number, task in enumerate(tasks, start=1):
print(f"Step {number}: {task}")
# Output:
# Step 1: Write code
# Step 2: Test code
# Step 3: Deploy codeIsso é particularmente útil ao exibir listas numeradas para usuários, que normalmente esperam que a contagem comece em 1:
# Menu com opções numeradas
menu_items = ["New Game", "Load Game", "Settings", "Quit"]
print("Main Menu:")
for number, item in enumerate(menu_items, start=1):
print(f"{number}. {item}")
# Output:
# Main Menu:
# 1. New Game
# 2. Load Game
# 3. Settings
# 4. Quit37.2.4) enumerate() com Strings e Outros Iteráveis
A função enumerate() funciona com qualquer iterável(iterable), não apenas listas:
# Enumerando caracteres em uma string
word = "Python"
for position, letter in enumerate(word):
print(f"Letter {position}: {letter}")
# Output:
# Letter 0: P
# Letter 1: y
# Letter 2: t
# Letter 3: h
# Letter 4: o
# Letter 5: n
# Enumerando uma tupla
coordinates = (10, 20, 30, 40)
for index, value in enumerate(coordinates):
print(f"Coordinate {index}: {value}")
# Output:
# Coordinate 0: 10
# Coordinate 1: 20
# Coordinate 2: 30
# Coordinate 3: 40A função enumerate() torna o código mais legível e menos propenso a erros. Sempre que você precisar tanto da posição quanto do valor em um loop, prefira enumerate() em vez de gerenciar um contador manualmente.
37.3) Combinando Sequências com zip()
A função zip() combina vários iteráveis(iterables) elemento a elemento, criando pares (ou tuplas) de elementos correspondentes. Isso é inestimável quando você precisa processar dados relacionados de sequências separadas simultaneamente.
37.3.1) Entendendo Como zip() Funciona
A função zip() recebe dois ou mais iteráveis e retorna um iterador(iterator) de tuplas, onde cada tupla contém um elemento de cada iterável de entrada:
# Combinando duas listas
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
combined = list(zip(names, ages))
print(combined)
# Output: [('Alice', 25), ('Bob', 30), ('Charlie', 35)]O nome "zip" vem do zíper de roupas — ele combina dois lados separados em uma estrutura unida, elemento por elemento.
Aqui está uma representação visual de como zip() emparelha elementos:
37.3.2) Usando zip() em Loops
O uso mais comum de zip() é em loops for, quando você precisa iterar sobre várias sequências simultaneamente:
# Processando dados em paralelo
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
for student, score in zip(students, scores):
print(f"{student} scored {score}")
# Output:
# Alice scored 92
# Bob scored 85
# Charlie scored 88
# Diana scored 95Isso é muito mais limpo do que usar índices:
# Sem zip() - mais complexo e propenso a erros
students = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 88, 95]
for i in range(len(students)):
print(f"{students[i]} scored {scores[i]}")
# Same output, but more code and potential for index errors37.3.3) Lidando com Sequências de Comprimentos Diferentes
Quando as sequências de entrada têm comprimentos diferentes, zip() para quando a sequência mais curta se esgota:
# Sequências com comprimentos diferentes
names = ["Alice", "Bob", "Charlie", "Diana"]
ages = [25, 30] # Apenas 2 idades
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Output:
# Alice is 25 years old
# Bob is 30 years old
# (Charlie and Diana are not processed)Esse comportamento evita erros, mas pode levar à perda silenciosa de dados se você não tomar cuidado. Sempre verifique se suas sequências têm os comprimentos esperados:
# Verificando incompatibilidade de comprimentos
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30]
if len(names) != len(ages):
print(f"Warning: {len(names)} names but {len(ages)} ages")
print("Only processing the first", min(len(names), len(ages)), "entries")
# Output: Warning: 3 names but 2 ages
# Output: Only processing the first 2 entries
# Continue com zip() - ele vai parar na menor sequência
for name, age in zip(names, ages):
print(f"{name} is {age} years old")37.3.4) Fazendo zip de Mais de Duas Sequências
A função zip() pode combinar qualquer número de iteráveis:
# Combinando três sequências
products = ["Laptop", "Mouse", "Keyboard"]
prices = [999.99, 24.99, 79.99]
quantities = [5, 20, 15]
print("Inventory Report:")
for product, price, quantity in zip(products, prices, quantities):
total_value = price * quantity
print(f"{product}: ${price} × {quantity} = ${total_value:.2f}")
# Output:
# Inventory Report:
# Laptop: $999.99 × 5 = $4999.95
# Mouse: $24.99 × 20 = $499.80
# Keyboard: $79.99 × 15 = $1199.8537.3.5) Criando Dicionários com zip()
Um padrão poderoso é usar zip() para criar dicionários(dict) a partir de sequências separadas de chaves e valores:
# Criando um dicionário a partir de duas listas
keys = ["name", "age", "city"]
values = ["Alice", 25, "Boston"]
person = dict(zip(keys, values))
print(person)
# Output: {'name': 'Alice', 'age': 25, 'city': 'Boston'}37.4) Agregação Booleana com any() e all()
As funções any() e all() testam condições ao longo de iteráveis inteiros, retornando um único resultado booleano. Elas são ferramentas poderosas para validação e tomada de decisão com base em múltiplas condições.
37.4.1) Entendendo any(): True se Pelo Menos um Elemento for True
A função any() retorna True se pelo menos um elemento em um iterável(iterable) for truthy (avaliar como True). Se todos os elementos forem falsy, ela retorna False:
# Exemplos básicos de any()
print(any([True, False, False])) # Output: True (at least one True)
print(any([False, False, False])) # Output: False (all False)
print(any([False, True, True])) # Output: True (multiple True values)
# Iteráveis vazios
print(any([])) # Output: False (no elements to be True)A função any() usa as regras de truthiness do Python (como vimos no Capítulo 7). Números diferentes de zero, strings não vazias e coleções não vazias são truthy:
# any() com diferentes valores truthy/falsy
print(any([0, 0, 1])) # Output: True (1 is truthy)
print(any([0, 0, 0])) # Output: False (all zeros are falsy)
print(any(["", "", "text"])) # Output: True ("text" is truthy)
print(any(["", "", ""])) # Output: False (empty strings are falsy)37.4.2) Usos Práticos de any()
Exemplo: Verificando se alguma condição é atendida
# Verificar se alguma nota está reprovando (abaixo de 60)
scores = [75, 82, 55, 90, 88]
has_failing_grade = any(score < 60 for score in scores)
if has_failing_grade:
print("Warning: At least one failing grade")
# Output: Warning: At least one failing grade
else:
print("All grades are passing")37.4.3) Entendendo all(): True Somente se Todos os Elementos forem True
A função all() retorna True somente se todos os elementos em um iterável(iterable) forem truthy. Se algum elemento for falsy, ela retorna False:
# Exemplos básicos de all()
print(all([True, True, True])) # Output: True (all True)
print(all([True, False, True])) # Output: False (one False)
print(all([True, True, False])) # Output: False (one False)
# Iteráveis vazios
print(all([])) # Output: True (vacuous truth - no False elements)O comportamento com iteráveis vazios pode parecer surpreendente: all([]) retorna True. Isso se chama verdade vacuosa (vacuous truth) — a afirmação "todos os elementos são True" é tecnicamente verdadeira quando não há elementos para contradizê-la.
# all() com diferentes valores truthy/falsy
print(all([1, 2, 3])) # Output: True (all non-zero)
print(all([1, 0, 3])) # Output: False (0 is falsy)
print(all(["a", "b", "c"])) # Output: True (all non-empty)
print(all(["a", "", "c"])) # Output: False (empty string is falsy)37.4.4) Usos Práticos de all()
Exemplo: Validando que todas as condições são atendidas
# Verificar se todas as notas estão aprovadas (60 ou acima)
scores = [75, 82, 68, 90, 88]
all_passing = all(score >= 60 for score in scores)
if all_passing:
print("Congratulations! All grades are passing")
# Output: Congratulations! All grades are passing
else:
print("Some grades need improvement")37.4.5) Avaliação de Curto-Circuito em any() e all()
Tanto any() quanto all() usam avaliação de curto-circuito (short-circuit evaluation) (como vimos no Capítulo 9). Elas param de verificar assim que o resultado é determinado:
# Função que imprime quando é chamada (para mostrar a execução)
def is_positive(n):
print(f"Checking {n}")
return n > 0
# any() para no primeiro True
print("Testing any():")
numbers = [0, 0, 1, 2, 3]
result = any(is_positive(n) for n in numbers)
# Output:
# Testing any():
# Checking 0
# Checking 0
# Checking 1
# (Stops here - doesn't check 2 or 3)
print(f"Result: {result}") # Output: Result: True
print("\nTesting all():")
numbers = [1, 2, 0, 3, 4]
result = all(is_positive(n) for n in numbers)
# Output:
# Testing all():
# Checking 1
# Checking 2
# Checking 0
# (Stops here - doesn't check 3 or 4)
print(f"Result: {result}") # Output: Result: FalseIsso torna any() e all() eficientes — elas não perdem tempo verificando elementos depois que o resultado é determinado.
37.5) Ordenando com sorted() e Chaves Personalizadas
A função sorted() cria uma nova lista(list) ordenada a partir de qualquer iterável(iterable). Diferente do método .sort() (que só funciona em listas e as modifica no lugar), sorted() funciona com qualquer iterável e sempre retorna uma nova lista.
37.5.1) Ordenação Básica com sorted()
A função sorted() organiza os elementos em ordem crescente por padrão:
# Ordenando números
numbers = [42, 17, 93, 8, 55]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # Output: [8, 17, 42, 55, 93]
# A lista original não é alterada
print(numbers) # Output: [42, 17, 93, 8, 55]
# Ordenando strings (alfabeticamente)
names = ["Charlie", "Alice", "Bob", "Diana"]
sorted_names = sorted(names)
print(sorted_names) # Output: ['Alice', 'Bob', 'Charlie', 'Diana']A função sorted() funciona com qualquer iterável, não apenas listas:
# Ordenando uma tupla (retorna uma lista)
coordinates = (5, 2, 8, 1, 9)
sorted_coords = sorted(coordinates)
print(sorted_coords) # Output: [1, 2, 5, 8, 9]
# Ordenando uma string (retorna lista de caracteres)
word = "python"
sorted_letters = sorted(word)
print(sorted_letters) # Output: ['h', 'n', 'o', 'p', 't', 'y']
# Ordenando um conjunto (retorna uma lista ordenada)
unique_numbers = {5, 8, 2, 1}
sorted_unique = sorted(unique_numbers)
print(sorted_unique) # Output: [1, 2, 5, 8]37.5.2) Ordenação Reversa
Use o parâmetro reverse=True para ordenar em ordem decrescente:
# Ordem decrescente para números
scores = [85, 92, 78, 95, 88]
highest_first = sorted(scores, reverse=True)
print(highest_first) # Output: [95, 92, 88, 85, 78]
# Ordem decrescente para strings (alfabética reversa)
names = ["Charlie", "Alice", "Bob", "Diana"]
reverse_alpha = sorted(names, reverse=True)
print(reverse_alpha) # Output: ['Diana', 'Charlie', 'Bob', 'Alice']37.5.3) Entendendo o Parâmetro key
O parâmetro key é onde sorted() se torna realmente poderoso. Ele transforma como o Python compara elementos durante a ordenação.
O que é o parâmetro key?
O parâmetro key aceita uma função(function). O Python chama essa função em cada elemento para extrair uma "chave de comparação", e então ordena com base nessas chaves em vez dos elementos originais.
Como funciona passo a passo:
- O Python chama a função key em cada elemento
- O Python coleta todas as chaves
- O Python ordena comparando essas chaves
- O Python retorna os elementos originais na nova ordem
# Exemplo: Ordenar pelo comprimento
words = ["python", "is", "awesome"]
# Passo 1: O Python chama len() em cada palavra
# len("python") → 6
# len("is") → 2
# len("awesome") → 7
# Passo 2: O Python tem estas chaves: [6, 2, 7]
# Passo 3: O Python ordena as chaves: [2, 6, 7]
# Passo 4: O Python retorna words nessa ordem: ["is", "python", "awesome"]
result = sorted(words, key=len)
print(result) # Output: ['is', 'python', 'awesome']Visualizando a função key:
O que pode ser uma função key?
A função key precisa:
- Aceitar um argumento (o elemento que está sendo ordenado)
- Retornar um valor que o Python consiga comparar (números, strings, tuplas, etc.)
# Funções embutidas funcionam muito bem
sorted(numbers, key=abs) # Ordenar por valor absoluto
sorted(words, key=len) # Ordenar por comprimento
sorted(names, key=str.lower) # Ordenar sem diferenciar maiúsculas/minúsculas
# Suas próprias funções
def first_letter(word):
return word[0]
sorted(words, key=first_letter) # Ordenar pela primeira letra
# Funções lambda (Capítulo 23)
sorted(words, key=lambda w: w[-1]) # Ordenar pela última letraImportante: A função key é chamada uma vez por elemento
# Demonstrando quando a função key é chamada
def show_key(word):
print(f"Getting key for: {word}")
return len(word)
words = ["cat", "elephant", "dog"]
result = sorted(words, key=show_key)
# Output:
# Getting key for: cat
# Getting key for: elephant
# Getting key for: dog
print(result) # Output: ['cat', 'dog', 'elephant']Importante: A função key é chamada uma vez por elemento
Perceba que show_key é chamada exatamente uma vez para cada palavra, e não repetidamente durante comparações. O Python é eficiente — ele extrai todas as chaves primeiro, armazena em cache, e então ordena usando as chaves em cache.
Pense em key como respondendo: "Qual aspecto eu devo comparar?"
key=len→ "Compare pelo comprimento"key=abs→ "Compare pelo valor absoluto"key=str.lower→ "Compare como se tudo estivesse em minúsculas"key=lambda x: x[1]→ "Compare pelo segundo elemento"
O parâmetro key permite que você ordene por qualquer propriedade dos seus elementos, tornando sorted() incrivelmente versátil.
37.5.4) Ordenando com Funções Embutidas como Chaves
As funções embutidas do Python são excelentes funções key:
# Ordenando por valor absoluto
numbers = [-5, 2, -8, 1, -3, 7]
sorted_by_magnitude = sorted(numbers, key=abs)
print(sorted_by_magnitude) # Output: [1, 2, -3, -5, 7, -8]
# Ordenando strings sem diferenciar maiúsculas/minúsculas
names = ["alice", "Bob", "CHARLIE", "diana"]
sorted_case_insensitive = sorted(names, key=str.lower)
print(sorted_case_insensitive) # Output: ['alice', 'Bob', 'CHARLIE', 'diana']37.5.5) Ordenando Estruturas de Dados Complexas
Ao ordenar listas de tuplas ou listas, você pode usar indexação para especificar por qual elemento ordenar:
# Ordenando tuplas pelo segundo elemento
students = [
("Alice", 92),
("Bob", 85),
("Charlie", 88),
("Diana", 95)
]
# Ordenar pela pontuação (segundo elemento)
by_score = sorted(students, key=lambda student: student[1])
print(by_score)
# Output: [('Bob', 85), ('Charlie', 88), ('Alice', 92), ('Diana', 95)]
# Ordenar pela pontuação em ordem decrescente
by_score_desc = sorted(students, key=lambda student: student[1], reverse=True)
print(by_score_desc)
# Output: [('Diana', 95), ('Alice', 92), ('Charlie', 88), ('Bob', 85)]Observação: Estamos usando lambda aqui (como vimos no Capítulo 23). Uma lambda é uma pequena função anônima. A expressão lambda student: student[1] cria uma função que recebe uma tupla de estudante e retorna seu segundo elemento (a pontuação).
37.5.6) Ordenação em Múltiplos Níveis
Você pode ordenar por múltiplos critérios retornando uma tupla a partir da função key. O Python compara tuplas elemento por elemento, da esquerda para a direita:
Como a comparação de tuplas funciona:
Quando o Python compara duas tuplas, ele segue estas regras:
- Compare os primeiros elementos. Se forem diferentes, a comparação termina.
- Se os primeiros elementos forem iguais, compare os segundos elementos.
- Se os segundos elementos forem iguais, compare os terceiros elementos.
- Continue até encontrar uma diferença ou acabar os elementos.
# Exemplos de comparação de tuplas
print((1, 'a') < (2, 'z')) # Output: True (1 < 2, so True immediately)
print((1, 'z') < (1, 'a')) # Output: False (1 == 1, so compare 'z' < 'a')
print((1, 'a') < (1, 'a')) # Output: False (both tuples are equal)
print((1, 2, 9) < (1, 3, 1)) # Output: True (1 == 1, then 2 < 3)Isso torna tuplas perfeitas para ordenação em múltiplos níveis — o Python lida automaticamente com a lógica de "comparar o primeiro critério, depois o segundo, depois o terceiro" por você:
# Ordenar por múltiplos critérios
students = [
("Alice", "Smith", 92),
("Bob", "Jones", 85),
("Alice", "Brown", 88),
("Charlie", "Smith", 85)
]
# Ordenar pelo primeiro nome, depois pelo sobrenome
by_name = sorted(students, key=lambda s: (s[0], s[1]))
print("By name:")
for student in by_name:
print(f" {student}")
# Output:
# By name:
# ('Alice', 'Brown', 88)
# ('Alice', 'Smith', 92)
# ('Bob', 'Jones', 85)
# ('Charlie', 'Smith', 85)
# Ordenar pela pontuação em ordem decrescente, depois pelo nome em ordem crescente
by_score_then_name = sorted(students, key=lambda s: (-s[2], s[0]))
print("\nBy score (high to low), then name:")
for student in by_score_then_name:
print(f" {student}")
# Output:
# By score (high to low), then name:
# ('Alice', 'Smith', 92)
# ('Alice', 'Brown', 88)
# ('Bob', 'Jones', 85)
# ('Charlie', 'Smith', 85)Observação: Para ordenar um critério em ordem decrescente e outro em ordem crescente, nós negamos o valor numérico (-s[2]). Isso funciona porque negar inverte a ordem de ordenação para números. No exemplo acima, -s[2] ordena as pontuações da maior para a menor, enquanto s[0] ordena os nomes de A a Z.
37.5.7) Usando Funções Auxiliares para Chaves Complexas
Quando a lógica de ordenação fica complexa, definir uma função auxiliar deixa o código mais legível e fácil de manter. Você pode então usar essa função auxiliar dentro da sua função key.
Exemplo: Ordenando arquivos por extensão
Suponha que você queira agrupar arquivos pela extensão (.csv, .jpg, .pdf, etc.) e, dentro de cada grupo, ordenar alfabeticamente pelo nome do arquivo. A função key precisa extrair a extensão do arquivo, o que exige alguma manipulação de string.
# Ordenar arquivos por extensão, depois por nome
files = [
"report.pdf",
"data.csv",
"image.jpg",
"notes.txt",
"backup.csv",
"photo.jpg"
]
# Extrair extensão para ordenação
def get_extension(filename):
"""Extrai a extensão do arquivo a partir de um nome de arquivo."""
return filename.split(".")[-1] # Divide por "." e pega a última parte
# Usar a função auxiliar na key
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))
print("Files sorted by extension, then name:")
for file in sorted_files:
print(f" {file}")
# Output:
# Files sorted by extension, then name:
# backup.csv # csv files first (alphabetically)
# data.csv # csv files first (alphabetically)
# image.jpg # jpg files next
# photo.jpg # jpg files next
# report.pdf # pdf files next
# notes.txt # txt files lastComo funciona:
- A função key
lambda f: (get_extension(f), f)retorna uma tupla para cada nome de arquivo - Para "report.pdf", ela retorna
("pdf", "report.pdf") - Para "data.csv", ela retorna
("csv", "data.csv") - O Python ordena pelo primeiro elemento da tupla (extensão), depois pelo segundo elemento (nome completo do arquivo)
- Isso agrupa arquivos por extensão e ordena alfabeticamente dentro de cada grupo
Por que usar uma função auxiliar?
Compare a legibilidade:
# Sem função auxiliar - mais difícil de entender
sorted_files = sorted(files, key=lambda f: (f.split(".")[-1], f))
# Com função auxiliar - intenção mais clara
sorted_files = sorted(files, key=lambda f: (get_extension(f), f))A função auxiliar torna seu código autoexplicativo. Qualquer pessoa lendo get_extension(f) entende imediatamente o que está acontecendo, enquanto f.split(".")[-1] exige interpretação mental.
37.5.8) sorted() vs .sort(): Quando Usar Cada Um
O Python oferece duas formas de ordenar:
sorted()- Função que retorna uma nova lista ordenada.sort()- Método de lista que ordena no lugar
# sorted() - cria nova lista, original inalterada
numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)
print(f"Original: {numbers}") # Output: Original: [3, 1, 4, 1, 5]
print(f"Sorted: {sorted_numbers}") # Output: Sorted: [1, 1, 3, 4, 5]
# .sort() - modifica a lista no lugar, retorna None
numbers = [3, 1, 4, 1, 5]
result = numbers.sort()
print(f"Modified: {numbers}") # Output: Modified: [1, 1, 3, 4, 5]
print(f"Return value: {result}") # Output: Return value: NoneQuando usar sorted():
- Você precisa manter a ordem original
- Você está ordenando algo que não é uma lista (tupla, string, conjunto, etc.)
- Você quer ordenar e atribuir em uma única expressão
Quando usar .sort():
- Você tem uma lista e não precisa da ordem original
- Você quer economizar memória (nenhuma nova lista é criada)
- Você está ordenando uma lista grande no lugar por eficiência
A função sorted() é uma das ferramentas mais versáteis do Python. Combinada com o parâmetro key, ela pode lidar com praticamente qualquer requisito de ordenação, desde uma simples ordenação numérica até uma ordenação complexa, com múltiplos critérios, de estruturas de dados aninhadas.
Este capítulo equipou você com as funções embutidas e ferramentas essenciais do Python. Você aprendeu como:
- Entender a hierarquia de tipos do Python e prever quais operações funcionam com quais tipos
- Usar funções fundamentais como
len(),sum(),min(),max(),abs()eround()para operações comuns - Iterar com informações de posição usando
enumerate() - Processar sequências paralelas simultaneamente com
zip() - Tomar decisões ao longo de coleções usando
any()eall() - Ordenar dados com flexibilidade usando
sorted()e funções key personalizadas
Essas ferramentas formam a base do código idiomático do Python. Elas são eficientes, legíveis e lidam corretamente com casos extremos. Conforme você continuar programando, vai se pegar recorrendo a essas funções o tempo todo — elas são os blocos de construção que tornam o código Python elegante e expressivo.
No próximo capítulo, vamos explorar decorators, que permitem modificar e aprimorar o comportamento de funções de maneiras poderosas, construindo sobre os conceitos de função de primeira classe que aprendemos no Capítulo 23.