0% acharam este documento útil (0 voto)
63 visualizações618 páginas

Guia Prático de Programação em Python

O livro 'Python Descomplicado' é um guia prático destinado a iniciantes na programação com Python, abordando desde a configuração do ambiente até conceitos fundamentais e estruturas de dados. Com uma abordagem clara e acessível, o autor, Alan Afif Helal, busca desmistificar a programação e empoderar leitores de diversas formações, promovendo um aprendizado interativo e prático. O material é ideal para estudantes, profissionais e entusiastas que desejam desenvolver habilidades em Python para resolver problemas e inovar em suas áreas.

Enviado por

higorrafael563
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
63 visualizações618 páginas

Guia Prático de Programação em Python

O livro 'Python Descomplicado' é um guia prático destinado a iniciantes na programação com Python, abordando desde a configuração do ambiente até conceitos fundamentais e estruturas de dados. Com uma abordagem clara e acessível, o autor, Alan Afif Helal, busca desmistificar a programação e empoderar leitores de diversas formações, promovendo um aprendizado interativo e prático. O material é ideal para estudantes, profissionais e entusiastas que desejam desenvolver habilidades em Python para resolver problemas e inovar em suas áreas.

Enviado por

higorrafael563
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Python Descomplicado: Seu Guia

Prático para Começar a Programar


Alan Afif Helal
Prefácio

Seja muito bem-vindo(a) ao universo da programação com Python! Ter este livro,
Python Descomplicado: Seu Guia Prático para Começar a Programar, em suas mãos é o
primeiro passo em uma jornada que, espero, será transformadora e repleta de descobertas.
Se você busca uma forma clara, objetiva e prática para desvendar os mistérios da progra-
mação, ou se já teve algum contato, mas deseja solidificar seus alicerces, saiba que este
material foi cuidadosamente elaborado pensando em você.
Minha trajetória como professor no Instituto Federal do Espírito Santo (IFES) tem
sido uma fonte constante de aprendizado e inspiração. Ao longo dos anos, lecionando
disciplinas cruciais como Algoritmos, Estruturas de Dados e Linguagens de Programação
para vibrantes turmas de futuros engenheiros (nas áreas de Computação, Elétrica e
Mecânica), pude observar de perto os desafios e as necessidades dos estudantes que
mergulham no mundo do desenvolvimento de software. Muitos, embora tecnicamente
brilhantes em suas especialidades, encontravam na programação uma barreira inicial,
por vezes causada por abordagens excessivamente teóricas ou distantes de sua realidade
prática. Foi essa percepção, aliada à experiência gratificante de publicar meu livro anterior,
"Programação em C Essencial", que me motivou a criar este novo guia, desta vez focado
na linguagem Python.
Python, com sua sintaxe intuitiva que se assemelha à linguagem humana e seu ecos-
sistema robusto de bibliotecas, emergiu não apenas como uma linguagem poderosa para
desenvolvedores experientes, mas como uma porta de entrada incrivelmente acessível para
a programação. É uma ferramenta que transcende as fronteiras da ciência da computação,
tornando-se vital para a análise de dados em laboratórios, a automação de processos na
indústria, a simulação de sistemas complexos na engenharia e uma infinidade de outras
aplicações inovadoras. A filosofia “descomplicada” deste livro reside exatamente em traduzir
o potencial do Python em um aprendizado leve, lógico e progressivo. Aqui, cada novo
conceito é introduzido com explicações diretas, analogias do cotidiano e, fundamentalmente,
com o código “vivo”, mostrando como a teoria se materializa em resultados práticos.
Este guia foi carinhosamente preparado para um público diversificado. Se você é
um estudante universitário, especialmente nas áreas de engenharias, ciências exatas
ou qualquer campo que se beneficie da análise de dados e automação, encontrará aqui
uma base sólida para aplicar Python em seus projetos acadêmicos, otimizar seus estudos
e se preparar para um mercado de trabalho cada vez mais tecnológico. Se você é um
profissional já estabelecido em sua carreira, mas que busca novas competências para
inovar, automatizar tarefas ou extrair insights valiosos de dados, este livro oferecerá o
conhecimento prático para integrar o Python ao seu arsenal de ferramentas. E se você
é um entusiasta da tecnologia ou um autodidata curioso, descobrirá um caminho
amigável para aprender a ‘pensar como um programador” e, quem sabe, dar vida às suas
próprias ideias e projetos. O ponto de partida é o desejo de aprender; nenhuma experiência
prévia em programação é estritamente necessária, pois começaremos dos fundamentos.
Ao longo destas páginas, nossa jornada será eminentemente prática. Iniciaremos
com a configuração do ambiente de desenvolvimento e os blocos de construção essenciais
da lógica de programação e da sintaxe do Python. Avançaremos gradualmente para o
entendimento e a manipulação de diferentes estruturas de dados – como listas, dicionários
e tuplas – que são o alicerce para organizar informações de forma eficiente. Exploraremos
a criação de funções para modularizar e reutilizar código, um pilar das boas práticas de
programação. E, para conectar o aprendizado com o vasto potencial do Python, faremos
uma introdução a bibliotecas fundamentais para a computação científica e análise de
dados, como NumPy e Pandas, que são verdadeiros canivetes suíços para quem trabalha
com números e informações. Cada etapa será permeada por uma quantidade generosa de
exemplos, muitos deles contextualizados em cenários que, embora possam ter um sabor da
engenharia (minha área de origem e atuação), ilustram princípios aplicáveis a uma vasta
gama de problemas.
Como Engenheiro de Computação e educador, minha filosofia de ensino se baseia
na crença de que a tecnologia, incluindo a programação, deve ser uma ferramenta de
empoderamento, e não uma fonte de intimidação. Meu objetivo é desmistificar, mostrar
que a lógica por trás do código é acessível e que, com a orientação correta e prática
diligente, qualquer pessoa pode aprender a programar. Este livro é um reflexo dessa crença:
um convite para você experimentar, errar sem medo, aprender com os erros e, acima de
tudo, construir.
Aprender a programar é, em muitos aspectos, como aprender uma nova língua ou
um instrumento musical. Requer paciência, persistência e, o mais importante, prática
constante. Não encare este livro apenas como um material de leitura, mas como um roteiro
interativo. Execute os exemplos, modifique-os, crie suas próprias variações e desafios. A
verdadeira aprendizagem acontece quando você coloca a mão na massa.
Espero, do fundo do meu coração, que Python Descomplicado seja um farol em sua
jornada de aprendizado, tornando-a não apenas instrutiva, mas também prazerosa e
inspiradora. Que ao final desta leitura, você se sinta confiante para explorar novas
fronteiras com Python e aplicar suas recém-adquiridas habilidades para criar, inovar e
resolver problemas de formas que talvez nem imagine agora.
Uma excelente e produtiva jornada no mundo da programação para você!

Alan Afif Helal


Sumário

I Fundamentos Essenciais do Python 1


1 Introdução ao Python e à Programação 1
1.1 O que é Python? Uma Visão Geral . . . . . . . . . . . . . . . . . . . . . . 1
História, Filosofia e o Zen de Python . . . . . . . . . . . . . . . . . . . . . 1
Como o Python “Pensa”? (Exemplo Conceitual) . . . . . . . . . . . . . . . 3
1.2 Por que Python para Resolver Problemas? . . . . . . . . . . . . . . . . . . 3
Simplicidade e Legibilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
O Poder das Bibliotecas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Comunidade e Suporte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Multiplataforma e Integração . . . . . . . . . . . . . . . . . . . . . . . . . 5
Produtividade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Relevância para Estudantes e Profissionais . . . . . . . . . . . . . . . . . . 5
1.3 Instalação do Python e Ambientes de Desenvolvimento (IDEs) . . . . . . . 6
O Interpretador Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Instalando o Python no seu Computador . . . . . . . . . . . . . . . . . . . 6
Verificando a Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Ferramentas para Escrever Código: IDEs e Editores . . . . . . . . . . . . . 8
Qual Escolher? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.4 Seu primeiro programa em Python: Olá, Mundo! . . . . . . . . . . . . . . 11
Prepare seu Ambiente para Programar . . . . . . . . . . . . . . . . . . . . 11
O Tradicional Olá, Mundo! . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Mais sobre print() e Comentários . . . . . . . . . . . . . . . . . . . . . . . 13
Exemplo Prático: Calculo da Área . . . . . . . . . . . . . . . . . . . . . . 14
Experimente! A Chave para Aprender . . . . . . . . . . . . . . . . . . . . 15
1.5 Conceitos Básicos de Algoritmos e Lógica de Programação . . . . . . . . . 15
O que e um Algoritmo? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Lógica de Programação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Representando Algoritmos Antes de Codificar . . . . . . . . . . . . . . . . 16
Traduzindo um Algoritmo Simples para Python . . . . . . . . . . . . . . . 18
Concluindo o Início da Jornada . . . . . . . . . . . . . . . . . . . . . . . . 19

2 Sintaxe Básica e Tipos de Dados 20


2.1 Variáveis, Nomes e Atribuição . . . . . . . . . . . . . . . . . . . . . . . . . 20
O que e uma Variável? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Nomeando Variáveis em Python . . . . . . . . . . . . . . . . . . . . . . . . 20
Atribuindo Valores: O Operador = . . . . . . . . . . . . . . . . . . . . . . 21
Atribuição Múltipla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Tipagem Dinâmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Sumário

Exemplos Práticos com Variáveis . . . . . . . . . . . . . . . . . . . . . . . 22


2.2 Palavras Reservadas (Keywords) . . . . . . . . . . . . . . . . . . . . . . . . 23
O que são Palavras Reservadas? . . . . . . . . . . . . . . . . . . . . . . . . 23
A Lista de Palavras Reservadas do Python . . . . . . . . . . . . . . . . . . 23
Consultando Palavras Reservadas em Python . . . . . . . . . . . . . . . . 24
Exemplos do que NÃO fazer com Keywords . . . . . . . . . . . . . . . . . 24
Keywords vs. Funções/Tipos Embutidos . . . . . . . . . . . . . . . . . . . 24
Concluindo sobre Palavras Reservadas . . . . . . . . . . . . . . . . . . . . 25
2.3 Tipos de Dados Primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.3.1 Tipos Numéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.3.2 Tipo Booleano (bool) . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3.3 Strings (str) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3.4 Verificando o Tipo de um Dado com type() . . . . . . . . . . . . . 29
2.4 Saída de Dados Formatada: Mais sobre print() e F-strings . . . . . . . . 30
Revisando o Básico da Função print() . . . . . . . . . . . . . . . . . . . . . 30
Controlando Separador (sep) e Final (end) no print() . . . . . . . . . . . . 30
F-strings (Strings Formatadas Literais) . . . . . . . . . . . . . . . . . . . . 31
Outras Formas de Formatação . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.5 Entrada de Dados pelo Usuário: A Função input() . . . . . . . . . . . . . 33
Usando a Função input() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
input() Sempre Retorna String . . . . . . . . . . . . . . . . . . . . . . . . . 34
Convertendo Tipos de Dados (Type Casting) . . . . . . . . . . . . . . . . . 34
Exemplo Prático: Calculadora de Soma . . . . . . . . . . . . . . . . . . . . 35
2.6 Comentários em Python: Deixando Anotações no Seu Código . . . . . . . 35
Comentários de Linha Única . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Comentários de Múltiplas Linhas . . . . . . . . . . . . . . . . . . . . . . . 36
Docstrings: Uma Breve Introdução . . . . . . . . . . . . . . . . . . . . . . 38
Boas Práticas para Comentar . . . . . . . . . . . . . . . . . . . . . . . . . 38
Concluindo sobre Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.7 Manipulando Textos: Métodos Comuns de Strings . . . . . . . . . . . . . . 39
2.7.1 Métodos para Alterar Maiúsculas e Minúsculas . . . . . . . . . . . . 39
Strings: Métodos para Maiúsculas/Minúsculas . . . . . . . . . . . . . . . . 39
2.7.2 Métodos para Remover Espaços em Branco . . . . . . . . . . . . . . 40
Strings: Métodos para Remover Espaços . . . . . . . . . . . . . . . . . . . 40
2.7.3 Métodos para Busca e Substituição . . . . . . . . . . . . . . . . . . 41
Strings: Métodos de Busca e Substituição . . . . . . . . . . . . . . . . . . 41
2.7.4 Métodos para Dividir e Juntar Strings . . . . . . . . . . . . . . . . 42
Strings: Métodos para Dividir e Juntar . . . . . . . . . . . . . . . . . . . . 42
2.7.5 Métodos de Verificação (Retornam True ou False) . . . . . . . . . 43
Strings: Métodos de Verificação . . . . . . . . . . . . . . . . . . . . . . . . 43
2.7.6 Encadeando Chamadas de Métodos . . . . . . . . . . . . . . . . . . 44
Strings: Encadeando Chamadas de Métodos . . . . . . . . . . . . . . . . . 44
Conclusão sobre Métodos de Strings . . . . . . . . . . . . . . . . . . . . . . 44

3 Operadores e Expressões 45
3.1 Introdução a Operadores e Expressões . . . . . . . . . . . . . . . . . . . . 45
A Importância de Entender Operadores . . . . . . . . . . . . . . . . . . . . 46
3.2 Operadores Aritméticos: Fazendo Contas com Python . . . . . . . . . . . . 46
Sumário

Exemplo: Conversão de Temperatura . . . . . . . . . . . . . . . . . . . . . 49


Tipos em Operações Aritméticas . . . . . . . . . . . . . . . . . . . . . . . 49
3.3 Operadores de Atribuição: Guardando e Atualizando Valores . . . . . . . . 50
O Operador de Atribuição Simples (=) . . . . . . . . . . . . . . . . . . . . 50
Operadores de Atribuição Compostos . . . . . . . . . . . . . . . . . . . . . 50
Benefícios dos Operadores de Atribuição Compostos . . . . . . . . . . . . . 53
Exemplo Pratico: Estatísticas de Jogo . . . . . . . . . . . . . . . . . . . . 53
3.4 Operadores de Comparação (Relacionais): Verificando Relações entre Valores 54
Comparando Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.5 Operadores Lógicos (Booleanos): Combinando Condições . . . . . . . . . . 57
O Operador and (E Logico) . . . . . . . . . . . . . . . . . . . . . . . . . . 57
O Operador or (OU Logico) . . . . . . . . . . . . . . . . . . . . . . . . . . 58
O Operador not (NÃO Logico) . . . . . . . . . . . . . . . . . . . . . . . . 58
Combinando Operadores Lógicos . . . . . . . . . . . . . . . . . . . . . . . 59
Avaliação de Curto-Circuito . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.6 Operadores de Identidade (is, is not) . . . . . . . . . . . . . . . . . . . . 60
Identidade de Objeto e a Função id() . . . . . . . . . . . . . . . . . . . . . 60
O Operador is . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
O Operador is not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Diferença Crucial: is versus == . . . . . . . . . . . . . . . . . . . . . . . . 61
Quando Usar is e is not? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Resumo sobre is vs == . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.7 Operadores de Associação (in, not in): Verificando Pertinência . . . . . . 63
O Operador in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
O Operador not in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Usando in e not in com Strings . . . . . . . . . . . . . . . . . . . . . . . . 64
Usando com Outras Sequências (Listas) . . . . . . . . . . . . . . . . . . . . 65
Importância dos Operadores de Associação . . . . . . . . . . . . . . . . . . 65
3.8 Precedência de Operadores: Definindo a Ordem das Coisas . . . . . . . . . 66
O que e Precedência de Operadores? . . . . . . . . . . . . . . . . . . . . . 66
Visão Geral da Precedência em Python . . . . . . . . . . . . . . . . . . . . 66
A Regra de Ouro: Use Parenteses . . . . . . . . . . . . . . . . . . . . . . . 67
Exemplos Práticos de Precedência . . . . . . . . . . . . . . . . . . . . . . . 67
3.9 Expressões: Combinando Tudo para Obter Resultados . . . . . . . . . . . 68
A Anatomia de uma Expressão . . . . . . . . . . . . . . . . . . . . . . . . 69
Como o Python Avalia Expressões . . . . . . . . . . . . . . . . . . . . . . . 69
Onde Usamos Expressões? . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Dicas para Escrever Boas Expressões . . . . . . . . . . . . . . . . . . . . . 70
Concluindo o Capítulo sobre Operadores e Expressões . . . . . . . . . . . . 71

4 Estruturas de Controle Condicional 72


4.1 Introdução às Estruturas de Controle: Dando Inteligência aos Programas . 72
4.2 A Estrutura if (Se): Tomando Decisões Simples . . . . . . . . . . . . . . . 73
Sintaxe da Estrutura if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Como Funciona o if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Exemplos Práticos com if . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
O que acontece se a condição for False? . . . . . . . . . . . . . . . . . . . . 75
4.3 A Estrutura if-else (Se-Senão): Escolhendo entre Duas Alternativas . . . 76
Sumário

Sintaxe da Estrutura if-else . . . . . . . . . . . . . . . . . . . . . . . . . . 76


Como Funciona o if-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Exemplos Práticos com if-else . . . . . . . . . . . . . . . . . . . . . . . . . 77
Fluxograma de um if-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.4 A Estrutura if-elif-else (Se-Senão Se-Senão): Múltiplas Condições . . . 80
Sintaxe da Estrutura if-elif-else . . . . . . . . . . . . . . . . . . . . . . . . 80
Como Funciona o if-elif-else . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Exemplos Práticos com if-elif-else . . . . . . . . . . . . . . . . . . . . . . . 81
A Opcionalidade do else no if-elif-else . . . . . . . . . . . . . . . . . . . . . 83
4.5 Estruturas Condicionais Aninhadas: Decisões Dentro de Decisões . . . . . 84
O que são Estruturas Condicionais Aninhadas? . . . . . . . . . . . . . . . 84
Quando Usar o Aninhamento? . . . . . . . . . . . . . . . . . . . . . . . . . 85
Exemplos Práticos de Aninhamento . . . . . . . . . . . . . . . . . . . . . . 85
Cuidados com o Aninhamento Excessivo . . . . . . . . . . . . . . . . . . . 88
4.6 Múltiplos ifs Independentes vs. if-elif-else: Qual Usar? . . . . . . . . 88
Múltiplos ifs Independentes . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Estrutura if-elif-else (Contraste) . . . . . . . . . . . . . . . . . . . . . . . . 90
Resumo da Diferença: Múltiplos ifs vs if-elif-else . . . . . . . . . . . . . . . 91
Dica para Lembrar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.7 O Operador Condicional Ternário (Expressão Condicional) . . . . . . . . . 92
Sintaxe da Expressão Condicional (Ternária) . . . . . . . . . . . . . . . . . 93
Comparando com if-else Tradicional . . . . . . . . . . . . . . . . . . . . . . 93
Exemplos Práticos do Operador Ternário . . . . . . . . . . . . . . . . . . . 93
Vantagens do Operador Ternário . . . . . . . . . . . . . . . . . . . . . . . 94
Desvantagens e Quando Evitar o Ternário . . . . . . . . . . . . . . . . . . 94
Recomendação: Use com Moderação . . . . . . . . . . . . . . . . . . . . . 94
4.8 Valores “Truthy” e “Falsy”: O que Python Considera Verdadeiro ou Falso . 95
Valores Falsy em Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Valores Truthy em Python . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Como Truthiness e Falsiness Simplificam Condições . . . . . . . . . . . . . 96
Exemplos Práticos de Truthiness e Falsiness . . . . . . . . . . . . . . . . . 96
A Função bool() para Conversão Explícita . . . . . . . . . . . . . . . . . . 97
Clareza vs. Concisão Pythônica . . . . . . . . . . . . . . . . . . . . . . . . 97
Concluindo o Capitulo sobre Estruturas de Controle Condicional . . . . . . 98

5 Estruturas de Repetição (Laços) 99


5.1 Introdução aos Laços de Repetição: Automatizando Tarefas Repetitivas . . 99
Por que Precisamos de Laços? . . . . . . . . . . . . . . . . . . . . . . . . . 99
Tipos Principais de Laços em Python . . . . . . . . . . . . . . . . . . . . . 100
5.2 O Laço while (Enquanto): Repetindo com Base em uma Condição . . . . 100
Sintaxe do Laco while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Como Funciona o Laço while . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Importância de Alterar a Condição (Evitando Laços Infinitos) . . . . . . . 101
Exemplos Práticos com while . . . . . . . . . . . . . . . . . . . . . . . . . 102
Laços Infinitos Controlados com while True e break . . . . . . . . . . . . . 103
5.3 O Laço for (Para): Iterando sobre Sequências . . . . . . . . . . . . . . . . 104
Sintaxe do Laço for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Como Funciona o Laço for . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Sumário

Iterando sobre Strings com for . . . . . . . . . . . . . . . . . . . . . . . . . 105


A Função range() com for . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Exemplos Práticos com for e range() . . . . . . . . . . . . . . . . . . . . . 106
Iterando sobre Listas com for (Prévia) . . . . . . . . . . . . . . . . . . . . 107
Quando Usar for vs. while? . . . . . . . . . . . . . . . . . . . . . . . . . . 107
5.4 Comandos de Controle de Laço: break e continue . . . . . . . . . . . . . 108
A Instrução break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
A Instrução continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Onde break e continue Podem Ser Usados . . . . . . . . . . . . . . . . . . 111
5.5 A Cláusula else em Laços (Opcional e Menos Comum) . . . . . . . . . . . 111
Sintaxe do else em Laços . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Quando o Bloco else de um Laço e Executado . . . . . . . . . . . . . . . . 112
Casos de Uso e Exemplos com else em Laços . . . . . . . . . . . . . . . . . 112
Clareza vs. Surpresa com else em Laços . . . . . . . . . . . . . . . . . . . . 113
5.6 Laços Aninhados: Repetições Dentro de Repetições . . . . . . . . . . . . . 114
O que são Laços Aninhados? . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Como Funcionam os Laços Aninhados . . . . . . . . . . . . . . . . . . . . . 115
Exemplos Práticos de Laços Aninhados . . . . . . . . . . . . . . . . . . . . 115
Cuidados com Laços Aninhados . . . . . . . . . . . . . . . . . . . . . . . . 116
Concluindo o Capítulo 5 sobre Estruturas de Repetição . . . . . . . . . . . 117

II Estruturas de Dados Fundamentais 118


6 Listas (Lists) 119
6.1 Introdução às Listas: Coleções Ordenadas e Mutáveis . . . . . . . . . . . . 119
Por que as Listas são Tão Úteis? . . . . . . . . . . . . . . . . . . . . . . . 120
6.2 Criando Listas: Dando Vida às Nossas Coleções . . . . . . . . . . . . . . . 120
Sintaxe Principal para Criar Listas: Colchetes [] . . . . . . . . . . . . . . . 120
Usando a Funcao Construtora list() . . . . . . . . . . . . . . . . . . . . . . 121
Listas e Quebras de Linha no Codigo Fonte . . . . . . . . . . . . . . . . . . 122
6.3 Acessando Elementos da Lista: Índices . . . . . . . . . . . . . . . . . . . . 123
6.3.1 Indexação Baseada em Zero: O Ponto de Partida . . . . . . . . . . 123
Indexação Baseada em Zero . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.3.2 Acessando Elementos com Índices Positivos . . . . . . . . . . . . . . 124
Acessando Elementos com Índices Positivos . . . . . . . . . . . . . . . . . . 124
6.3.3 Acessando Elementos com Índices Negativos . . . . . . . . . . . . . 124
Acessando Elementos com Índices Negativos (Detalhado) . . . . . . . . . . 124
6.3.4 Cuidado: Erro de Índice Fora do Intervalo (IndexError) . . . . . . 125
Erro de Indice Fora do Intervalo (IndexError) . . . . . . . . . . . . . . . . 125
6.3.5 Usando Elementos Acessados da Lista . . . . . . . . . . . . . . . . 126
Usando Elementos Acessados da Lista . . . . . . . . . . . . . . . . . . . . . 126
6.4 Fatiamento de Listas (Slicing): Obtendo Sublistas . . . . . . . . . . . . . . 127
Sintaxe Basica do Fatiamento: [inicio:fim] . . . . . . . . . . . . . . . . . . 127
Fatiamento com Passo (Stride) . . . . . . . . . . . . . . . . . . . . . . . . . 128
Fatiamento com Índices Negativos . . . . . . . . . . . . . . . . . . . . . . . 129
Fatias Criam Novas Listas (Copias Rasas) . . . . . . . . . . . . . . . . . . 129
Atribuicao a Fatias: Modificando a Lista Original . . . . . . . . . . . . . . 129
6.5 Modificando Listas (Listas são Mutáveis!) . . . . . . . . . . . . . . . . . . 130
Sumário

6.5.1 Alterando um Elemento Existente na Lista . . . . . . . . . . . . . . 131


Alterando um Elemento Existente na Lista . . . . . . . . . . . . . . . . . . 131
6.5.2 Adicionando Elementos à Lista . . . . . . . . . . . . . . . . . . . . 131
Adicionando Elementos à Lista . . . . . . . . . . . . . . . . . . . . . . . . 131
6.5.3 Removendo Elementos da Lista . . . . . . . . . . . . . . . . . . . . 133
Removendo Elementos da Lista . . . . . . . . . . . . . . . . . . . . . . . . 133
6.6 Métodos Comuns de Listas: Ferramentas Poderosas para Manipulação . . . 134
6.6.1 Revisitando len(lista): Obtendo o Tamanho da Lista . . . . . . . 134
Revisitando len(lista): Obtendo o Tamanho . . . . . . . . . . . . . . . . . 134
6.6.2 Ordenando Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Ordenando Listas: sort() e sorted() . . . . . . . . . . . . . . . . . . . . . . 135
6.6.3 Invertendo a Ordem dos Elementos . . . . . . . . . . . . . . . . . . 136
Invertendo a Ordem: reverse() . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.6.4 Contando Ocorrências de um Item . . . . . . . . . . . . . . . . . . 136
Contando Ocorrências: count() . . . . . . . . . . . . . . . . . . . . . . . . 136
6.6.5 Encontrando o Índice de um Elemento . . . . . . . . . . . . . . . . 136
Encontrando o Índice: index() . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.6.6 Copiando Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Copiando Listas: copy() e Fatiamento [:] . . . . . . . . . . . . . . . . . . . 137
6.7 Compreensão de Listas (List Comprehensions) . . . . . . . . . . . . . . . . 138
A Sintaxe da Compreensão de Listas . . . . . . . . . . . . . . . . . . . . . 138
Comparando List Comprehensions com Lacos for . . . . . . . . . . . . . . 139
Vantagens das List Comprehensions . . . . . . . . . . . . . . . . . . . . . . 139
Mais Exemplos Praticos de List Comprehensions . . . . . . . . . . . . . . . 139
Quando Usar (e Quando Evitar) List Comprehensions . . . . . . . . . . . . 140
6.8 Listas Aninhadas (Listas dentro de Listas) . . . . . . . . . . . . . . . . . . 140
Criando Listas Aninhadas . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Acessando Elementos em Listas Aninhadas . . . . . . . . . . . . . . . . . . 141
Iterando sobre Listas Aninhadas . . . . . . . . . . . . . . . . . . . . . . . . 142
Modificando Listas Aninhadas . . . . . . . . . . . . . . . . . . . . . . . . . 143
Consideracoes sobre Listas Aninhadas . . . . . . . . . . . . . . . . . . . . . 144
6.9 Quando Usar Listas (e Algumas Considerações) . . . . . . . . . . . . . . . 144
Caracteristicas Chave das Listas (Resumo) . . . . . . . . . . . . . . . . . . 144
Quando as Listas Brilham (Casos de Uso Ideais) . . . . . . . . . . . . . . . 144
Consideracoes Importantes sobre Listas . . . . . . . . . . . . . . . . . . . . 145
Concluindo o Capitulo sobre Listas . . . . . . . . . . . . . . . . . . . . . . 145

7 Tuplas (Tuples) 147


7.1 Introdução às Tuplas: Sequências Ordenadas e Imutáveis . . . . . . . . . . 147
Por que usar Tuplas se já temos Listas? . . . . . . . . . . . . . . . . . . . . 147
Comparacao Inicial com Listas . . . . . . . . . . . . . . . . . . . . . . . . . 148
7.2 Criando Tuplas: Definindo Nossas Coleções Imutáveis . . . . . . . . . . . . 148
Sintaxe Principal para Criar Tuplas . . . . . . . . . . . . . . . . . . . . . . 148
Usando a Funcao Construtora tuple() . . . . . . . . . . . . . . . . . . . . . 150
Tuplas e Quebras de Linha no Codigo Fonte . . . . . . . . . . . . . . . . . 150
7.3 Acessando Elementos da Tupla: Índices e Fatiamento . . . . . . . . . . . . 151
7.3.1 Acessando Elementos Individuais com Índices . . . . . . . . . . . . 151
Acessando Elementos da Tupla com Índices . . . . . . . . . . . . . . . . . . 151
Sumário

7.3.2 Obtendo Sub-Tuplas com Fatiamento (Slicing) . . . . . . . . . . . . 152


Obtendo Sub-Tuplas com Fatiamento (Slicing) . . . . . . . . . . . . . . . . 152
Fatias de Tuplas sao Novas Tuplas . . . . . . . . . . . . . . . . . . . . . . . 153
7.4 Imutabilidade em Detalhe: O que Significa Não Poder Mudar? . . . . . . . 153
Tentando Modificar uma Tupla: O TypeError . . . . . . . . . . . . . . . . 154
A Nuance da Imutabilidade: Objetos Mutaveis Dentro de Tuplas . . . . . . 154
7.5 Operações e Métodos Comuns com Tuplas (Menos que Listas) . . . . . . . 156
Operacoes Comuns com Tuplas . . . . . . . . . . . . . . . . . . . . . . . . 156
Metodos Especificos de Tuplas . . . . . . . . . . . . . . . . . . . . . . . . . 157
Por que Tao Poucos Metodos? A Imutabilidade . . . . . . . . . . . . . . . 158
7.6 Desempacotamento de Tuplas (Tuple Unpacking) . . . . . . . . . . . . . . 159
Como Funciona o Desempacotamento Basico . . . . . . . . . . . . . . . . . 159
Desempacotamento em Lacos for . . . . . . . . . . . . . . . . . . . . . . . 159
Desempacotamento Estendido com * . . . . . . . . . . . . . . . . . . . . . 160
Utilidade: Retorno de Multiplos Valores de Funcoes . . . . . . . . . . . . . 161
Trocando Valores de Variaveis (Swapping) . . . . . . . . . . . . . . . . . . 161
7.7 Quando Usar Tuplas em vez de Listas? . . . . . . . . . . . . . . . . . . . . 161
Situacoes Onde Tuplas Sao Preferiveis . . . . . . . . . . . . . . . . . . . . 162
Quando Listas Ainda Sao a Melhor Escolha . . . . . . . . . . . . . . . . . 163
Concluindo o Capitulo sobre Tuplas . . . . . . . . . . . . . . . . . . . . . . 163

8 Dicionários (Dictionaries) 164


8.1 Introdução aos Dicionários: Coleções de Pares Chave-Valor . . . . . . . . . 164
Caracteristicas Principais dos Dicionarios . . . . . . . . . . . . . . . . . . . 165
8.2 Criando Dicionários: Formando Nossos Mapeamentos Chave-Valor . . . . . 165
Sintaxe Principal para Criar Dicionários: {} e chave:valor . . . . . . . . . . 165
Usando a Funcao Construtora dict() . . . . . . . . . . . . . . . . . . . . . 167
Dicionarios e Quebras de Linha no Codigo Fonte . . . . . . . . . . . . . . . 168
8.3 Acessando Valores no Dicionário: A Chave para a Informação . . . . . . . 168
Acesso Direto Usando a Chave ([]) . . . . . . . . . . . . . . . . . . . . . . . 168
Acesso Seguro com o Metodo get() . . . . . . . . . . . . . . . . . . . . . . 169
Chaves sao Case-Sensitive . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
8.4 Adicionando e Modificando Pares Chave-Valor . . . . . . . . . . . . . . . . 171
Adicionando Novos Pares Chave-Valor . . . . . . . . . . . . . . . . . . . . 171
Modificando o Valor de uma Chave Existente . . . . . . . . . . . . . . . . 171
O Metodo update() para Multiplos Pares . . . . . . . . . . . . . . . . . . . 172
8.5 Removendo Pares Chave-Valor de um Dicionário . . . . . . . . . . . . . . . 173
Removendo com a Instrucao del . . . . . . . . . . . . . . . . . . . . . . . . 173
Removendo com o Metodo pop() . . . . . . . . . . . . . . . . . . . . . . . 173
Removendo com o Metodo popitem() . . . . . . . . . . . . . . . . . . . . . 174
Limpando Todos os Itens com clear() . . . . . . . . . . . . . . . . . . . . . 175
8.6 Iterando sobre Dicionários: Percorrendo os Pares Chave-Valor . . . . . . . 175
Iterando Diretamente sobre um Dicionario (Chaves) . . . . . . . . . . . . . 175
Iterando sobre os Valores com values() . . . . . . . . . . . . . . . . . . . . 176
Iterando sobre Pares Chave-Valor com items() . . . . . . . . . . . . . . . . 176
Cuidado ao Modificar Dicionario Durante Iteracao . . . . . . . . . . . . . . 177
8.7 Métodos Úteis de Dicionários (e Funções Relacionadas) . . . . . . . . . . . 178
Revisitando len(dicionario) . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Sumário

Obtendo Visoes (Views) de Dicionarios . . . . . . . . . . . . . . . . . . . . 178


Verificando Existencia de Chave com in . . . . . . . . . . . . . . . . . . . . 179
Copiando Dicionarios com copy() . . . . . . . . . . . . . . . . . . . . . . . 180
Criando Dicionarios com [Link]() . . . . . . . . . . . . . . . . . . . 180
8.8 Compreensão de Dicionários (Dictionary Comprehensions) . . . . . . . . . 181
A Sintaxe da Compreensão de Dicionários . . . . . . . . . . . . . . . . . . 181
Comparando com Lacos for Tradicionais . . . . . . . . . . . . . . . . . . . 182
Vantagens das Dicionario Comprehensions . . . . . . . . . . . . . . . . . . 182
Mais Exemplos Praticos de Dicionario Comprehensions . . . . . . . . . . . 183
Quando Usar (e Quando Evitar) Dicionario Comprehensions . . . . . . . . 183
8.9 Dicionários Aninhados (Dicionários dentro de Dicionários) . . . . . . . . . 184
Criando Dicionarios Aninhados . . . . . . . . . . . . . . . . . . . . . . . . 184
Acessando Elementos em Dicionarios Aninhados . . . . . . . . . . . . . . . 185
Modificando Dicionarios Aninhados . . . . . . . . . . . . . . . . . . . . . . 186
Iterando sobre Dicionarios Aninhados . . . . . . . . . . . . . . . . . . . . . 186
Consideracoes sobre Dicionarios Aninhados . . . . . . . . . . . . . . . . . . 187
8.10 Quando Usar Dicionários? A Escolha Certa para Mapeamentos . . . . . . 187
Caracteristicas Chave dos Dicionarios (Resumo) . . . . . . . . . . . . . . . 187
Quando os Dicionarios Sao a Melhor Escolha . . . . . . . . . . . . . . . . . 188
Quando Outras Estruturas Podem Ser Mais Adequadas . . . . . . . . . . . 188
Concluindo o Capitulo sobre Dicionarios . . . . . . . . . . . . . . . . . . . 189

9 Conjuntos (Sets) 190


9.1 Introdução aos Conjuntos: Coleções Não Ordenadas de Itens Únicos . . . . 190
Analogias para Conjuntos . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Outras Caracteristicas Importantes dos Conjuntos . . . . . . . . . . . . . . 191
Quando os Conjuntos Sao Uteis? . . . . . . . . . . . . . . . . . . . . . . . 191
9.2 Criando Conjuntos: Definindo Coleções de Itens Únicos . . . . . . . . . . . 191
Sintaxe Literal com Chaves {} (para Conjuntos Nao Vazios) . . . . . . . . 191
Criando um Conjunto Vazio: Use set() Obrigatoriamente! . . . . . . . . . . 192
Usando a Funcao Construtora set(iteravel) . . . . . . . . . . . . . . . . . . 192
Conjuntos e Quebras de Linha no Codigo Fonte . . . . . . . . . . . . . . . 193
9.3 Operações Básicas com Conjuntos . . . . . . . . . . . . . . . . . . . . . . . 194
Obtendo o Tamanho de um Conjunto com len() . . . . . . . . . . . . . . . 194
Verificando Pertinencia com in e not in . . . . . . . . . . . . . . . . . . . . 194
Iterando sobre um Conjunto com Laco for . . . . . . . . . . . . . . . . . . 195
O que NAO se pode fazer com Conjuntos (sem ordem) . . . . . . . . . . . 195
9.4 Adicionando e Removendo Elementos de Conjuntos . . . . . . . . . . . . . 195
9.4.1 Adicionando Elementos a um Conjunto . . . . . . . . . . . . . . . . 196
Adicionando Elementos a um Conjunto . . . . . . . . . . . . . . . . . . . . 196
9.4.2 Removendo Elementos de um Conjunto . . . . . . . . . . . . . . . . 196
Removendo Elementos de um Conjunto . . . . . . . . . . . . . . . . . . . . 196
9.5 Operações de Teoria dos Conjuntos . . . . . . . . . . . . . . . . . . . . . . 198
9.5.1 União de Conjuntos: Juntando Tudo . . . . . . . . . . . . . . . . . 198
União de Conjuntos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
9.5.2 Interseção de Conjuntos: Encontrando os Elementos Comuns . . . . 199
Interseção de Conjuntos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
9.5.3 Diferença de Conjuntos: O que Há em Um, mas Não no Outro . . . 199
Sumário

Diferença de Conjuntos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199


9.5.4 Diferença Simétrica de Conjuntos: Elementos Não Comuns a Ambos 200
Diferença Simétrica de Conjuntos . . . . . . . . . . . . . . . . . . . . . . . 200
9.5.5 Métodos de Atualização “In-Place” . . . . . . . . . . . . . . . . . . 200
Métodos de Atualização In-Place . . . . . . . . . . . . . . . . . . . . . . . 200
9.6 Métodos de Comparação de Conjuntos: Analisando Relações . . . . . . . . 201
9.6.1 Verificando se um Conjunto é Subconjunto de Outro . . . . . . . . 201
Verificando Subconjuntos: issubset(), <=, < . . . . . . . . . . . . . . . . . 201
9.6.2 Verificando se um Conjunto é Superconjunto de Outro . . . . . . . 202
Verificando Superconjuntos: issuperset(), >=, > . . . . . . . . . . . . . . . 202
9.6.3 Verificando se Dois Conjuntos são Disjuntos . . . . . . . . . . . . . 202
Verificando se Conjuntos são Disjuntos: isdisjoint() . . . . . . . . . . . . . 202
9.7 Conjuntos Imutáveis: frozenset (Conjunto Congelado) . . . . . . . . . . . 203
O que e um frozenset? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Criando um frozenset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Operacoes Permitidas com frozenset . . . . . . . . . . . . . . . . . . . . . . 204
Vantagem do frozenset: Ser Hasheavel . . . . . . . . . . . . . . . . . . . . 204
9.8 Quando Usar Conjuntos (set e frozenset)? . . . . . . . . . . . . . . . . . 205
Quando Usar set (Conjuntos Mutaveis) . . . . . . . . . . . . . . . . . . . . 205
Quando Usar frozenset (Conjuntos Imutaveis) . . . . . . . . . . . . . . . . 206
Comparativo Rapido: Listas, Tuplas, Dicionarios e Conjuntos . . . . . . . 207
Concluindo o Capitulo sobre Conjuntos . . . . . . . . . . . . . . . . . . . . 207

III Funções e Modularização 209


10 Definindo e Usando Funções 210
10.1 Introdução às Funções: Blocos de Código Reutilizáveis . . . . . . . . . . . 210
Por que Usar Funções? Os Superpoderes da Modularização . . . . . . . . . 210
Já Usamos Funções! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
10.2 Definindo uma Função: A Instrução def . . . . . . . . . . . . . . . . . . . 211
A Sintaxe da Definição de Função com def . . . . . . . . . . . . . . . . . . 212
O que Acontece Quando uma Função é Definida? . . . . . . . . . . . . . . 212
Exemplo: Definindo uma Função Simples de Saudação . . . . . . . . . . . 213
10.3 Chamando (Invocando) uma Função: Colocando o Código em Ação . . . . 213
A Sintaxe para Chamar uma Função . . . . . . . . . . . . . . . . . . . . . 213
O Fluxo de Execução Durante uma Chamada . . . . . . . . . . . . . . . . 214
Exemplo Prático: Chamando saudacao_inicial . . . . . . . . . . . . . . . . 214
Benefício da Reutilização em Ação . . . . . . . . . . . . . . . . . . . . . . 215
Funções Devem Ser Definidas Antes de Serem Chamadas . . . . . . . . . . 215
10.4 Parâmetros e Argumentos: Passando Informações para Funções . . . . . . 216
Definindo Funções com Parâmetros . . . . . . . . . . . . . . . . . . . . . . 216
Chamando Funções com Argumentos Posicionais . . . . . . . . . . . . . . . 216
Exemplos Práticos com Parâmetros e Argumentos . . . . . . . . . . . . . . 216
Parâmetros como Variáveis Locais . . . . . . . . . . . . . . . . . . . . . . . 217
Importância da Ordem para Argumentos Posicionais . . . . . . . . . . . . 217
10.5 Retornando Valores de Funções: A Instrução return . . . . . . . . . . . . 218
A Instrução return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Recebendo e Usando o Valor Retornado . . . . . . . . . . . . . . . . . . . . 218
Sumário

Exemplos Práticos com return . . . . . . . . . . . . . . . . . . . . . . . . . 219


Funções sem return Explícito (Retornam None) . . . . . . . . . . . . . . . 220
Múltiplas Instruções return . . . . . . . . . . . . . . . . . . . . . . . . . . 220
10.6 Docstrings (Strings de Documentação): Documentando Suas Funções . . . 220
O que e uma Docstring? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Por que Usar Docstrings? . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Como Escrever Boas Docstrings . . . . . . . . . . . . . . . . . . . . . . . . 221
Acessando Docstrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Concluindo sobre Docstrings . . . . . . . . . . . . . . . . . . . . . . . . . . 223
10.7 Escopo de Variáveis: Local vs. Global . . . . . . . . . . . . . . . . . . . . . 223
Escopo Local (Variaveis Locais) . . . . . . . . . . . . . . . . . . . . . . . . 223
Escopo Global (Variaveis Globais) . . . . . . . . . . . . . . . . . . . . . . . 224
Sombreamento de Variaveis (Shadowing) . . . . . . . . . . . . . . . . . . . 224
Modificando Globais com a Palavra-chave global . . . . . . . . . . . . . . . 224
Regra LEGB (Breve Mencao) . . . . . . . . . . . . . . . . . . . . . . . . . 225
10.8 Argumentos com Valores Padrão (Default Arguments) . . . . . . . . . . . . 225
Definindo Parâmetros com Valores Padrão . . . . . . . . . . . . . . . . . . 225
Como Funcionam os Argumentos Padrão na Chamada . . . . . . . . . . . 226
Exemplos Práticos com Argumentos Padrão . . . . . . . . . . . . . . . . . 226
Cuidado com Valores Padrão Mutáveis . . . . . . . . . . . . . . . . . . . . 227
10.9 Argumentos Nomeados (Keyword Arguments) . . . . . . . . . . . . . . . . 228
Sintaxe dos Argumentos Nomeados . . . . . . . . . . . . . . . . . . . . . . 228
Vantagens dos Argumentos Nomeados . . . . . . . . . . . . . . . . . . . . . 228
Exemplos Práticos com Argumentos Nomeados . . . . . . . . . . . . . . . 228
Misturando Argumentos Posicionais e Nomeados . . . . . . . . . . . . . . . 229
Quando Usar Argumentos Nomeados? . . . . . . . . . . . . . . . . . . . . 229
10.10Argumentos Opcionais (com Valores Padrão) vs. Argumentos Nomeados . 230
Opcionais vs. Nomeados: Diferenças e Usos . . . . . . . . . . . . . . . . . 230
10.11Funções Lambda (Funções Anônimas): Pequenas Funções em Uma Linha . 232
Funções Lambda (Funções Anônimas) . . . . . . . . . . . . . . . . . . . . . 232
Concluindo o Capitulo 10 sobre Funções . . . . . . . . . . . . . . . . . . . 234

11 Módulos e Pacotes 236


11.1 Introdução a Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
O que é um Módulo em Python? . . . . . . . . . . . . . . . . . . . . . . . 236
Por que Usar Módulos? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Relação com Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
11.2 Criando Nossos Próprios Módulos: Empacotando Nosso Código . . . . . . 237
Passo 1: Escrevendo o Código do Módulo . . . . . . . . . . . . . . . . . . . 237
Passo 2: Importando e Usando o Módulo . . . . . . . . . . . . . . . . . . . 238
Outras Formas de Importar . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Quando o Código de um Módulo é Executado? . . . . . . . . . . . . . . . . 239
11.3 A Busca por Módulos: Como Python Encontra os Módulos Importados? . 240
O Caminho de Busca de Módulos: [Link] . . . . . . . . . . . . . . . . . . 240
A Ordem de Busca de Módulos . . . . . . . . . . . . . . . . . . . . . . . . 240
Implicações da Ordem de Busca e Sombreamento . . . . . . . . . . . . . . 241
11.4 Módulos da Biblioteca Padrão do Python (Python Standard Library) . . . 241
Explorando Alguns Módulos Essenciais da Biblioteca Padrão . . . . . . . . 241
Sumário

A Ponta do Iceberg: Explore a Documentação! . . . . . . . . . . . . . . . . 244


11.5 Código Executável em Módulos . . . . . . . . . . . . . . . . . . . . . . . . 244
A Variavel Especial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
O Bloco Guarda de execução principal . . . . . . . . . . . . . . . . . . . . 245
Casos de Uso Comuns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Exemplo Prático Detalhado . . . . . . . . . . . . . . . . . . . . . . . . . . 246
11.6 Introdução a Pacotes (Packages): Organizando Módulos em Diretórios . . . 247
O que são Pacotes (Packages)? . . . . . . . . . . . . . . . . . . . . . . . . . 247
Criando um Pacote Simples . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Importando de Pacotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Subpacotes (Breve Menção) . . . . . . . . . . . . . . . . . . . . . . . . . . 249
11.7 Instalando Pacotes Externos com pip (Breve Introdução) . . . . . . . . . . 249
PyPI (Python Package Index) . . . . . . . . . . . . . . . . . . . . . . . . . 249
pip: O Instalador de Pacotes . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Ambientes Virtuais (venv): Breve Introdução . . . . . . . . . . . . . . . . 250
Concluindo o Capitulo sobre Módulos e Pacotes . . . . . . . . . . . . . . . 250

IV Programação Orientada a Objetos (POO) em Python 252


12 Conceitos Fundamentais de Programação Orientada a Objetos 253
12.1 Mudança de Paradigma: O que é Programação Orientada a Objetos? . . . 253
12.2 Classes e Objetos: Os Pilares da POO . . . . . . . . . . . . . . . . . . . . 254
Classes: As Plantas Baixas ou Moldes . . . . . . . . . . . . . . . . . . . . . 254
Objetos: As Instâncias Concretas de uma Classe . . . . . . . . . . . . . . . 255
A Relação entre Classe e Objeto . . . . . . . . . . . . . . . . . . . . . . . . 256
12.3 Atributos: As Características dos Objetos . . . . . . . . . . . . . . . . . . 256
Atributos de Instância . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
O Método Construtor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
Exemplo: Classe Cachorro com Atributos . . . . . . . . . . . . . . . . . . . 257
Acessando e Modificando Atributos de Instância . . . . . . . . . . . . . . . 258
12.4 Métodos: Os Comportamentos dos Objetos . . . . . . . . . . . . . . . . . . 258
O que são Métodos de Instância? . . . . . . . . . . . . . . . . . . . . . . . 258
Definindo Métodos de Instância . . . . . . . . . . . . . . . . . . . . . . . . 259
Chamando Métodos de Instância . . . . . . . . . . . . . . . . . . . . . . . 259
Exemplo Prático: Classe Cachorro com Métodos . . . . . . . . . . . . . . . 259
12.5 Os Quatro Pilares da POO (Introdução Breve e Conceitual) . . . . . . . . 260
Pilar 1: Abstração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Pilar 2: Encapsulamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Pilar 3: Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Pilar 4: Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Concluindo o Capítulo sobre Conceitos Fundamentais de POO . . . . . . . 262

13 Trabalhando (Mais) com Classes e Objetos 263


13.1 Classes, Objetos, __init__ e self – Os Fundamentos da POO . . . . . . . 263
Revisão Rápida: Classes, Objetos, __init__ e self . . . . . . . . . . . . . 263
13.2 Atributos de Classe vs. Atributos de Instância: Diferenciação e Uso . . . . 264
Atributos de Classe vs. Atributos de Instância . . . . . . . . . . . . . . . . 264
13.3 Métodos de Instância (Revisão e Mais Exemplos) . . . . . . . . . . . . . . 266
Sumário

Métodos de Instância (Revisão e Mais Exemplos) . . . . . . . . . . . . . . 266


13.4 Métodos Especiais (Dunder Methods) - Além do __init__ . . . . . . . . . 269
Métodos Especiais (Dunder Methods) . . . . . . . . . . . . . . . . . . . . . 269
13.4.1 O Método __str__(self) . . . . . . . . . . . . . . . . . . . . . . . 269
13.4.2 O Método __repr__(self) . . . . . . . . . . . . . . . . . . . . . . 269
13.4.3 Outros Dunder Methods Comuns (Breve Menção) . . . . . . . . . . 270
13.5 “Escondendo” Atributos: Convenções para Atributos “Privados” . . . . . . 270
“Escondendo” Atributos (Convenções) . . . . . . . . . . . . . . . . . . . . . 270
Concluindo o Capítulo sobre Classes e Objetos . . . . . . . . . . . . . . . . 272

14 Herança e Polimorfismo 274


14.1 Introdução à Herança: Criando Hierarquias de Classes . . . . . . . . . . . 274
Introdução à Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
14.1.1 O Conceito de “É um(a)” (Is-A Relationship) . . . . . . . . . . . . . 274
O Conceito de “É um(a)” (Is-A Relationship) . . . . . . . . . . . . . . . . . 274
14.1.2 Por que Usar Herança? Os Principais Benefícios . . . . . . . . . . . 275
Por que Usar Herança? Principais Benefícios . . . . . . . . . . . . . . . . . 275
14.1.3 Uma Analogia Simples: O Reino Animal (Novamente) . . . . . . . 276
Analogia: O Reino Animal (Novamente) . . . . . . . . . . . . . . . . . . . 276
14.2 Sintaxe da Herança em Python: Definindo Subclasses . . . . . . . . . . . . 276
Sintaxe da Herança em Python . . . . . . . . . . . . . . . . . . . . . . . . 276
14.3 O Método super().__init__(...) . . . . . . . . . . . . . . . . . . . . . . 279
O Método super().__init__() . . . . . . . . . . . . . . . . . . . . . . . . 279
14.4 Sobrescrevendo Métodos: Especializando Comportamentos . . . . . . . . . 281
Sobrescrevendo Métodos (Method Overriding) . . . . . . . . . . . . . . . . 281
14.5 Adicionando Novos Métodos e Atributos na Subclasse . . . . . . . . . . . . 283
Adicionando Novos Métodos e Atributos na Subclasse . . . . . . . . . . . . 283
14.6 Introdução ao Polimorfismo: “Muitas Formas” . . . . . . . . . . . . . . . . 285
Introdução ao Polimorfismo: “Muitas Formas” . . . . . . . . . . . . . . . . 285
14.7 Herança Múltipla: Herdando de Várias Fontes . . . . . . . . . . . . . . . . 287
Herança Múltipla (Breve Menção e Cuidados) . . . . . . . . . . . . . . . . 287
Concluindo o Capítulo sobre Herança e Polimorfismo . . . . . . . . . . . . 288

15 Tópicos Avançados em POO 289


15.1 Introdução aos Tópicos Avançados em POO: Refinando Suas Classes . . . . 289
Introdução aos Tópicos Avançados em POO . . . . . . . . . . . . . . . . . 289
15.2 Propriedades (@property): Controlando o Acesso a Atributos . . . . . . . 290
Propriedades (@property): Controlando Acesso . . . . . . . . . . . . . . . 290
15.3 Métodos de Classe (@classmethod) . . . . . . . . . . . . . . . . . . . . . . 292
Métodos de Classe (@classmethod) . . . . . . . . . . . . . . . . . . . . . . 292
15.4 Métodos Estáticos (@staticmethod): Funções Utilitárias Dentro de Classes 294
Métodos Estáticos (@staticmethod) . . . . . . . . . . . . . . . . . . . . . . 294
15.5 Classes Abstratas e Métodos Abstratos (Módulo abc): Definindo Contratos 296
Classes Abstratas e Métodos Abstratos (Módulo abc) . . . . . . . . . . . . 296
Concluindo o Capítulo sobre Tópicos Avançados em POO . . . . . . . . . . 298
Sumário

V Manipulação de Arquivos e Tratamento de Erros 299


16 Leitura e Escrita de Arquivos 300
16.1 Introdução à Manipulação de Arquivos: Persistindo e Acessando Dados . . 300
Introdução à Manipulação de Arquivos . . . . . . . . . . . . . . . . . . . . 300
16.2 Abrindo e Fechando Arquivos: A Função open() e a Instrução with . . . . 301
A Função Embutida open() . . . . . . . . . . . . . . . . . . . . . . . . . . 301
A Necessidade de Fechar Arquivos: close() . . . . . . . . . . . . . . . . . . 302
A Instrução with open() (Forma Recomendada) . . . . . . . . . . . . . . . 302
Exemplos Práticos com with open() . . . . . . . . . . . . . . . . . . . . . . 303
16.3 Lendo Arquivos de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
Lendo Arquivos de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
16.4 Escrevendo em Arquivos de Texto . . . . . . . . . . . . . . . . . . . . . . . 307
Escrevendo em Arquivos de Texto . . . . . . . . . . . . . . . . . . . . . . . 307
16.5 Trabalhando com Caminhos de Arquivo (Módulo [Link]) . . . . . . . . . 308
Trabalhando com Caminhos de Arquivo ([Link]) . . . . . . . . . . . . . . 308
16.6 Arquivos Binários (Breve Introdução) . . . . . . . . . . . . . . . . . . . . . 311
Arquivos Binários (Breve Introdução) . . . . . . . . . . . . . . . . . . . . . 311
Concluindo o Capítulo sobre Leitura e Escrita de Arquivos . . . . . . . . . 312

17 Tratamento de Exceções 313


17.1 Introdução a Erros e Exceções: Quando as Coisas Saem do Script . . . . . 313
Introdução a Erros e Exceções . . . . . . . . . . . . . . . . . . . . . . . . . 313
17.2 O Bloco try-except: Capturando e Tratando Exceções . . . . . . . . . . . 315
O Bloco try-except: Capturando e Tratando Exceções . . . . . . . . . . . . 315
17.3 Capturando Múltiplas Exceções: Lidando com Diferentes Tipos de Erros . 317
Capturando Múltiplas Exceções . . . . . . . . . . . . . . . . . . . . . . . . 317
17.4 A Cláusula else no Bloco try-except . . . . . . . . . . . . . . . . . . . . 319
A Cláusula else no Bloco try-except . . . . . . . . . . . . . . . . . . . . . . 319
17.5 A Cláusula finally: Código que Sempre Executa . . . . . . . . . . . . . . 321
A Cláusula finally: Código que Sempre Executa . . . . . . . . . . . . . . . 321
17.6 Levantando Exceções com raise: Sinalizando Seus Próprios Erros . . . . . 323
Levantando Exceções com raise . . . . . . . . . . . . . . . . . . . . . . . . 323
17.7 Definindo Exceções Personalizadas . . . . . . . . . . . . . . . . . . . . . . 326
Definindo Exceções Personalizadas . . . . . . . . . . . . . . . . . . . . . . 326
Concluindo o Capítulo sobre Tratamento de Exceções . . . . . . . . . . . . 327

VI Bibliotecas Essenciais para Engenharia 328


18 NumPy para Computação Numérica 329
18.1 Introdução ao NumPy: Computação Numérica Eficiente com Arrays . . . . 329
Introdução ao NumPy: Computação Numérica Eficiente . . . . . . . . . . 329
18.2 Criando Arrays NumPy (ndarray) . . . . . . . . . . . . . . . . . . . . . . 330
Criando Arrays NumPy (ndarray) . . . . . . . . . . . . . . . . . . . . . . . 330
18.2.1 Criando Arrays a Partir de Sequências Python Existentes . . . . . . 331
18.2.2 Criando Arrays com Valores Específicos . . . . . . . . . . . . . . . . 331
18.2.3 Criando Arrays com Sequências Numéricas . . . . . . . . . . . . . . 332
18.2.4 Criando Arrays Especiais . . . . . . . . . . . . . . . . . . . . . . . . 332
Sumário

18.2.5 Criando Arrays com Valores Aleatórios ([Link]) . . . . . . . . 333


18.3 Atributos Fundamentais de um ndarray . . . . . . . . . . . . . . . . . . . 333
Atributos Fundamentais de um ndarray . . . . . . . . . . . . . . . . . . . . 333
18.4 Indexação e Fatiamento (Slicing) de Arrays NumPy . . . . . . . . . . . . . 335
Indexação e Fatiamento de Arrays NumPy . . . . . . . . . . . . . . . . . . 335
18.4.1 Indexação e Fatiamento em Arrays Unidimensionais (1D) . . . . . . 335
18.4.2 Indexação e Fatiamento em Arrays Bidimensionais (2D - Matrizes) 336
18.4.3 Indexação Booleana (Mascaramento) . . . . . . . . . . . . . . . . . 337
18.4.4 Indexação com Array de Inteiros (Fancy Indexing) . . . . . . . . . . 337
18.5 Operações com Arrays: Vetorização e Broadcasting . . . . . . . . . . . . . 338
Operações com Arrays: Vetorização e Broadcasting . . . . . . . . . . . . . 338
18.5.1 Operações Aritméticas Elemento a Elemento (Vetorização) . . . . . 338
18.5.2 Funções Universais (ufuncs) . . . . . . . . . . . . . . . . . . . . . . 339
18.5.3 Broadcasting: Operando com Arrays de Formas Diferentes . . . . . 339
18.6 Funções Matemáticas e Estatísticas Básicas com NumPy . . . . . . . . . . 340
Funções Matemáticas e Estatísticas Básicas . . . . . . . . . . . . . . . . . 340
18.7 Álgebra Linear Básica com NumPy (Submódulo [Link]) . . . . . . . . 342
Álgebra Linear Básica com NumPy ([Link]) . . . . . . . . . . . . . . . . 342
18.8 Leitura e Escrita de Arrays NumPy em Arquivos (Simples) . . . . . . . . . 344
Leitura e Escrita de Arrays em Arquivos . . . . . . . . . . . . . . . . . . . 344
18.8.1 Salvando e Carregando no Formato Binário NumPy (.npy, .npz) . 344
18.8.2 Salvando e Carregando Arrays em Formato Texto . . . . . . . . . . 345
Concluindo o Capítulo sobre NumPy . . . . . . . . . . . . . . . . . . . . . 346

19 Matplotlib e Seaborn para Visualização de Dados 347


19.1 Introdução à Visualização de Dados com Python . . . . . . . . . . . . . . . 347
Introdução à Visualização de Dados com Python . . . . . . . . . . . . . . . 347
19.2 Matplotlib: A Biblioteca Fundamental para Gráficos . . . . . . . . . . . . 348
Matplotlib: A Biblioteca Fundamental . . . . . . . . . . . . . . . . . . . . 348
19.2.1 Introdução ao Matplotlib e o Módulo pyplot . . . . . . . . . . . . 349
Matplotlib: Introdução e Módulo pyplot . . . . . . . . . . . . . . . . . . . 349
19.2.2 Gráficos de Linha ([Link]()) . . . . . . . . . . . . . . . . . . . . 350
19.2.3 Gráficos de Dispersão (Scatter Plots - [Link]()) . . . . . . . 352
19.2.4 Gráficos de Barras ([Link](), [Link]()) . . . . . . . . . . . . 353
19.2.5 Histogramas ([Link]()): Entendendo a Distribuição dos Dados . 355
19.2.6 Gráficos de Pizza (Pie Charts - [Link]()): Mostrando Proporções
(Uso com Moderação) . . . . . . . . . . . . . . . . . . . . . . . . . . 357
19.2.7 Trabalhando com Múltiplos Gráficos (Subplots) . . . . . . . . . . . 358
19.2.8 Salvando Gráficos em Arquivos: Persistindo Suas Visualizações . . . 360
19.3 Seaborn: Gráficos Estatísticos Mais Elaborados e Atraentes . . . . . . . . . 361
Seaborn: Gráficos Estatísticos Elaborados . . . . . . . . . . . . . . . . . . 361
19.3.1 Introdução ao Seaborn: Visualizações Estatísticas Simplificadas . . 361
19.3.2 Gráficos de Distribuição com Seaborn . . . . . . . . . . . . . . . . . 362
19.3.3 Gráficos Categóricos com Seaborn . . . . . . . . . . . . . . . . . . . 364
19.3.4 Gráficos de Relação com Seaborn . . . . . . . . . . . . . . . . . . . 366
19.3.5 Mapas de Calor (Heatmaps - [Link]()) . . . . . . . . . . . . 368
19.3.6 Personalizando Estilos e Temas no Seaborn . . . . . . . . . . . . . . 370
19.4 Dicas para Boas Visualizações: Comunicando com Clareza e Impacto . . . 371
Sumário

Dicas para Boas Visualizações . . . . . . . . . . . . . . . . . . . . . . . . . 371


Concluindo o Capítulo sobre Matplotlib e Seaborn . . . . . . . . . . . . . . 372

20 Pandas para Análise e Manipulação de Dados 374


20.1 Introdução ao Pandas: Estruturas de Dados Poderosas para Análise . . . . 374
Introdução ao Pandas: Estruturas Poderosas . . . . . . . . . . . . . . . . . 374
20.2 Introdução ao Pandas Series: Arrays Unidimensionais Rotulados . . . . . . 375
Pandas Series: Arrays Unidimensionais Rotulados . . . . . . . . . . . . . . 375
20.3 Pandas DataFrame: Estruturas de Dados Tabulares Bidimensionais . . . . 379
Pandas DataFrame: Estruturas Tabulares 2D . . . . . . . . . . . . . . . . 379
20.4 Seleção e Indexação de Dados em DataFrames . . . . . . . . . . . . . . . . 384
Seleção e Indexação em DataFrames . . . . . . . . . . . . . . . . . . . . . . 384
20.4.1 Selecionando Colunas . . . . . . . . . . . . . . . . . . . . . . . . . . 385
20.4.2 Selecionando Linhas (e Colunas) com .loc[] e .iloc[] . . . . . . 386
20.4.3 Indexação Booleana (Filtragem de Linhas por Condição) . . . . . . 387
20.4.4 Uma Breve Nota sobre set_index() e reset_index() . . . . . . . 388
20.5 Lidando com Dados Ausentes (Missing Data - NaN) . . . . . . . . . . . . . 389
Lidando com Dados Ausentes (Missing Data - NaN) . . . . . . . . . . . . . 389
20.6 Operações Comuns e Funções em DataFrames . . . . . . . . . . . . . . . . 392
Operações Comuns e Funções em DataFrames . . . . . . . . . . . . . . . . 392
20.7 Agrupamento de Dados (Group By): Analisando Dados por Categorias . . 394
Agrupamento de Dados (Group By) . . . . . . . . . . . . . . . . . . . . . . 394
20.8 Mesclando, Juntando e Concatenando DataFrames (Merge, Join, Concat) . 397
Combinando DataFrames: Merge, Join, Concat . . . . . . . . . . . . . . . 397
20.8.1 Concatenando DataFrames com [Link]() . . . . . . . . . . . . 397
20.8.2 Combinando DataFrames com [Link]() (Estilo SQL JOIN) . . . 398
20.8.3 Juntando DataFrames com o Método .join() . . . . . . . . . . . . 399
20.9 Leitura e Escrita de Arquivos com Pandas . . . . . . . . . . . . . . . . . . 400
Leitura e Escrita de Arquivos com Pandas . . . . . . . . . . . . . . . . . . 400
20.9.1 Lendo e Escrevendo Arquivos CSV (Comma-Separated Values) . . . 400
20.9.2 Lendo e Escrevendo Arquivos Excel (.xls, .xlsx) . . . . . . . . . . 401
20.9.3 Breve Menção a Outros Formatos . . . . . . . . . . . . . . . . . . . 402
Concluindo o Capítulo sobre Pandas . . . . . . . . . . . . . . . . . . . . . 403

21 SciPy para Computação Científica (Introdução) 404


21.1 Introdução ao SciPy: Ferramentas Científicas Avançadas sobre NumPy . . 404
Introdução ao SciPy: Ferramentas Científicas Avançadas sobre NumPy . . 404
21.1.1 O que é SciPy? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
O que é SciPy? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
21.1.2 Como a SciPy Complementa o NumPy? . . . . . . . . . . . . . . . 404
Como a SciPy Complementa o NumPy? . . . . . . . . . . . . . . . . . . . 404
21.1.3 Estrutura da SciPy: Uma Coleção de Submódulos . . . . . . . . . . 405
Estrutura da SciPy: Uma Coleção de Submódulos . . . . . . . . . . . . . . 405
21.1.4 Convenção de Importação . . . . . . . . . . . . . . . . . . . . . . . 405
Convenção de Importação da SciPy . . . . . . . . . . . . . . . . . . . . . . 405
21.1.5 O que Esperar Deste Capítulo (Uma Introdução) . . . . . . . . . . 406
O que Esperar Deste Capítulo (SciPy) . . . . . . . . . . . . . . . . . . . . 406
21.2 Funções Especiais ([Link]): Indo Além da Matemática Básica . . 406
Funções Especiais ([Link]) . . . . . . . . . . . . . . . . . . . . . . . . 406
Sumário

21.2.1 Importando o Submódulo special . . . . . . . . . . . . . . . . . . 406


21.2.2 Alguns Exemplos de Funções Especiais Comuns . . . . . . . . . . . 407
21.2.3 Explorando Mais Funções Especiais . . . . . . . . . . . . . . . . . . 409
21.3 Álgebra Linear com [Link] (Complementando NumPy) . . . . . . . 409
Álgebra Linear com [Link] (Detalhado) . . . . . . . . . . . . . . . . . 409
21.3.1 Relação com [Link] . . . . . . . . . . . . . . . . . . . . . . 409
21.3.2 Funcionalidades Adicionais ou Aprimoradas em [Link] . . . 410
21.3.3 Quando Usar [Link] vs. [Link]? . . . . . . . . . . . 413
Quando Usar [Link] vs. [Link]? . . . . . . . . . . . . . . . . . . 413
21.4 Otimização e Busca de Raízes ([Link]): Encontrando Soluções e
Pontos Ótimos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
Otimização e Busca de Raízes ([Link]) . . . . . . . . . . . . . . . . 414
21.4.1 Busca de Raízes de Funções Escalares (Uma Variável) . . . . . . . . 414
21.4.2 Otimização (Minimização) de Funções Escalares (Uma Variável) . . 415
21.4.3 Otimização (Minimização) de Funções Multivariáveis (Breve Intro-
dução) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
21.5 Interpolação ([Link]): Estimando Valores Entre Pontos de
Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
Interpolação ([Link]) . . . . . . . . . . . . . . . . . . . . . . . . 416
21.6 Integração Numérica ([Link]): Calculando Áreas e Acumulações 419
Integração Numérica ([Link]) . . . . . . . . . . . . . . . . . . . . . 419
21.6.1 Integrando Funções Definidas Analiticamente com quad() . . . . . 419
21.6.2 Integrando Dados Amostrados (Discretos) . . . . . . . . . . . . . . 421
21.6.3 Outras Funções de Integração (Breve Menção) . . . . . . . . . . . . 422
21.7 Processamento de Sinais ([Link]) (Breve Menção) . . . . . . . . . 422
Processamento de Sinais ([Link]) (Breve Menção) . . . . . . . . . . . 422
21.8 Estatísticas com [Link] (Complementando NumPy) . . . . . . . . . 424
Estatísticas com [Link] . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
21.8.1 Trabalhando com Distribuições de Probabilidade . . . . . . . . . . . 425
21.8.2 Testes de Hipóteses Estatísticas (Introdução Muito Básica) . . . . . 427
21.8.3 Estatísticas Descritivas Adicionais . . . . . . . . . . . . . . . . . . . 429
Concluindo o Capítulo sobre SciPy . . . . . . . . . . . . . . . . . . . . . . 429

VII Exercícios e Projetos 432


Exercícios e Projetos Sugeridos 433
Parte I: Fundamentos Essenciais do Python (Exercícios) . . . . . . . . . . . . . 433
Capítulo 1: Introdução ao Python e à Programação (Exercícios) . . . . . . 433
Capítulo 2: Sintaxe Básica e Tipos de Dados (Exercícios) . . . . . . . . . . 437
Capítulo 3: Operadores e Expressões (Exercícios) . . . . . . . . . . . . . . 441
Capítulo 4: Estruturas de Controle Condicional (Exercícios) . . . . . . . . 444
Capítulo 5: Estruturas de Repetição (Exercícios) . . . . . . . . . . . . . . 450
Parte II: Estruturas de Dados Fundamentais (Exercícios) . . . . . . . . . . . . . 454
Capítulo 6: Listas (Lists) (Exercícios) . . . . . . . . . . . . . . . . . . . . . 454
Capítulo 7: Tuplas (Tuples) (Exercícios) . . . . . . . . . . . . . . . . . . . 460
Capítulo 8: Dicionários (Dictionaries) (Exercícios) . . . . . . . . . . . . . . 466
Capítulo 9: Conjuntos (Sets) (Exercícios) . . . . . . . . . . . . . . . . . . . 472
Parte III: Funções e Modularização (Exercícios) . . . . . . . . . . . . . . . . . . 477
Sumário

Capítulo 10: Definindo e Usando Funções (Exercícios) . . . . . . . . . . . . 477


Capítulo 11: Módulos e Pacotes (Exercícios) . . . . . . . . . . . . . . . . . 484
Parte IV: Programação Orientada a Objetos em Python (Exercícios) . . . . . . 491
Capítulo 12: Conceitos Fundamentais de POO (Exercícios) . . . . . . . . . 491
Capítulo 13: Trabalhando (Mais) com Classes e Objetos (Exercícios) . . . 495
Capítulo 14: Herança e Polimorfismo (Exercícios) . . . . . . . . . . . . . . 502
Capítulo 15: Tópicos Avançados em POO (Exercícios) . . . . . . . . . . . . 508
Parte V: Manipulação de Arquivos e Tratamento de Erros (Exercícios) . . . . . 515
Capítulo 16: Leitura e Escrita de Arquivos (Exercícios) . . . . . . . . . . . 515
Capítulo 17: Tratamento de Exceções (Exercícios) . . . . . . . . . . . . . . 521
Parte VI: Bibliotecas Essenciais para Engenharia (Exercícios) . . . . . . . . . . 528
Capítulo 18: NumPy (Exercícios) . . . . . . . . . . . . . . . . . . . . . . . 528
Capítulo 19: Matplotlib e Seaborn (Exercícios) . . . . . . . . . . . . . . . . 533
Capítulo 20: Pandas (Exercícios) . . . . . . . . . . . . . . . . . . . . . . . 540
Capítulo 21: SciPy (Exercícios) . . . . . . . . . . . . . . . . . . . . . . . . 545

A Guia Rápido de Sintaxe Python 550

Apêndice A: Guia Rápido de Sintaxe Python 550


A.1 Comentários . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
A.2 Variáveis, Atribuição e Tipos de Dados Primitivos . . . . . . . . . . . . . . 550
A.3 Entrada e Saída de Dados Básica . . . . . . . . . . . . . . . . . . . . . . . 551
A.4 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
A.4.1 Operadores Aritméticos . . . . . . . . . . . . . . . . . . . . . . . . 552
A.4.2 Operadores de Atribuição . . . . . . . . . . . . . . . . . . . . . . . 552
A.4.3 Operadores de Comparação (Relacionais) . . . . . . . . . . . . . . . 553
A.4.4 Operadores Lógicos (Booleanos) . . . . . . . . . . . . . . . . . . . . 553
A.4.5 Operadores de Identidade . . . . . . . . . . . . . . . . . . . . . . . 553
A.4.6 Operadores de Associação (Pertinência) . . . . . . . . . . . . . . . . 554
A.4.7 Precedência de Operadores (Breve Menção) . . . . . . . . . . . . . 554
A.5 Estruturas de Controle Condicional . . . . . . . . . . . . . . . . . . . . . . 554
A.5.1 A Instrução if (Se) . . . . . . . . . . . . . . . . . . . . . . . . . . . 555
A.5.2 A Instrução if-else (Se-Senão) . . . . . . . . . . . . . . . . . . . . 555
A.5.3 A Instrução if-elif-else (Se-Senão Se-Senão) . . . . . . . . . . . 555
A.5.4 O Operador Condicional Ternário (Expressão Condicional) . . . . . 555
A.5.5 Valores “Truthy” e “Falsy” em Condições . . . . . . . . . . . . . . . 556
A.6 Estruturas de Repetição (Laços) . . . . . . . . . . . . . . . . . . . . . . . . 556
A.6.1 O Laço while (Enquanto) . . . . . . . . . . . . . . . . . . . . . . . 557
A.6.2 O Laço for (Para) . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
A.6.3 Comandos de Controle de Laço: break e continue . . . . . . . . . 558
A.6.4 A Cláusula else em Laços (Opcional) . . . . . . . . . . . . . . . . 559
A.6.5 Laços Aninhados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
A.7 Estruturas de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
A.7.1 Listas (list) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
A.7.2 Tuplas (tuple) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
A.7.3 Dicionários (dict) . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
A.7.4 Conjuntos (set) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
A.7.5 Conjuntos Imutáveis (frozenset) . . . . . . . . . . . . . . . . . . . 564
A.8 Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
Sumário

A.8.1 Definição de Funções com def . . . . . . . . . . . . . . . . . . . . . 564


A.8.2 Chamando (Invocando) Funções . . . . . . . . . . . . . . . . . . . . 565
A.8.3 Parâmetros e Argumentos . . . . . . . . . . . . . . . . . . . . . . . 565
A.8.3 Parâmetros e Argumentos . . . . . . . . . . . . . . . . . . . . . . . . 565
A.8.4 Retornando Valores com return . . . . . . . . . . . . . . . . . . . . 566
A.8.5 Docstrings (Strings de Documentação) . . . . . . . . . . . . . . . . 566
A.8.6 Escopo de Variáveis (Local vs. Global) . . . . . . . . . . . . . . . . 566
A.8.7 Argumentos com Valores Padrão (Default Arguments) . . . . . . . 567
A.8.8 Argumentos Nomeados (Keyword Arguments) . . . . . . . . . . . . 567
A.8.9 Funções Lambda (Funções Anônimas) . . . . . . . . . . . . . . . . 568
A.9 Módulos e Pacotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
A.9.1 Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
A.9.2 Pacotes (Packages) . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
A.9.3 Biblioteca Padrão do Python (Breve Menção) . . . . . . . . . . . . 571
A.9.4 Instalando Pacotes Externos com pip (Breve Menção) . . . . . . . 571
A.10 Programação Orientada a Objetos (POO) . . . . . . . . . . . . . . . . . . 571
A.10.1 Classes e Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571
A.10.2 Construtor (__init__) e Atributos de Instância . . . . . . . . . . . 572
A.10.3 Métodos de Instância . . . . . . . . . . . . . . . . . . . . . . . . . . 572
A.10.4 Atributos de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
A.10.5 Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
A.10.6 Métodos Especiais (Dunder Methods) . . . . . . . . . . . . . . . . . 574
A.10.7 Encapsulamento (Convenções de Nomenclatura) . . . . . . . . . . . 574
A.10.8 Propriedades (@property, @*.setter, @*.deleter) . . . . . . . . . 574
A.10.9 Métodos de Classe (@classmethod) . . . . . . . . . . . . . . . . . . 575
A.10.10Métodos Estáticos (@staticmethod) . . . . . . . . . . . . . . . . . 576
A.10.11Classes Abstratas (Módulo abc) . . . . . . . . . . . . . . . . . . . . 576
A.11 Manipulação de Arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
A.11.1 Abrindo e Fechando Arquivos . . . . . . . . . . . . . . . . . . . . . 577
A.11.2 Lendo Arquivos de Texto . . . . . . . . . . . . . . . . . . . . . . . . 577
A.11.3 Escrevendo em Arquivos de Texto . . . . . . . . . . . . . . . . . . . 578
A.11.4 Módulo [Link] (Funções Comuns para Caminhos) . . . . . . . . . 578
A.11.5 Arquivos Binários (Breve Menção) . . . . . . . . . . . . . . . . . . 579
A.12 Tratamento de Exceções . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
A.12.1 O Bloco try-except . . . . . . . . . . . . . . . . . . . . . . . . . . 579
A.12.2 Capturando Múltiplas Exceções . . . . . . . . . . . . . . . . . . . . 580
A.12.3 A Cláusula else no Bloco try-except . . . . . . . . . . . . . . . . 581
A.12.4 A Cláusula finally: Código que Sempre Executa . . . . . . . . . . 581
A.12.5 Levantando Exceções com raise . . . . . . . . . . . . . . . . . . . 582
A.12.6 Definindo Exceções Personalizadas (Breve Menção) . . . . . . . . . 582
A.13 Bibliotecas Essenciais (Breve Menção) . . . . . . . . . . . . . . . . . . . . 582
A.13.1 NumPy (np) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
A.13.2 Pandas (pd) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
A.13.3 Matplotlib (pyplot como plt) . . . . . . . . . . . . . . . . . . . . . 583
A.13.4 Seaborn (sns) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
A.13.5 SciPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584

B Recursos Online e Comunidades Python 586


Sumário

Apêndice B: Recursos Online e Comunidades Python 586


B.1 Documentação Oficial do Python . . . . . . . . . . . . . . . . . . . . . . . 586
B.2 Google Colaboratory (Colab) com o Código do Livro . . . . . . . . . . . . 587
B.3 Outras Plataformas de Aprendizado e Cursos Online . . . . . . . . . . . . 588
B.4 Documentação das Principais Bibliotecas Científicas e de Dados . . . . . . 589
Parte I

Fundamentos Essenciais do Python

1
Capítulo 1

Introdução ao Python e à Programação

Imagine uma linguagem que permite que você escreva instruções para um computador
de forma quase tão natural quanto escreve uma frase em português. Uma linguagem
poderosa o suficiente para analisar grandes volumes de dados, controlar robôs ou criar
aplicações web interativas, mas ao mesmo tempo elegante e amigável para quem está
apenas começando no mundo da programação. Essa linguagem existe, e ela se chama
Python!
Neste primeiro capítulo, vamos embarcar juntos na jornada de descoberta do Python.
Começaremos entendendo o que exatamente é essa linguagem, um pouco de sua história,
a filosofia que a norteia e por que ela se tornou uma das ferramentas mais queridas e
versáteis para programadores, estudantes, cientistas e engenheiros ao redor do mundo,
inclusive aqui no Brasil, em pleno 2025.

1.1 O que é Python? Uma Visão Geral


Python é uma linguagem de programação de alto nível, o que significa que
sua sintaxe (as regras de escrita) é projetada para ser facilmente legível e compreensível
por seres humanos. Diferente de linguagens de baixo nível, que estão mais próximas da
linguagem da máquina (sequências de zeros e uns), o Python nos permite expressar ideias
complexas com menos linhas de código e de forma mais intuitiva.
Ela é também uma linguagem interpretada. Isso quer dizer que os programas em
Python são executados por um programa chamado“interpretador”, que lê o seu código
linha por linha e o converte em instruções que o computador pode entender em tempo real.
Esse processo difere das linguagens compiladas (como o C, que você pode conhecer do
seu livro anterior), onde todo o código é primeiro traduzido para linguagem de máquina
(gerando um arquivo executável) antes de ser rodado. A natureza interpretada do Python
facilita o desenvolvimento rápido e o teste de pequenos trechos de código.

Um Pouco de História e Filosofia: O Zen de Python


O Python foi criado no final da década de 1980 e lançado pela primeira vez em 1991
por um programador holandês chamado Guido van Rossum. Curiosamente, o nome
“Python” não vem da cobra, mas sim do grupo de comédia britânico Monty Python, do
qual Guido era fã!
Mais do que apenas um conjunto de regras e comandos, o Python carrega consigo
uma filosofia de desenvolvimento de software que valoriza a clareza, a simplicidade e

1
2 Capítulo 1. Introdução ao Python e à Programação

a legibilidade do código. Esses princípios estão elegantemente resumidos no “Zen de


Python” (The Zen of Python), um conjunto de 19 ’aforismos escritos por Tim Peters,
um dos principais desenvolvedores da linguagem. Estes “mandamentos” servem como um
guia para escrever código Pythônico – ou seja, código que não apenas funciona, mas que
também é elegante e alinhado com o espírito da linguagem.
Você pode visualizar o Zen de Python diretamente no seu interpretador Python (algo
que você aprenderá a usar em breve!) digitando o comando:
1 import this
Código 1.1: Acessando o Zen de Python (apenas o comando)
Ao fazer isso, os seguintes princípios serão exibidos:

O Zen de Python, por Tim Peters

Bonito é melhor que feio.


Explícito é melhor que implícito.
Simples é melhor que complexo.
Complexo é melhor que complicado.
Linear é melhor do que aninhado.
Esparso é melhor que denso.
Legibilidade conta.
Casos especiais não são especiais o bastante para quebrar as regras.
Embora a praticidade vença a pureza.
Erros nunca devem passar silenciosamente.
A menos que sejam explicitamente silenciados.
Diante da ambiguidade, recuse a tentação de adivinhar.
Deveria haver um — e preferencialmente só um — modo óbvio para fazer algo.
Embora esse modo possa não ser óbvio à primeira vista a menos que você seja
holandês.
Agora é melhor que nunca.
Embora nunca frequentemente seja melhor do que exatamente agora.
Se a implementação é difícil de explicar, é uma má ideia.
Se a implementação é fácil de explicar, pode ser uma boa ideia.
Namespaces são uma grande ideia — vamos fazer mais desses!

Analisar cada um desses aforismos pode levar a discussões profundas sobre design de
software, mas, para quem está começando, a mensagem principal é: escreva código
que você e outros possam entender facilmente. A clareza e a simplicidade são
altamente valorizadas na comunidade Python. Ao longo deste livro, tentaremos aplicar
esses princípios nos nossos exemplos e explicações.
Essa filosofia não é apenas um detalhe curioso; ela influencia diretamente a forma como
os programas em Python são escritos e organizados, tornando-os, em geral, mais fáceis de
manter e depurar (corrigir erros).
1.2. Por que Python para Resolver Problemas? 3

Como o Python “Pensa”?


Para ilustrar a clareza do Python, imagine que você queira calcular a área de um
retângulo. Em linguagem natural, você diria algo como:
“A área de um retângulo é igual à sua base multiplicada pela sua altura.”
Em Python, a tradução dessa ideia é quase direta:
1 # Exemplo conceitual de codigo Python
2 base = 10
3 altura = 5
4 area = base * altura
5 # Para exibir o resultado na tela , poderiamos usar :
6 # print ( f " A area e : { area }")

Código 1.2: Cálculo de área (conceitual)


Perceba como as palavras base, altura e area são escolhidas por nós para dar
significado ao que estamos fazendo, e a operação base * altura é exatamente como
faríamos na matemática. Essa proximidade com a linguagem natural e a matemática
convencional, alinhada com os princípios do Zen de Python, é um dos grandes trunfos da
linguagem para iniciantes.
Nas próximas seções, exploraremos mais a fundo por que essa linguagem se tornou tão
popular e como você pode começar a usá-la para resolver seus próprios problemas.

1.2 Por que Python para Resolver Problemas?


Agora que você já teve um primeiro contato com o que é o Python e sua filosofia
focada na clareza, pode estar se perguntando: “Com tantas linguagens de programação
por aí, por que eu deveria escolher aprender Python, especialmente para me ajudar a
resolver problemas práticos, seja nos estudos ou na minha futura carreira?”. A resposta
é multifacetada e reside na combinação única de simplicidade, poder e versatilidade que
o Python oferece. Não é à toa que, em 2025, ele continua sendo uma das linguagens
mais populares e demandadas em todo o mundo, do Brasil à Austrália, em empresas de
tecnologia, universidades e centros de pesquisa.

Simplicidade que Impulsiona, Legibilidade que Facilita


Como vimos, a sintaxe do Python foi projetada para ser limpa e intuitiva, quase como
ler pseudocódigo. Para quem está começando, isso é uma vantagem imensa, pois reduz a
"carga cognitiva"inicial – você gasta menos tempo lutando com regras complexas e mais
tempo focando na lógica para resolver o problema em si. Para programadores experientes,
essa clareza se traduz em desenvolvimento mais rápido e código mais fácil de manter e
colaborar.

Um Arsenal de Ferramentas: O Poder das Bibliotecas


Uma das maiores forças do Python é seu ecossistema gigantesco de bibliotecas (também
chamadas de módulos ou pacotes). Pense em bibliotecas como caixas de ferramentas
especializadas, repletas de código pronto e otimizado que você pode usar nos seus programas
sem ter que criar tudo do zero. Para quase qualquer tarefa que você possa imaginar,
4 Capítulo 1. Introdução ao Python e à Programação

provavelmente já existe uma biblioteca Python para ajudar. Isso significa que você não
precisa “reinventar a roda” e pode construir soluções sofisticadas de forma muito mais
eficiente.
Algumas áreas onde as bibliotecas Python brilham intensamente são:

• Computação Numérica e Científica:

– NumPy (Numerical Python): Fundamental para trabalhar com arrays (ve-


tores e matrizes) de forma eficiente, álgebra linear e outras operações numéricas.
Se você lida com cálculos em engenharia ou ciências, NumPy será um grande
aliado.
– SciPy (Scientific Python): Construída sobre o NumPy, oferece um vasto le-
que de funções para otimização, integração, processamento de sinais, estatística,
e muito mais.

• Análise e Manipulação de Dados:

– Pandas: Se você precisa ler dados de planilhas (Excel, CSV), limpar esses
dados, transformá-los, analisá-los e prepará-los para visualização ou modelagem,
o Pandas é a ferramenta ideal. Ele introduz estruturas de dados poderosas
como DataFrames.

• Visualização de Dados:

– Matplotlib: É a biblioteca mais tradicional para criar uma enorme variedade


de gráficos estáticos, animados e interativos – desde simples gráficos de linha e
barra até visualizações 3D.
– Seaborn: Construída sobre o Matplotlib, facilita a criação de gráficos estatísti-
cos mais atraentes e informativos com menos código.

• Automação de Tarefas: Python é excelente para escrever scripts que automatizam


tarefas repetitivas, como renomear arquivos em massa, organizar pastas, extrair
informações de sites (web scraping) ou interagir com outros programas.

• Desenvolvimento Web: Frameworks como Django e Flask permitem construir


desde sites simples até aplicações web complexas e robustas com Python.

• Inteligência Artificial (IA) e Aprendizado de Máquina (Machine Learning):


Bibliotecas como Scikit-learn (para algoritmos clássicos de machine learning),
TensorFlow e PyTorch (para deep learning e redes neurais) fizeram do Python a
linguagem dominante nessas áreas de ponta.

Uma Comunidade Vibrante e Acolhedora


Aprender algo novo pode ser desafiador, mas com Python, você nunca está sozinho.
Existe uma comunidade global imensa e muito ativa de desenvolvedores Python. Isso se
traduz em:

• Documentação Abundante: A documentação oficial do Python e de suas princi-


pais bibliotecas é geralmente excelente e muito completa.
1.2. Por que Python para Resolver Problemas? 5

• Tutoriais e Cursos: Há uma infinidade de tutoriais, blogs, vídeos e cursos (muitos


gratuitos) disponíveis online para todos os níveis.

• Fóruns de Ajuda: Sites como o Stack Overflow têm milhões de perguntas e


respostas sobre Python, tornando fácil encontrar soluções para problemas comuns.

Multiplataforma e Integração
O código Python que você escreve em um sistema operacional (como Windows) geral-
mente funciona sem modificações (ou com poucas) em outros (como macOS ou Linux).
Essa portabilidade é uma grande vantagem. Além disso, Python é frequentemente usado
como uma "linguagem cola", capaz de integrar diferentes sistemas e componentes escritos
em outras linguagens.

Produtividade em Foco
A combinação da sintaxe simples, a natureza interpretada (que permite testar ideias
rapidamente) e a vasta quantidade de bibliotecas disponíveis torna os desenvolvedores
Python muito produtivos. Você pode ir da ideia à primeira versão funcional de um
programa de forma notavelmente rápida.

Relevância para Você, Estudante (e Futuro Profissional)


Como estudante, especialmente nas áreas de engenharia, ciências ou tecnologia, aprender
Python pode abrir muitas portas:

• Auxílio nos Estudos: Automatize cálculos complexos de disciplinas, simule fenô-


menos físicos ou químicos, analise dados de experimentos de laboratório.

• Desenvolvimento de Ferramentas: Crie pequenas ferramentas personalizadas


para resolver problemas específicos do seu curso ou de projetos de pesquisa.

• Diferencial no Currículo: Python é uma habilidade cada vez mais valorizada


no mercado de trabalho em diversas áreas, não apenas para cargos de programador
“puro”.

Imagine, por exemplo, analisar milhares de leituras de um sensor coletadas em um


experimento com apenas algumas linhas de código usando Pandas e NumPy. Pense em
visualizar o comportamento de uma equação diferencial complexa de forma gráfica com
Matplotlib para entender melhor seus parâmetros. Ou, quem sabe, automatizar a geração
de relatórios a partir de dados brutos, poupando horas de trabalho manual. Esses são
apenas alguns cenários onde o Python pode se tornar uma ferramenta indispensável no
seu dia a dia.
Em resumo, Python não é apenas uma linguagem para aprender a programar; é uma
plataforma poderosa para resolver problemas reais, explorar dados, automatizar processos
e, em última análise, para criar e inovar. E a proposta deste livro é ‘descomplicar” esse
caminho para você.
6 Capítulo 1. Introdução ao Python e à Programação

1.3 Instalação do Python e Ambientes de Desenvolvi-


mento (IDEs)
Com uma compreensão inicial do que é o Python e por que ele é tão útil, é hora de
trazê-lo para o seu computador! Nesta seção, vamos guiá-lo pelo processo de instalação do
Python e apresentar algumas ferramentas que tornarão sua jornada de programação mais
agradável e produtiva: os Ambientes de Desenvolvimento Integrado (IDEs) e editores de
código.

O Coração da Execução: O Interpretador Python


Antes de instalar, é bom entender rapidamente o que é o “interpretador Python”. Lembra
que dissemos que Python é uma linguagem interpretada? Pois bem, o interpretador é o
programa que efetivamente lê o seu código Python (os arquivos com extensão .py que
você vai criar) e o executa, linha por linha, traduzindo suas instruções para algo que o
computador entenda. Portanto, o primeiro passo é instalar esse interpretador.

Instalando o Python no seu Computador


A fonte oficial e mais segura para baixar o Python é o site da Python Software
Foundation: [Link]. Recomendamos sempre baixar a versão estável mais recente
disponível (em meados de 2025, estamos geralmente falando de versões como Python 3.12,
3.13 ou superiores).
O processo de instalação varia um pouco dependendo do seu sistema operacional:

Instalando no Windows
Siga os passos a seguir para efetuar o download e instalação:
1. Acesse [Link]/downloads/. O site geralmente detecta que você está no Windows
e sugere o download do instalador mais recente. Clique no botão de download para a
versão estável do Python 3.x. Você baixará um arquivo .exe.
2. Execute o Instalador: Dê um duplo clique no arquivo .exe baixado para iniciar a
instalação.
3. Tela de Instalação Inicial (MUITO IMPORTANTE!):

• Marque a caixa que diz “Add Python X.Y to PATH” (onde X.Y é a versão
do Python, por exemplo, “Add Python 3.13 to PATH”). Esta opção é crucial! Ela
permite que você execute o Python e suas ferramentas (como o pip, que veremos
depois) diretamente do prompt de comando (CMD) ou PowerShell de qualquer pasta
do seu sistema. Se você não marcar isso, terá que configurar o PATH manualmente
depois, o que pode ser um pouco chato.

• Você pode escolher “Install Now” para uma instalação padrão, que geralmente é
adequada para a maioria dos usuários, ou “Customize installation” se quiser escolher
o local de instalação e outros recursos. Para iniciantes, “Install Now” (com a opção
do PATH marcada) é o mais recomendado.

4. Aguarde a Instalação: O processo é rápido. Ao final, você verá uma mensagem


de instalação bem-sucedida. Pode haver uma opção para “Disable path length limit”
1.3. Instalação do Python e Ambientes de Desenvolvimento (IDEs) 7

(Desabilitar limite de comprimento de caminho); se aparecer, é recomendável clicar nela,


pois pode evitar problemas com projetos em pastas com nomes longos. 5. Feche o
Instalador.

Instalando no macOS
1. Verifique se já tem Python: O macOS costumava vir com uma versão mais antiga do
Python (Python 2) pré-instalada. Para verificar, abra o Terminal (Applications > Utilities
> Terminal) e digite python –version ou python3 –version. Se for Python 2.x, ou se o
comando python3 não for encontrado, você precisará instalar a versão mais recente do
Python 3. 2. Acesse [Link]/downloads/. O site deve detectar que você está no
macOS e oferecer o download do instalador (.pkg) para a versão mais recente do Python 3.
3. Execute o Instalador: Abra o arquivo .pkg baixado. Siga as instruções do assistente de
instalação (clicar em “Continue”, “Agree” para a licença, “Install”). Você precisará digitar
sua senha de administrador. 4. Após a Instalação: O Python geralmente é instalado em
/usr/local/bin/python3. O instalador também deve adicionar os links necessários para
que você possa chamar o python3 do Terminal.

Instalando no Linux
Muitas distribuições Linux já vêm com Python 3 pré-instalado, ou ele está disponível
nos repositórios oficiais, tornando a instalação bem simples. 1. Verifique a Versão: Abra seu
terminal e digite python3 –version. Se você tiver uma versão recente do Python 3, pode
ser que não precise fazer nada. 2. Instalando/Atualizando (Exemplos para distribuições
comuns):

• Debian, Ubuntu e derivados (Mint, etc.):

sudo apt update


sudo apt install python3 python3-pip python3-venv

(O python3-pip instala o gerenciador de pacotes pip, e python3-venv é para


ambientes virtuais, algo útil que veremos mais adiante).

• Fedora:

sudo dnf install python3 python3-pip

• CentOS/RHEL (versões mais recentes podem usar dnf):

sudo yum install python3 python3-pip

Usar o gerenciador de pacotes da sua distribuição é geralmente a forma preferida para


manter o Python atualizado e integrado ao sistema.
8 Capítulo 1. Introdução ao Python e à Programação

Verificando a Instalação (em qualquer sistema)


Após a instalação, abra um novo terminal ou prompt de comando e digite os seguintes
comandos para verificar se tudo ocorreu bem:

• Para verificar a versão do Python:

python3 --version

(Em alguns sistemas, especialmente no Windows após adicionar ao PATH, apenas


python –version pode funcionar também para o Python 3). Você deverá ver a
versão que acabou de instalar (ex: Python 3.13.1).

• Para verificar a versão do pip (o instalador de pacotes do Python):

pip3 --version

(Ou apenas pip –version em alguns casos).

O pip é uma ferramenta essencial que você usará para instalar bibliotecas Python adicionais.
É importante que ele também esteja funcionando.
Se esses comandos mostrarem as versões corretas, parabéns! O Python está instalado
e pronto para ser usado.

Ferramentas para Escrever Código: IDEs e Editores


Embora você possa escrever código Python em qualquer editor de texto simples (como
o Bloco de Notas no Windows, TextEdit no Mac, ou Gedit/Nano no Linux) e executá-lo
pelo terminal, usar uma ferramenta mais especializada pode tornar sua vida muito mais
fácil. Essas ferramentas são chamadas de Ambientes de Desenvolvimento Integrado (IDEs)
ou editores de código avançados.
Eles oferecem recursos como:

• Destaque de sintaxe (Syntax Highlighting): Colore diferentes partes do seu


código (palavras-chave, comentários, strings) para facilitar a leitura.

• Autocompletar (Auto-completion): Sugere complementos para o código que


você está digitando, economizando tempo e reduzindo erros de digitação.

• Depurador (Debugger): Ajuda a encontrar e corrigir erros no seu código, permi-


tindo que você execute o programa passo a passo.

• Gerenciamento de projetos: Facilita a organização de arquivos em projetos


maiores.

Aqui estão algumas sugestões populares e “descomplicadas” para quem está começando
com Python:
1.3. Instalação do Python e Ambientes de Desenvolvimento (IDEs) 9

Thonny: O Amigo do Iniciante


O Thonny ([Link]) é um IDE Python projetado especificamente para iniciantes.

• Prós: Interface super simples e limpa, Python já vem embutido em algumas versões
(facilitando ainda mais o início), possui um depurador visual muito intuitivo que
mostra passo a passo como suas variáveis mudam e como o código é executado. É
leve e multiplataforma.

• Contras: Pode ser um pouco simples demais para projetos muito grandes ou
complexos, mas é perfeito para aprender.

• Como obter: Baixe diretamente do site [Link].

Visual Studio Code (VS Code): Poder e Versatilidade


O VS Code ([Link]) é um editor de código-fonte gratuito, extrema-
mente popular e poderoso, desenvolvido pela Microsoft.

• Prós: Leve, rápido, altamente personalizável com milhares de extensões. O suporte


para Python é excelente através da extensão oficial “Python” da Microsoft, que
oferece depuração, autocompletar inteligente (IntelliSense), linting (verificação de
erros de estilo), e integração com Jupyter Notebooks. É uma ótima ferramenta para
aprender e que continua sendo útil à medida que seus projetos crescem.

• Contras: Pode ter uma curva de aprendizado um pouco maior que o Thonny devido
à quantidade de recursos, mas a configuração básica para Python é bem direta.

• Como obter/configurar: Baixe do [Link]. Após instalar o


VS Code, abra-o, vá para a aba de Extensões (ícone de blocos no lado esquerdo) e
procure por “Python” (da Microsoft) e clique em “Install’. O VS Code pode pedir
para você selecionar o interpretador Python que você instalou – geralmente ele
detecta automaticamente.

Jupyter Notebooks / JupyterLab: Interatividade para Dados e Exploração


O Jupyter ([Link]) é uma aplicação web que permite criar e compartilhar
documentos que contêm código vivo, equações, visualizações e texto narrativo.

• Prós: Excelente para aprendizado interativo, prototipagem rápida, análise de


dados e visualização. Você escreve código em “células” que podem ser executadas
individualmente, e os resultados (texto, gráficos) aparecem logo abaixo. Muito usado
em cência de dados e pesquisa.

• Contras: Não é ideal para desenvolver aplicações complexas ou scripts longos que
precisam ser executados como um todo. A organização do projeto pode ser diferente
de IDEs tradicionais.

• Como obter: Se você instalou o Python com pip, pode instalar o Jupyter Notebook
ou JupyterLab (uma interface mais moderna) via terminal:
10 Capítulo 1. Introdução ao Python e à Programação

pip install notebook # Para o Jupyter Notebook clássico


# ou
pip install jupyterlab # Para o JupyterLab

Depois, para iniciar, você digita jupyter notebook ou jupyter lab no terminal.

Qual Escolher?
Para começar, especialmente seguindo este livro, Thonny é uma excelente primeira
escolha pela sua simplicidade. Se você já se sente um pouco mais aventureiro ou quer uma
ferramenta que te acompanhará por mais tempo e em projetos mais complexos, VS Code
com a extensão Python é altamente recomendado. O Jupyter é fantástico e você
certamente o encontrará se for para a área de análise de dados, mas para os fundamentos
da programação, Thonny ou VS Code podem ser mais diretos.
Não se preocupe em fazer a escolha “perfeita” agora. Você pode experimentar mais de
um e ver com qual se sente mais confortável! O importante é ter um local para escrever e
testar seu código Python.

IDEs Online: Programando na Nuvem sem Instalar Nada


Além das ferramentas que você instala no seu computador, existe uma categoria cada
vez mais popular de Ambientes de Desenvolvimento Integrado que rodam diretamente no
seu navegador de internet: as IDEs Online. Elas são uma ótima opção para começar
rapidamente, testar trechos de código ou até mesmo para colaboração em tempo real. A
principal vantagem é que você não precisa instalar nada – apenas um navegador e uma
conexão com a internet.
Aqui estão algumas opções populares e amigáveis para iniciantes:

• Google Colaboratory (Colab)

– O que é: O Colab ([Link]) é um ambiente de Jupyter


Notebook gratuito que roda inteiramente na nuvem e é oferecido pelo Google.
– Prós: Nenhuma configuração necessária, basta ter uma conta Google; interface
baseada em notebooks, excelente para aprendizado interativo; muitas bibliotecas
populares já vêm pré-instaladas; fácil de compartilhar; oferece acesso gratuito a
GPUs e TPUs.
– Contras: Focado em notebooks; requer conexão constante com a internet;
recursos gratuitos têm limites.

• Replit

– O que é: O Replit ([Link]) é uma plataforma online que oferece um


ambiente de desenvolvimento completo no navegador para dezenas de linguagens,
incluindo Python.
– Prós: Ambiente de projeto com editor, console e sistema de arquivos; per-
mite criar e rodar scripts .py; excelente para colaboração em tempo real;
gerenciamento de pacotes integrado; pode hospedar pequenos projetos web.
1.4. Seu primeiro programa em Python: Olá, Mundo! 11

– Contras: Versão gratuita tem limitações (poder computacional, Repls priva-


dos); requer conexão com a internet.

• Programiz Online Python Compiler

– O que é: O Compilador Python Online da Programiz ([Link]/


python-programming/online-compiler/) é um exemplo de muitas ferramen-
tas online super simples para rodar pequenos trechos de código Python.
– Prós: Extremamente fácil e rápido de usar para testes rápidos ou exercícios
simples, geralmente sem necessidade de login.
– Contras: Muito básico, não adequado para projetos com múltiplos arquivos
ou bibliotecas externas não padrão; sem recursos avançados; requer conexão
com a internet.

Outras plataformas como PythonAnywhere ([Link]) também oferecem


ambientes online, embora PythonAnywhere seja mais conhecido como uma plataforma de
hospedagem para aplicações Python.
Quando usar uma IDE Online?
• Para dar os primeiros passos sem se preocupar com instalação.

• Quando estiver usando um computador que não é seu ou que tem restrições de
instalação.

• Para compartilhar código rapidamente com colegas ou professores.

• Para testar pequenos exemplos ou ideias.


Embora as IDEs online sejam muito convenientes, para um desenvolvimento mais robusto
e para ter total controle sobre seu ambiente e pacotes, especialmente para projetos maiores,
ter uma instalação local do Python e um IDE como VS Code ou Thonny ainda é a prática
mais comum e recomendada a longo prazo.

1.4 Seu primeiro programa em Python: Olá, Mundo!


Chegou a hora de dar vida ao Python no seu computador! Escrever o primeiro programa
é um marco importante na jornada de qualquer programador. É o momento em que a
teoria começa a se transformar em algo tangível, algo que você criou e que o computador
executa. E a melhor parte? Em Python, fazer isso é incrivelmente simples.

Prepare seu Ambiente


Antes de começarmos, abra o ambiente de desenvolvimento que você escolheu na seção
anterior:
• Se optou pelo Thonny, abra-o.

• Se escolheu o VS Code, abra-o e crie um novo arquivo (Arquivo > Novo Arquivo ou
Ctrl+N / Cmd+N). Salve este arquivo com um nome terminado em .py, por exemplo,
meu_primeiro_programa.py. O VS Code deve reconhecer a extensão .py e ativar
os recursos para Python.
12 Capítulo 1. Introdução ao Python e à Programação

• Se estiver usando uma IDE Online (como Replit ou o compilador da Programiz),


abra a interface de programação Python dela.

O Tradicional Olá, Mundo!


Na programação, existe uma tradição antiga: o primeiro programa que se escreve em
uma nova linguagem é um que simplesmente exibe a mensagem “Olá, Mundo!” na tela. É
um teste básico para verificar se o ambiente está configurado corretamente e para ter um
primeiro contato com a sintaxe da linguagem.
Em Python, o programa “Olá, Mundo!” é elegantemente simples. Digite o seguinte
código no seu editor ou IDE:
1 print ( " Ola , Mundo ! " )

Código 1.3: Programa Ola, Mundo! em Python

Sim, é só isso! Uma única linha.

Entendendo o print()

• print(): Este é o nome de uma função embutida do Python. Uma função é um


bloco de código nomeado que realiza uma tarefa específica. A tarefa da função
print() é exibir dados na tela (no local que chamamos de “console” ou “terminal”
de saída).

• ("Ola, Mundo!"): O texto que você quer exibir, “Olá, Mundo!”, é passado como
um argumento para a função print(). Em Python, quando queremos representar
um texto literal, nós o colocamos entre aspas. Podem ser aspas duplas (") como
no exemplo, ou aspas simples (’). Esse tipo de dado textual é chamado de string
(ou “cadeia de caracteres”). Veremos strings em muito mais detalhe em um próximo
capítulo.

Executando Seu Primeiro Programa


A forma de executar o código varia um pouco conforme o ambiente:

• No Thonny: Clique no grande botão verde “Run current script” (Executar script
atual), que geralmente tem um símbolo de “play”, ou pressione a tecla F5.

• No VS Code (com um arquivo .py salvo): Você pode clicar com o botão
direito do mouse no editor onde está o código e escolher "Run Python File in
Terminal"(Executar Arquivo Python no Terminal). Ou, geralmente há um símbolo
de “play” no canto superior direito da janela do editor que executa o arquivo atual.

• Em IDEs Online (Replit, Programiz, etc.): Procure por um botão que diga
“Run”, “Execute” ou similar.

• Diretamente pelo Terminal/Prompt de Comando (se você salvou como


um arquivo .py):

1. Abra o terminal (Linux/macOS) ou o Prompt de Comando/PowerShell (Win-


dows).
1.4. Seu primeiro programa em Python: Olá, Mundo! 13

2. Navegue até a pasta onde você salvou o arquivo usando o comando cd (change
directory). Por exemplo, se salvou em Documentos\PythonScripts, digitaria
cd Documentos\PythonScripts.
3. Digite python3 nome_do_arquivo.py (ou python nome_do_arquivo.py se python
já executa o Python 3 no seu sistema) e pressione Enter. Por exemplo: python3
meu_primeiro_programa.py.

A Saída Esperada
Se tudo correu bem, você deverá ver a seguinte saída no console ou na área de resultados
do seu IDE:

Ola, Mundo!

Parabéns! Você acabou de escrever e executar seu primeiro programa em Python!

Indo um Pouco Além com print() e Comentários


A função print() é muito versátil. Veja mais alguns exemplos:

• Imprimindo números:
1 print (123)
2 print (3.14159)

Código 1.4: Imprimindo numeros

• Imprimindo o resultado de expressões matemáticas:


1 print (10 + 5) # Vai imprimir 15
2 print (20 * 2) # Vai imprimir 40

Código 1.5: Imprimindo resultados de expressoes

• Imprimindo múltiplos itens: Você pode passar vários argumentos para a função
print(), separados por vírgula. Por padrão, o print() colocará um espaço entre
eles na saída.
1 print ( " A resposta para a pergunta fundamental " , " é " , 42)
2 # Sa í da : A resposta para a pergunta fundamental é 42

Código 1.6: Imprimindo multiplos itens

Deixando Anotações: Comentários em Python


À medida que seus programas ficam mais complexos, é útil adicionar notas ou expli-
cações diretamente no código. Essas notas são chamadas de comentários. Em Python,
qualquer texto em uma linha que comece com o símbolo de cerquilha (#) é considerado
um comentário e é completamente ignorado pelo interpretador Python.
Comentários servem para:

• Explicar o que uma parte do codigo faz.

• Deixar lembretes para voce mesmo ou para outros programadores que possam ler
seu codigo.
14 Capítulo 1. Introdução ao Python e à Programação

• Desabilitar temporariamente uma linha de codigo sem apaga-la (chamado de “co-


mentar o codigo”).

Exemplo (comentários sem acentos, como combinado):


1 # Este programa demonstra o uso de comentarios
2 # e a funcao print () .
3
4 # Exibe uma sauda ç ã o personalizada
5 nome_usuario = " Alan " # Define o nome do usuario
6 print ( " Ol á ," , nome_usuario ) # Usa a v í rgula para separar os itens
no print
7
8 # A linha abaixo est á comentada , entao n ã o sera executada :
9 # print (" Esta linha n ã o aparece !")

Código 1.7: Exemplo de comentários em Python

Um Exemplo Prático: Cálculo da Área (Revisitado e Exibido!)


Vamos resgatar nosso exemplo conceitual do cálculo da área de um retângulo e, desta
vez, vamos calcular e exibir os resultados usando o que aprendemos.
Digite o seguinte código:
1 # Calculando e exibindo a á rea de um retangulo
2
3 # Defini ç ã o das vari á veis
4 base = 10 # Define a base do retangulo ( em unidades )
5 altura = 5 # Define a altura do retangulo ( em unidades )
6
7 # Calculo da á rea
8 area = base * altura
9
10 # Exibindo os resultados de forma detalhada
11 print ( " --- Calculo da Á rea do Retangulo ---" )
12 print ( " Valor da base : " , base )
13 print ( " Valor da altura : " , altura )
14 print ( " Á rea calculada : " , area )
15
16 # Uma forma mais elegante de exibir usando f - strings ( apenas uma
pr é via !)
17 print ( f " Um retangulo com base { base } e altura { altura } tem á rea
igual a { area }. " )

Código 1.8: Calculando e exibindo a área de um retangulo


Entendendo o Exemplo:

1. As primeiras linhas (com #) são comentários.

2. base = 10 e altura = 5: Aqui, estamos criando variáveis chamadas base e altura


e atribuindo valores a elas (10 e 5, respectivamente). Variáveis são como “caixinhas”
ou “etiquetas” que guardam dados na memória do computador. Veremos variáveis
em detalhes no próximo capítulo.

3. area = base * altura: Criamos uma variável area e guardamos nela o resultado
da multiplicação do valor de base pelo valor de altura.
1.5. Conceitos Básicos de Algoritmos e Lógica de Programação 15

4. As várias chamadas print(...) exibem textos descritivos e os valores das nossas


variáveis.

5. A última linha com print(f“...”) usa uma funcionalidade do Python chamada


f-string (string formatada). Ela permite embutir valores de variáveis diretamente
dentro de uma string de forma bem legível. Colocamos um f antes das aspas e as
variáveis entre chaves {}. Não se preocupe em dominar f-strings agora; é só para
você ver uma forma comum e útil de formatar saídas em Python.

Execute este programa e veja a saída!

Experimente! A Chave para Aprender


A melhor forma de aprender a programar é praticando. Não tenha medo de experi-
mentar!

• Mude os valores da base e altura no exemplo anterior e veja o resultado.

• Tente imprimir outras mensagens.

• Crie seus próprios pequenos cálculos e exiba os resultados.

Quanto mais você mexer no código e observar o que acontece, mais rápido você internalizará
os conceitos.

1.5 Conceitos Básicos de Algoritmos e Lógica de Pro-


gramação
Até agora, vimos o que é Python, por que ele é tão popular, como instalá-lo e até
escrevemos nossos primeiros pequenos programas. No entanto, programar vai muito
além de apenas aprender a sintaxe de uma linguagem. No cerne da programação está
a habilidade de resolver problemas, e para isso, precisamos entender os conceitos de
algoritmo e lógica de programação.
Para muitos de vocês, especialmente aqueles com alguma experiência prévia em disci-
plinas de introdução à computação (como as que envolvem a linguagem C), estes termos
já serão familiares. Portanto, esta seção servirá como uma revisão leve e um lembrete de
como esses conceitos fundamentais se aplicam universalmente, inclusive quando usamos
Python.

O que é um Algoritmo? A Receita para o Sucesso


Um algoritmo nada mais é do que uma sequência finita de passos bem definidos
e ordenados, projetada para realizar uma tarefa específica ou resolver um
problema. Pense nele como uma receita de bolo, um manual de instruções para montar
um móvel, ou até mesmo a sua rotina para se preparar para sair de casa pela manhã.

• Receita de Bolo: Lista os ingredientes (dados de entrada), as instruções de preparo


(processamento) em uma ordem específica, e o resultado esperado (o bolo pronto,
ou a saída). Se você pular um passo ou errar a ordem, o resultado pode não ser o
esperado!
16 Capítulo 1. Introdução ao Python e à Programação

• Manual de Montagem: Indica as peças, as ferramentas e a sequência exata de


encaixes e parafusamentos para que o móvel seja montado corretamente.

• Rotina Matinal: Acordar, escovar os dentes, tomar café, etc. Cada ação é um
passo, e a ordem geralmente importa.

Um bom algoritmo, para ser útil em computação, geralmente possui algumas características
importantes:

• Clareza e Ausência de Ambiguidade: Cada passo deve ser preciso e não deixar
margem para dúvidas.

• Finitude: Um algoritmo deve sempre terminar após um número finito de passos.

• Eficácia: Cada instrução deve ser básica o suficiente para ser realizada, em princípio,
de forma exata e em um tempo finito.

• Entradas e Saídas: Um algoritmo geralmente recebe dados de entrada (inputs) e


produz dados de saída (outputs) como resultado de seu processamento.

Lógica de Programação: Ensinando o Computador a “Pensar”


A lógica de programação é a maneira como organizamos nossos pensamentos e
as instruções de um algoritmo para que um computador possa “entender” e executar a
tarefa desejada. Não se trata de o computador pensar como um humano, mas sim de nós,
programadores, aprendermos a detalhar um problema em uma sequência de operações
lógicas e precisas que a máquina possa seguir.
Isso envolve:

• Sequenciamento: Definir a ordem correta em que as ações devem ocorrer.

• Tomada de Decisão (Condicionais): Permitir que o programa escolha entre


diferentes caminhos de execução com base em certas condições (ex: “Se a nota for
maior ou igual a 7, então o aluno está aprovado, senão, está reprovado”). Veremos
isso com o if, elif e else do Python.

• Repetição (Laços ou Loops): Permitir que um conjunto de instruções seja


executado múltiplas vezes, seja um número definido de vezes ou enquanto uma
condição for verdadeira (ex: “Para cada aluno na lista, calcule a média”). Veremos
isso com o for e while do Python.

Dominar a lógica de programação é mais importante do que decorar a sintaxe de uma


linguagem específica, pois a lógica é transferível entre diferentes linguagens.

Representando Algoritmos Antes de Codificar


Muitas vezes, antes de sair escrevendo código em Python (ou qualquer outra linguagem),
é útil esboçar o algoritmo de uma forma mais abstrata. Duas ferramentas comuns para
isso são o pseudocódigo e os fluxogramas.
1.5. Conceitos Básicos de Algoritmos e Lógica de Programação 17

Pseudocódigo: Escrevendo em “Programês”


O pseudocódigo é uma forma informal e de alto nível de descrever um algoritmo,
usando uma mistura de linguagem natural (como o português) com algumas convenções
e palavras-chave que lembram uma linguagem de programação, mas sem se prender
rigidamente à sintaxe de nenhuma delas. É uma ótima ferramenta para planejar a lógica.
Exemplo: Algoritmo para calcular a média de duas notas em pseudocódigo:

Algoritmo CalcularMediaDuasNotas

VARIÁVEIS
nota1: REAL
nota2: REAL
soma: REAL
media: REAL

INÍCIO
ESCREVER "Digite a primeira nota:"
LER nota1

ESCREVER "Digite a segunda nota:"


LER nota2

soma = nota1 + nota2


media = soma / 2

ESCREVER "A média das notas é: ", media


FIM

Este pseudocódigo é fácil de entender, mesmo para quem não conhece uma linguagem
de programação específica. Ele descreve claramente os passos: pedir as notas, calcular a
soma, calcular a média e mostrar o resultado.

Fluxogramas: Desenhando a Lógica


Um fluxograma é uma representação gráfica de um algoritmo. Ele utiliza símbolos
padronizados para representar os diferentes tipos de instruções (início/fim, entrada/saída
de dados, processamento, decisão) e setas para indicar o fluxo de execução.
Para problemas simples, um fluxograma pode parecer exagero, mas para algoritmos
mais complexos, ele pode ser uma ferramenta visual poderosa para entender e comunicar
a lógica, especialmente em áreas da engenharia onde diagramas são comuns.

• Símbolos Comuns:

– Oval: Início ou Fim do algoritmo.


– Retângulo: Uma instrução ou processo (ex: soma = nota1 + nota2).
– Paralelogramo: Entrada de dados (ex: LER nota1) ou Saída de dados (ex:
ESCREVER media).
– Losango: Decisão (condição SE, que leva a caminhos diferentes).
18 Capítulo 1. Introdução ao Python e à Programação

Desenhar um fluxograma ou escrever um pseudocódigo antes de começar a programar em


Python pode economizar muito tempo e ajudar a evitar erros de lógica.
A Figura 1.1 ilustra alguns dos símbolos mais comuns utilizados na construção de
fluxogramas e um exemplo simples de como eles podem ser conectados para representar
um algoritmo.

Figura 1.1: Exemplo de fluxograma demonstrando símbolos comuns: início/fim (oval),


entrada/saída de dados (paralelogramo), processo (retângulo) e decisão (losango).

Traduzindo um Algoritmo Simples para Python


Vamos pegar nosso algoritmo de cálculo da média e ver como ele se traduziria para
Python. Por enquanto, para simplificar, em vez de “LER” as notas do usuário (o que
1.5. Conceitos Básicos de Algoritmos e Lógica de Programação 19

faremos com a função input() em um capítulo futuro), vamos atribuir valores diretamente
às variáveis.
Pseudocódigo:

Algoritmo CalcularMediaDuasNotas
VARIÁVEIS nota1, nota2, soma, media: REAL
INÍCIO
LER nota1
LER nota2
soma = nota1 + nota2
media = soma / 2
ESCREVER media
FIM

Equivalente em Python:
1 # Algoritmo para calcular a m é dia de duas notas
2 # ( Vers ã o Python )
3

4 # No pseudoc ó digo , ter í amos " LER nota1 " e " LER nota2 ".
5 # Por enquanto , vamos simular isso atribuindo valores diretamente .
6 # Aprenderemos a pegar dados do usu á rio com input () mais tarde .
7 nota1 = 7.5
8 nota2 = 8.0
9

10 print ( f " A primeira nota é : { nota1 } " )


11 print ( f " A segunda nota é : { nota2 } " )
12
13 # No pseudoc ó digo : soma = nota1 + nota2
14 soma_das_notas = nota1 + nota2
15 print ( f " A soma das notas é : { soma_das_notas } " )
16
17 # No pseudoc ó digo : media = soma / 2
18 media_final = soma_das_notas / 2
19
20 # No pseudoc ó digo : ESCREVER media
21 print ( f " A m é dia final das notas é : { media_final } " )

Código 1.9: Algoritmo da media em Python


Observe como a sequência de passos e a lógica são diretamente traduzidas. O Python,
com sua sintaxe clara, torna essa tradução bastante natural.

Concluindo o Início da Jornada


Compreender e saber como criar algoritmos e aplicar a lógica de programação são as
verdadeiras habilidades de um programador. A linguagem de programação, como o Python,
é a ferramenta que usamos para expressar essa lógica de uma forma que o computador
possa executar.
Nos próximos capítulos, vamos mergulhar mais fundo nas ferramentas que o Python
nos oferece (tipos de dados, estruturas de controle, funções, etc.) para que você possa
construir algoritmos cada vez mais interessantes e úteis. Lembre-se sempre de pensar no
problema e na sequência de passos para resolvê-lo antes de começar a digitar seu código!
Capítulo 2

Sintaxe Básica e Tipos de Dados

No Capítulo 1, tivemos uma visão geral do Python, sua filosofia, como instalar o
ambiente e até escrevemos nossos primeiros programas simples. Agora, vamos mergulhar
um pouco mais fundo nos blocos de construção fundamentais de qualquer programa Python:
a sintaxe básica para criar instruções e os diferentes tipos de dados que podemos manipular.
Dominar esses conceitos é essencial para escrever programas mais complexos e úteis.

2.1 Variáveis, Nomes e Atribuição


Imagine que você está cozinhando e precisa guardar diferentes ingredientes em potes
para usá-los depois. Na programação, as variáveis funcionam de maneira similar: são
como "potes"ou "caixas"na memória do computador onde guardamos informações (dados)
que nossos programas vão utilizar. Cada "pote"tem um nome para que possamos nos
referir a ele facilmente.

O que é uma Variável?


Uma variável é um nome simbólico associado a um valor. Ela serve como uma referência
para um local na memória do computador onde um dado está armazenado. Ao usar o
nome da variável, podemos acessar ou modificar o dado guardado.

Nomeando suas Variáveis em Python


Escolher bons nomes para suas variáveis é crucial para escrever um código legível e
fácil de entender (lembre-se do “Legibilidade conta.” do Zen de Python!). Aqui estão
algumas regras e convenções para nomear variáveis em Python:

• Regras (Obrigatórias):

– Nomes de variáveis podem conter letras (maiúsculas e minúsculas, a-z, A-Z),


números (0-9) e o caractere underscore (_).
– Eles não podem começar com um número. (Ex: 1numero é inválido, mas
numero1 é válido).
– Eles não podem ser iguais a nenhuma das palavras reservadas do Python
(palavras que têm significado especial para a linguagem, como if, for, while,
etc. – veremos isso na próxima seção).

20
2.1. Variáveis, Nomes e Atribuição 21

• Convenções (Fortemente Recomendadas):

– snake_case: Para nomes de variáveis compostos por múltiplas palavras, a


convenção mais comum em Python é usar letras minúsculas separadas por
underscores.
∗ Exemplos: idade_do_aluno, velocidade_inicial, nome_completo,
valor_total_compra.
– Significativos: Escolha nomes que descrevam o dado que a variável arma-
zena. x = 10 funciona, mas preco_unitario = 10 é muito mais claro se 10
representa um preço.
– Case-Sensitive: Python diferencia letras maiúsculas de minúsculas. Isso
significa que idade, Idade e IDADE são três variáveis diferentes. A convenção é
usar minúsculas para variáveis.
– Evite usar caracteres como l (L minúsculo), O (O maiúsculo) ou I (i maiúsculo)
sozinhos como nomes de variáveis, pois podem ser confundidos com os números
1 e 0.

Atribuindo Valores: O Operador =


Para guardar um valor em uma variável, usamos o operador de atribuição, que em
Python é o sinal de igual (=). A sintaxe é: nome_da_variavel = valor
O valor à direita do = é calculado ou obtido, e então armazenado na variável nomeada
à esquerda.
1 # Exemplos de atribui ç ã o de vari á veis
2 idade = 30
3 nome = " Maria Silva " # Texto ( string ) é colocado entre aspas
4 altura_metros = 1.75
5 salario_mensal = 4500.50
6 aluno_aprovado = True # Valor booleano ( Verdadeiro ou Falso )

Código 2.1: Exemplos de atribuicao de variaveis


No exemplo acima:

• A variável idade recebe o valor inteiro 30.

• A variável nome recebe o texto (string) "Maria Silva".

• A variável altura_metros recebe o valor de ponto flutuante (decimal) 1.75.

• A variável salario_mensal recebe o valor 4500.50.

• A variável aluno_aprovado recebe o valor booleano True.

Você pode mudar o valor de uma variável simplesmente atribuindo um novo valor a ela:
1 preco = 100
2 print ( preco ) # Sa í da : 100
3 preco = 120 # Reatribuindo um novo valor à vari á vel preco
4 print ( preco ) # Sa í da : 120

Código 2.2: Reatribuindo valor a uma variavel


22 Capítulo 2. Sintaxe Básica e Tipos de Dados

Atribuição Múltipla
Python permite atribuir valores a múltiplas variáveis na mesma linha, o que pode ser
bem conveniente:
1 # Atribuindo valores diferentes a vari á veis diferentes
2 x , y , z = 10 , 20 , " ol á "
3 print ( x ) # Sa í da : 10
4 print ( y ) # Sa í da : 20
5 print ( z ) # Sa í da : ol á
6
7 # Atribuindo o mesmo valor a m ú ltiplas vari á veis
8 a = b = c = 100
9 print ( a ) # Sa í da : 100
10 print ( b ) # Sa í da : 100
11 print ( c ) # Sa í da : 100

Código 2.3: Exemplos de atribuicao multipla

Tipagem Dinâmica: Python Descobre o Tipo!


Uma característica importante do Python é a tipagem dinâmica. Isso significa que
você não precisa declarar explicitamente o tipo de dado que uma variável vai armazenar
(como em linguagens como C ou Java, onde você diria int idade; ou String nome;).
Em Python, o tipo da variável é determinado automaticamente pelo valor que você
atribui a ela.
1 minha_variavel = 10 # Python sabe que minha_variavel é do tipo
inteiro ( int )
2 print ( type ( minha_variavel ) ) # A fun ç ã o type () nos diz o tipo de um
dado
3
4 minha_variavel = " Python " # Agora , minha_variavel guarda uma string
e seu tipo muda para string ( str )
5 print ( type ( minha_variavel ) )
6
7 minha_variavel = 3.14 # E agora , é um float
8 print ( type ( minha_variavel ) )

Código 2.4: Demonstracao de tipagem dinamica

Essa flexibilidade torna o código Python mais conciso e rápido de escrever. No entanto, é
importante ter em mente qual tipo de dado suas variáveis contêm para evitar erros em
operações (por exemplo, tentar somar um número com um texto diretamente geralmente
causa um erro). Veremos os tipos de dados em detalhe logo em seguida.

Exemplos Práticos com Variáveis


Vamos ver como as variáveis são usadas em um contexto prático simples, como calcular
o perímetro e a área de um retângulo:
1 # Definindo as dimens õ es de um campo de futebol ( em metros )
2 comprimen to_campo = 105.0 # Usamos .0 para indicar que é um n ú mero
decimal ( float )
3 largura_campo = 68.0
4
2.2. Palavras Reservadas (Keywords) 23

5 # Calculando o per í metro


6 # Per í metro = 2 * ( comprimento + largura )
7 perimetro_campo = 2 * ( comp rimento_ campo + largura_campo )
8
9 # Calculando a á rea
10 # Á rea = comprimento * largura
11 area_campo = comprim ento_cam po * largura_campo
12
13 # Exibindo os resultados
14 print ( " Dados do Campo de Futebol : " )
15 print ( " Comprimento : " , comprimento_campo , " metros " )
16 print ( " Largura : " , largura_campo , " metros " )
17 print ( " Per í metro : " , perimetro_campo , " metros " )
18 print ( f " Á rea do campo : { area_campo } metros quadrados . " ) # Usando
f - string para uma sa í da formatada

Código 2.5: Calculando perimetro e area de um campo

Neste exemplo, comprimento_campo, largura_campo, perimetro_campo e area_campo


são variáveis que armazenam os dados e os resultados dos nossos cálculos, tornando o
programa muito mais legível e fácil de modificar do que se usássemos apenas os números
diretamente nas fórmulas. Se quiséssemos calcular para um campo de dimensões diferentes,
bastaria alterar os valores nas duas primeiras atribuições.

2.2 Palavras Reservadas (Keywords)


Na seção anterior, aprendemos como nomear nossas variáveis seguindo algumas regras e
convenções. No entanto, existe um conjunto especial de palavras na linguagem Python que
já possuem um significado e uma função predefinidos. Essas são as palavras reservadas
(ou keywords, em inglês), e você não pode usá-las como nomes para suas variáveis, funções,
classes ou qualquer outro identificador que você criar.

O que são e por que existem?


Pense nas palavras reservadas como os "verbos", "conjunções"e "substantivos especi-
ais"da gramática do Python. São elas que definem a estrutura das suas instruções e como
o interpretador Python entende o que você quer que ele faça. Por exemplo, a palavra if
sinaliza o início de uma estrutura de decisão, for indica um laço de repetição, e def é
usada para definir uma função.
Se você tentasse usar uma palavra reservada como nome de variável, o Python ficaria
confuso, pois ele esperaria que essa palavra tivesse seu significado especial. Por exemplo,
se você tentasse if = 10, o Python não saberia se você está tentando criar uma variável
chamada if ou iniciar um comando if. Para evitar essa ambiguidade, o uso delas como
identificadores é proibido e resultará em um erro de sintaxe.

A Lista de Palavras Reservadas do Python


Felizmente, a lista de palavras reservadas do Python é relativamente pequena, especi-
almente quando comparada a outras linguagens de programação. Isso contribui para a
simplicidade da linguagem.
24 Capítulo 2. Sintaxe Básica e Tipos de Dados

Aqui estão as palavras reservadas em Python (a lista pode ter pequenas variações entre
versões muito distantes do Python, mas estas são as fundamentais e atuais para o Python
3):

False None True and as assert async await break class


continue def del elif else except finally for from
global if import in is lambda nonlocal not or pass
raise return try while with yield

Não se preocupe em memorizar todas elas de uma vez! Você se familiarizará com
a maioria delas naturalmente à medida que avançamos no aprendizado da linguagem
e começamos a usá-las em seus contextos apropriados. Muitos editores de código e
IDEs também destacam as palavras reservadas com uma cor diferente, o que ajuda a
identificá-las.

Como Consultar as Palavras Reservadas no Próprio Python


Se você quiser ver a lista atual de palavras reservadas diretamente do seu ambiente
Python, pode usar o módulo keyword:
1 # Importa o m ó dulo keyword
2 import keyword
3
4 # Imprime a lista de todas as palavras reservadas
5 print ( keyword . kwlist )

Código 2.6: Consultando as palavras reservadas do Python


Ao executar este pequeno script, a saída será a lista de palavras que mostramos acima.

Exemplos do que NÃO fazer:


Tentar usar uma palavra reservada como nome de variável resultará em um erro
(SyntaxError):
1 # Tentativas inv á lidas de usar palavras reservadas como nomes de
vari á veis
2 # Estes c ó digos GERAR Ã O ERRO !
3
4 if = 10 # Erro ! ’ if ’ é uma palavra reservada para condi ç õ es
5 from = " Brasil " # Erro ! ’ from ’ é usada com ’ import ’
6 class = MinhaTurma () # Erro ! ’ class ’ é usada para definir classes

Código 2.7: Uso invalido de palavras reservadas

Palavras Reservadas vs. Funções e Tipos Embutidos


É importante fazer uma pequena distinção aqui. Palavras como print, len, input,
str, int, float, etc., são nomes de funções embutidas ou tipos de dados embutidos
do Python. Elas são incrivelmente úteis e estão sempre disponíveis para você.
Diferentemente das palavras reservadas, tecnicamente, você poderia redefinir esses
nomes (ou seja, criar uma variável chamada print, por exemplo: print = "meu texto").
No entanto, fazer isso é uma PÉSSIMA IDEIA! Se você fizer print = "meu texto",
2.3. Tipos de Dados Primitivos 25

você perderá o acesso à funcionalidade original da função print() naquela parte do seu
programa, o que quase certamente levará a comportamentos inesperados e erros difíceis de
rastrear.
A regra de ouro é: evite usar nomes de funções ou tipos embutidos já existentes
como nomes para suas próprias variáveis. Embora o Python permita (ao contrário das
palavras reservadas, que geram erro imediato), é uma prática que prejudica a legibilidade
e pode quebrar a funcionalidade esperada.

Concluindo
As palavras reservadas são a espinha dorsal da sintaxe do Python. Conhecê-las e, mais
importante, respeitá-las ao nomear seus próprios identificadores é um passo fundamental
para escrever código Python válido e funcional. Com o tempo e a prática, elas se tornarão
parte natural do seu vocabulário de programação.

2.3 Tipos de Dados Primitivos


No mundo da programação, os dados que manipulamos vêm em diferentes "formatos"ou
tipos. Um número é diferente de um texto, que por sua vez é diferente de um valor
lógico como "verdadeiro"ou "falso". O Python, como qualquer linguagem de programação,
possui um conjunto de tipos de dados embutidos para representar essas diversas formas de
informação. Nesta seção, vamos explorar os mais básicos e fundamentais, frequentemente
chamados de tipos de dados primitivos.
Entender os tipos de dados é crucial porque o tipo de um dado define quais operações
podemos realizar com ele. Por exemplo, faz sentido somar dois números, mas não faria
sentido (pelo menos não diretamente) "somar"um nome de pessoa com uma temperatura.
Lembre-se da função type() que vimos brevemente na Seção 2.1: ela é nossa amiga
para descobrir o tipo de qualquer variável ou valor. Por exemplo, type(10) nos dirá que
10 é de um tipo numérico específico.

2.3.1 Tipos Numéricos


Python oferece um suporte robusto para trabalhar com números, dividindo-os princi-
palmente em três tipos: inteiros, de ponto flutuante (decimais) e complexos.

Inteiros (int)
Os inteiros representam números completos, sem casas decimais, podendo ser positivos,
negativos ou zero. São usados para contagens, índices, ou qualquer quantidade que não
envolva frações.
1 # Exemplos de n ú meros inteiros ( int )
2 idade_aluno = 21
3 ano_atual = 2025
4 numero_de_erros = 0
5 saldo_negativo = -150
6 t e m p e r a t u r a _ a m b i e n t e = 23 # Graus Celsius
7

8 print ( type ( idade_aluno ) ) # Sa í da : < class ’ int ’>

Código 2.8: Exemplos de numeros inteiros (int)


26 Capítulo 2. Sintaxe Básica e Tipos de Dados

Uma grande vantagem do Python é que os inteiros podem ter um tamanho arbitrariamente
grande, limitados apenas pela memória disponível no seu computador. Você não precisa
se preocupar com um valor máximo como em algumas outras linguagens.

Números de Ponto Flutuante (float)


Os números de ponto flutuante, ou simplesmente "floats", são usados para representar
números reais, ou seja, aqueles que podem ter casas decimais. São ideais para representar
medidas, valores monetários, resultados de divisões, etc.
1 # Exemplos de n ú meros de ponto flutuante ( float )
2 a l t u r a _ p e s s o a _ m e t r o s = 1.78
3 pi_aproximado = 3.14159
4 preco_do_produto = 49.99
5 a c e l e r a c a o _ d a _ g r a v i d a d e = 9.81
6
7 print ( type ( preco_do_produto ) ) # Sa í da : < class ’ float ’>

Código 2.9: Exemplos de numeros de ponto flutuante (float)


Você também pode usar a notação científica (e) para representar floats muito grandes ou
muito pequenos:
1 v e l o c i d a d e _ d a _ l u z _ m s = 3.0 e8 # Equivalente a 3.0 * 10^8 , ou
300 ,000 ,000.0
2 c a r g a _ d o _ e l e t r o n _ c o u l o m b = 1.602 e -19
3

4 print ( v e l o c i d a d e _ d a _l u z _ m s )
5 print ( c a r g a _ d o _ e l e t r o n _ c o u l o m b )

Código 2.10: Notacao cientifica para floats


É bom saber: números de ponto flutuante em computadores têm uma precisão finita. Isso
significa que, para cálculos muito sensíveis, podem ocorrer pequenos arredondamentos.
Para a maioria das aplicações do dia a dia e muitos cálculos de engenharia, a precisão
padrão é mais do que suficiente, mas é um detalhe a se ter em mente em contextos
financeiros ou científicos de altíssima precisão.

Números Complexos (complex)


Para aqueles em áreas como engenharia elétrica, física ou matemática avançada, Python
também oferece suporte nativo a números complexos. Eles são representados na forma
a + bj (onde a é a parte real e b é a parte imaginária, e j (ou J) representa a unidade
imaginária (a raiz quadrada de -1)).
1 # Exemplos de n ú meros complexos ( complex )
2 numero_co mplexo_1 = 2 + 5 j
3 numero_co mplexo_2 = -3.5 j # Parte real é 0
4 numero_co mplexo_3 = complex (4 , -2) # Outra forma de criar :
complex ( real , imaginario )
5
6 print ( type ( nume ro_comple xo_1 ) ) # Sa í da : < class ’ complex ’>
7 print ( num ero_comp lexo_1 ) # Sa í da : (2+5 j )

Código 2.11: Exemplos de numeros complexos (complex)


Você pode acessar as partes real e imaginária de um número complexo usando os atributos
.real e .imag:
2.3. Tipos de Dados Primitivos 27

1 z = 7 - 4j
2 print ( f " N ú mero complexo z : { z } " )
3 print ( f " Parte real de z : { z . real } " ) # Sa í da : 7.0
4 print ( f " Parte imagin á ria de z : { z . imag } " ) # Sa í da : -4.0

Código 2.12: Acessando partes de um numero complexo

Todas as operações aritméticas básicas (soma, subtração, multiplicação, divisão) funcionam


como esperado com números complexos.

2.3.2 Tipo Booleano (bool)


O tipo booleano é um dos mais simples, mas fundamental na programação. Ele
representa apenas dois valores possíveis: Verdadeiro ou Falso. Em Python, esses valores
são escritos exatamente como True e False (com a primeira letra maiúscula).
Booleanos são o resultado de expressões de comparação (que veremos no próximo
capítulo) e são a base para as estruturas de controle condicional (Capítulo 4), que permitem
que nossos programas tomem decisões.
1 # Exemplos de valores booleanos ( bool )
2 a l un o_e s ta _ a pr o va d o = True
3 s i s t e m a _ o p e r a c i o n a l _ a t i v o = False
4 t e m _ p e r m i s s a o _ p a r a _ a c e s s a r = True
5
6 print ( type ( al u no _ e st a _a p ro v a do ) ) # Sa í da : < class ’ bool ’>
7 print ( s i s t e m a _ o p e r a c i o n a l _ a t i v o ) # Sa í da : False
8
9 # Exemplo de booleano resultante de uma compara ç ã o
10 idade = 20
11 maior_de_idade = ( idade >= 18) # A express ã o ( idade >= 18) resulta
em True ou False
12 print ( f " A pessoa é maior de idade ? { maior_de_idade } " ) # Sa í da : A
pessoa é maior de idade ? True

Código 2.13: Exemplos de valores booleanos (bool)

2.3.3 Strings (str)


Já encontramos as strings brevemente quando usamos a função print(). Strings são
sequências de caracteres usadas para representar dados textuais, como nomes, frases,
mensagens, ou qualquer outra informação em formato de texto.

Delimitando Strings
Em Python, você pode delimitar strings usando:

• Aspas simples (’): minha_string1 = ’Olá, Python!’

• Aspas duplas ("): minha_string2 = "Python é divertido!" A escolha entre


aspas simples ou duplas é geralmente uma questão de preferência ou conveniência
(por exemplo, se a sua string contém uma aspa simples, você pode delimitá-la com
aspas duplas, e vice-versa: frase = "Ele disse: ’Oi!’").
28 Capítulo 2. Sintaxe Básica e Tipos de Dados

• Aspas triplas (simples ”’ ou duplas "): São usadas para criar strings que ocupam
múltiplas linhas. Elas preservam as quebras de linha e os espaços exatamente como
você os digitou.
1 poema = ’ ’ ’ Este é um poema
2 que ocupa v á rias linhas
3 e mant é m a indenta ç ã o . ’ ’ ’
4

5 descricao_longa = " " " Este tipo de string tamb é m é muito


usado
6 para escrever " docstrings " , que s ã o strings de documenta ç ã o
7 para fun ç õ es e classes ( veremos isso mais tarde ) . " " "
8
9 print ( poema )
10 print ( descricao_longa )

Código 2.14: Strings multilinhas com aspas triplas

Caracteres de Escape
Às vezes, você precisa incluir caracteres especiais dentro de uma string, como uma
nova linha ou uma tabulação. Para isso, usamos sequências de escape, que começam com
uma barra invertida (\):
• \n: Nova linha (quebra de linha)

• \t: Tabulação horizontal

• \\: Insere uma barra invertida literal

• \’: Insere uma aspa simples literal (útil se você delimitou a string com aspas simples)

• \": Insere uma aspa dupla literal (útil se você delimitou a string com aspas duplas)

1 print ( " Primeira linha .\\ nSegunda linha . " )


2 print ( " Nome :\\ tAlan " ) # \ t para tabula ç ã o
3 print ( " O caminho do arquivo é :
C :\\\\ arquivos \\\\ meu_documento . txt " ) # \\ para barra literal
4 print ( ’ Ele disse : \\ ’ Python é legal !\\ ’. ’) # \ ’ para aspa simples
literal

Código 2.15: Exemplos de caracteres de escape

Strings Vazias
Uma string que não contém nenhum caractere é chamada de string vazia. Ela é
representada por duas aspas (simples ou duplas) sem nada entre elas.

Operações Básicas com Strings (Introdução)


Vamos ver duas operações muito comuns com strings agora; outras mais avançadas
virão depois.
• Concatenação (+): Usamos o operador + para juntar (concatenar) duas ou mais
strings.
2.3. Tipos de Dados Primitivos 29

1 primeiro_nome = " Ada "


2 sobrenome = " Lovelace "
3 nome_completo = primeiro_nome + " " + sobrenome #
Adicionamos um espa ç o entre eles
4 print ( nome_completo ) # Sa í da : Ada Lovelace

Código 2.16: Concatenacao de strings

• Repetição (*): Usamos o operador * para repetir uma string um certo número de
vezes.
1 eco = " eco ... " * 3
2 print ( eco ) # Sa í da : eco ... eco ... eco ...

Código 2.17: Repeticao de strings

Tamanho de uma String: A Função len()


Para saber quantos caracteres uma string possui (incluindo espaços e símbolos), usamos
a função embutida len():
1 linguagem = " Python "
2 tamanho = len ( linguagem )
3 print ( f " A palavra ’{ linguagem } ’ tem { tamanho } caracteres . " ) #
Sa í da : A palavra ’ Python ’ tem 6 caracteres .
4
5 frase = " Ol á , Mundo ! "
6 print ( f " A frase ’{ frase } ’ tem { len ( frase ) } caracteres . " ) # Sa í da : A
frase ’ Ol á , Mundo ! ’ tem 11 caracteres .

Código 2.18: Usando len() com strings

2.3.4 Verificando o Tipo de um Dado com type()


Como mencionamos no início desta seção e no contexto da tipagem dinâmica, a função
type() é sua aliada para descobrir ou confirmar o tipo de uma variável ou de um valor
literal em Python.
1 # Usando type () para verificar os tipos
2 numero_inteiro = 100
3 numero_decimal = 25.75
4 texto_exemplo = " Aprendendo Python "
5 valor_logico = False
6 numero_complexo = 1 + 2 j
7
8 print ( f " O valor { numero_inteiro } é do tipo : { type ( numero_inteiro ) } " )
9 print ( f " O valor { numero_decimal } é do tipo : { type ( numero_decimal ) } " )
10 print ( f " O texto ’{ texto_exemplo } ’ é do tipo : { type ( texto_exemplo ) } " )
11 print ( f " O valor { valor_logico } é do tipo : { type ( valor_logico ) } " )
12 print ( f " O valor { numero_complexo } é do tipo :
{ type ( numero_complexo ) } " )

Código 2.19: Verificando tipos com type()


30 Capítulo 2. Sintaxe Básica e Tipos de Dados

A saída mostrará algo como <class ’int’>, <class ’float’>, <class ’str’>, <class
’bool’> e <class ’complex’>, respectivamente.
Entender esses tipos de dados primitivos é o primeiro passo para manipular informações
de forma eficaz em seus programas Python.

2.4 Saída de Dados Formatada: Mais sobre print() e


F-strings
Já utilizamos a função print() diversas vezes para exibir mensagens e os valores
de nossas variáveis no console. Ela é uma das ferramentas mais fundamentais para
entendermos o que nosso programa está fazendo e para comunicarmos resultados ao
usuário. Nesta seção, vamos explorar um pouco mais a fundo a função print() e aprender
a técnica mais moderna e recomendada para formatar nossas saídas de forma elegante e
legível: as f-strings.

Revisando o Básico da Função print()


Como vimos, a função print() pode receber um ou mais argumentos e os exibe no
console. Por padrão, ela adiciona um espaço entre cada argumento e uma quebra de linha
ao final de sua execução.
1 nome = " Alice "
2 idade = 30
3 print ( " Nome : " , nome , " Idade : " , idade )
4 # Sa í da : Nome : Alice Idade : 30
5 # ( e o cursor vai para a pr ó xima linha )

Código 2.20: Uso basico de print() com multiplos argumentos

Controlando o Separador e o Final da Linha


A função print() possui dois parâmetros opcionais muito úteis que nos permitem
controlar melhor a saída: sep (separador) e end (finalizador).

• sep (Separador): Por padrão, quando você passa múltiplos argumentos para
print(), eles são separados por um espaço. O parâmetro sep permite que você
especifique um caractere (ou uma string) diferente para ser usado como separador.
1 dia = 15
2 mes = 10
3 ano = 2025
4
5 # Usando o separador padr ã o ( espa ç o )
6 print ( dia , mes , ano ) # Sa í da : 15 10 2025
7

8 # Usando ’/ ’ como separador


9 print ( dia , mes , ano , sep = " / " ) # Sa í da : 15/10/2025
10
11 # Usando ’ - ’ como separador
12 print ( dia , mes , ano , sep = " - " ) # Sa í da : 15 - 10 - 2025

Código 2.21: Usando o parametro sep no print()


2.4. Saída de Dados Formatada: Mais sobre print() e F-strings 31

• end (Finalizador): Por padrão, após exibir todos os argumentos, print() adiciona
um caractere de nova linha (\n), fazendo com que a próxima saída de print()
comece em uma nova linha. O parâmetro end permite que você especifique o que
deve ser impresso ao final.
1 print ( " Ol á " , end = " " ) # N ã o adiciona nova linha , o pr ó ximo
print continua na mesma
2 print ( " , Mundo ! " ) # Sa í da : Ol á , Mundo ! ( tudo na mesma
linha )
3
4 print ( " Item 1 " , end = " | " )
5 print ( " Item 2 " , end = " | " )
6 print ( " Item 3 " ) # Este ú ltimo adicionar á a nova linha
padr ã o
7 # Sa í da : Item 1 | Item 2 | Item 3

Código 2.22: Usando o parametro end no print()

Você pode usar end= para terminar com um espaço em vez de uma nova linha, ou
end= para não adicionar nada.

A Potência das F-strings (Strings Formatadas Literais)


Embora concatenar strings com + ou usar múltiplos argumentos no print() funcione,
muitas vezes queremos embutir valores de variáveis diretamente dentro de uma string de
uma forma mais legível e poderosa. É aqui que as f-strings (introduzidas no Python 3.6)
entram em cena. Elas são consideradas a maneira mais moderna, concisa e eficiente de
formatar strings em Python.
Para criar uma f-string, basta colocar a letra f (ou F) imediatamente antes da aspa
de abertura da string. Dentro da string, você pode colocar variáveis ou expressões entre
chaves {} e seus valores serão automaticamente inseridos naquele ponto.
1 # Usando f - strings
2 nome_aluno = " Carlos "
3 nota_final = 8.75
4 disciplina = " C á lculo I "
5

6 # Exemplo simples
7 mensagem = f " O aluno { nome_aluno } tirou nota { nota_final } em
{ disciplina }. "
8 print ( mensagem )
9 # Sa í da : O aluno Carlos tirou nota 8.75 em C á lculo I .
10

11 # Voc ê tamb é m pode colocar express õ es diretamente nas chaves


12 idade_aluno = 20
13 print ( f " No pr ó ximo ano , { nome_aluno } ter á { idade_aluno + 1} anos . " )
14 # Sa í da : No pr ó ximo ano , Carlos ter á 21 anos .

Código 2.23: Introducao as f-strings

Formatando Valores Dentro de F-strings


As f-strings não servem apenas para inserir valores, mas também para formatá-los. Isso
é especialmente útil para números de ponto flutuante, onde queremos controlar o número
de casas decimais, ou para alinhar texto.
32 Capítulo 2. Sintaxe Básica e Tipos de Dados

Para aplicar formatação, você usa dois pontos (:) após o nome da variável (ou expressão)
dentro das chaves, seguido por um especificador de formato.

• Formatando Floats (Número de Casas Decimais): Use :.Nf onde N é o número


de casas decimais desejado.
1 valor_pi = 3.1415926535
2 preco = 45.0
3 desconto = 0.1567
4
5 print ( f " Valor de Pi (2 casas ) : { valor_pi :.2 f } " ) #
Sa í da : Valor de Pi (2 casas ) : 3.14
6 print ( f " Valor de Pi (4 casas ) : { valor_pi :.4 f } " ) #
Sa í da : Valor de Pi (4 casas ) : 3.1416 ( arredonda !)
7 print ( f " Pre ç o : R$ { preco :.2 f } " ) # Sa í da :
Pre ç o : R$ 45.00
8 print ( f " Desconto percentual : { desconto :.1%} " ) #
Formata como porcentagem com 1 casa decimal
9 # Sa í da : Desconto percentual : 15.7%

Código 2.24: Formatando floats com f-strings

• Alinhamento e Preenchimento (Introdução): Você pode controlar o alinha-


mento do texto dentro de um espaço reservado.

– <: Alinha à esquerda


– >: Alinha à direita
– ˆ: Centraliza

Você também pode especificar um caractere de preenchimento e a largura total.


{variavel:[caractere_preenchimento][alinhamento][largura]}
1 produto = " Resistor "
2 quantidade = 5
3 valor_unitario = 2.50
4
5 print ( f " Item : { produto : <15} | Qtd : { quantidade : >3} | Pre ç o
Un .: R$ { valor_unitario : >6.2 f } " )
6 # Sa í da : Item : Resistor | Qtd : 5 | Pre ç o Un .: R$
2.50
7
8 # Exemplo com preenchimento e centraliza ç ã o
9 titulo = " Relatorio "
10 print ( f " { titulo :=^30} " ) # Preenche com ’= ’ e centraliza em
30 caracteres
11 # Sa í da : ========= Relatorio ==========

Código 2.25: Alinhamento e preenchimento com f-strings

A formatação de strings pode ser bem mais complexa e poderosa. Por enquanto, o
controle de casas decimais e o alinhamento básico são os mais úteis para começar.

Outras Formas de Formatação


Antes das f-strings, Python usava outros métodos para formatar strings:
2.5. Entrada de Dados pelo Usuário: A Função input() 33

• Operador % (estilo C sprintf): Ex: print("Nome: %s, Idade: %d"% (nome,


idade))

• Método [Link](): Ex: print("Nome: {}, Idade: {}".format(nome,


idade))
Você pode encontrar esses estilos em códigos mais antigos. Embora ainda funcionem, as
f-strings são geralmente preferidas em código Python moderno por serem mais
legíveis e, muitas vezes, mais rápidas.
Saber como formatar bem a saída dos seus programas é essencial para apresentar
informações de forma clara e profissional, tanto para o usuário final quanto para você
mesmo durante a depuração. As f-strings são uma ferramenta poderosa e elegante para
essa tarefa.

2.5 Entrada de Dados pelo Usuário: A Função input()


Até agora, os dados que nossos programas utilizaram foram definidos diretamente no
código (por exemplo, base = 10). No entanto, para que os programas sejam verdadeira-
mente úteis e flexíveis, eles geralmente precisam interagir com o usuário, ou seja, receber
informações enquanto estão em execução. Em Python, a principal maneira de obter dados
digitados pelo usuário no console é através da função embutida input().

Usando a Função input()


A função input() faz duas coisas principais:
1. Exibe uma mensagem (opcional, mas altamente recomendável) para o usuário,
indicando qual informação ele deve fornecer. Essa mensagem é chamada de prompt.

2. Pausa a execução do programa e espera que o usuário digite algo e pressione a tecla
Enter.

3. O texto que o usuário digitou é então retornado pela função e pode ser armazenado
em uma variável.
A sintaxe básica é: variavel_para_guardar_o_dado = input("Sua mensagem para o
usuário aqui: ")
Vamos a um exemplo simples: pedir o nome do usuário e depois cumprimentá-lo,
usando as f-strings que aprendemos na seção anterior para formatar a saída.
1 # Pedindo o nome do usu á rio
2 print ( " Bem - vindo ( a ) ao nosso programa interativo ! " )
3 nome_usuario = input ( " Por favor , digite seu nome : " ) # O texto
entre par ê nteses é o prompt
4 # Exibindo uma sauda ç ã o personalizada usando f - string
5 print ( f " Ol á , { nome_usuario }! É um prazer ter voc ê aqui . " )

Código 2.26: Pedindo e usando o nome do usuario


Quando este código for executado, a mensagem "Por favor, digite seu nome: "aparecerá
no console, e o cursor ficará piscando, esperando a entrada do usuário. Se o usuário digitar
"Beatriz"e pressionar Enter, a variável nome_usuario armazenará a string "Beatriz", e
a saudação será "Olá, Beatriz! É um prazer ter você aqui.".
34 Capítulo 2. Sintaxe Básica e Tipos de Dados

Atenção: input() Sempre Retorna uma String (str)!


Este é um dos pontos mais importantes (e que mais causa confusão em iniciantes)
sobre a função input(): independentemente do que o usuário digite – seja um
nome, um número inteiro, um número decimal – a função input() SEMPRE
retornará essa informação como uma string (str).
Vamos verificar isso com a função type():
1 # Solicitando a idade ao usu á rio
2 i d a d e _ d i g i t a d a _ p e l o _ u s u a r i o = input ( " Digite sua idade : " )
3
4 print ( f " Voc ê digitou : { i d a d e _ d i g i t a d a _ p e l o _ u s u a r i o } " )
5 print ( f " O tipo da vari á vel ’ i d a d e _ d i g i t a d a _ p e l o _ u s u a r i o ’ é :
{ type ( i d a d e _ d i g i t a d a _ p e l o _ u s u a r i o ) } " )

Código 2.27: Verificando o tipo do retorno de input()


Mesmo que o usuário digite 25 (que parece um número), a saída do type() será <class
’str’>. Isso significa que a variável idade_digitada_pelo_usuario contém a string
"25", e não o número inteiro 25.

A Necessidade de Converter Tipos (Type Casting)


Se input() sempre nos dá uma string, o que acontece se precisarmos usar o valor
digitado como um número para fazer cálculos? Por exemplo, se quisermos calcular o ano de
nascimento a partir da idade. Não podemos fazer operações matemáticas diretamente com
strings da forma que faríamos com números. Tentar somar "25" (string) com 1 (inteiro)
resultaria em um erro.
É aqui que entra a conversão de tipos (também chamada de type casting). Python
nos fornece funções para converter um valor de um tipo para outro, quando essa conversão
faz sentido. As mais comuns que usaremos com input() são:

• int(valor_string): Tenta converter valor_string para um número inteiro (int).

• float(valor_string): Tenta converter valor_string para um número de ponto


flutuante (float).

Importante: Se a string não puder ser convertida para o tipo desejado (por exemplo, se
o usuário digitar "vinte"quando pedimos um número e tentamos usar int("vinte")), o
Python gerará um erro chamado ValueError. Por enquanto, vamos assumir que o usuário
digitará informações no formato esperado. Aprenderemos a tratar esses tipos de erros de
forma elegante mais adiante no livro (Capítulo 17).
Vamos ver a conversão em ação:
1 # Pedindo a idade e convertendo para inteiro
2 idade_str = input ( " Digite sua idade novamente : " )
3 idade_int = int ( idade_str ) # Converte a string para um n ú mero
inteiro
4
5 ano_atual = 2025 # Vamos supor o ano atual
6 a n o _ n a s c i m e n t o _ a p r o x i m a d o = ano_atual - idade_int
7

8 print ( f " Voc ê tem { idade_int } anos . " )


9 print ( f " Voc ê nasceu aproximadamente em :
{ a n o _ n a s c i m e n t o _ a p r o x i m a d o }. " )
2.6. Comentários em Python: Deixando Anotações no Seu Código 35

10
11 # Pedindo a altura e convertendo para float
12 altura_str = input ( " Digite sua altura em metros ( ex : 1.75) : " )
13 altura_float = float ( altura_str ) # Converte para float
14
15 print ( f " Sua altura é de { altura_float } metros . " )
16 print ( f " O dobro da sua altura seria : { altura_float * 2:.2 f }
metros . " ) # Usando f - string para formatar a sa í da

Código 2.28: Convertendo input para int e float

Exemplo Prático Combinado: Calculadora de Soma Simples


Vamos criar um programa que pede dois números ao usuário, os soma e exibe o
resultado, aproveitando as f-strings para uma saída bem formatada.
1
2 # Pede o primeiro n ú mero ao usu á rio
3 num1_str = input ( " Digite o primeiro n ú mero : " )
4

5 # Pede o segundo n ú mero ao usu á rio


6 num2_str = input ( " Digite o segundo n ú mero : " )
7
8 # Convertendo as strings de entrada para n ú meros de ponto flutuante
( float )
9 # Usamos float para permitir que o usu á rio digite n ú meros com casas
decimais
10 num1_float = float ( num1_str )
11 num2_float = float ( num2_str )
12
13 # Realizando a soma
14 resultado_soma = num1_float + num2_float
15
16 # Exibindo o resultado formatado
17 print ( f " O primeiro n ú mero digitado foi : { num1_float } " )
18 print ( f " O segundo n ú mero digitado foi : { num2_float } " )
19 print ( f " A soma de { num1_float :.2 f } + { num2_float :.2 f } é igual a
{ resultado_soma :.2 f }. " )

Código 2.29: Calculadora de soma simples com input


Execute este programa! Tente digitar números inteiros e números com casas decimais
(ex: 10.5, 3.2). Veja como a conversão para float permite que o cálculo seja feito
corretamente. O que aconteceria se você tentasse digitar um texto como “cinco” quando o
programa espera um número? (Resposta: Dará um ValueError quando tentar converter!)
A função input() é a sua porta de entrada para criar programas que dialogam com o
usuário, tornando-os muito mais dinâmicos e interativos. Lembre-se sempre da regra de
ouro: input() retorna strings, então converta quando precisar de números!

2.6 Comentários em Python: Deixando Anotações no


Seu Código
Já esbarramos nos comentários (#) em alguns dos nossos exemplos anteriores. Eles são
como post-its ou anotações que você deixa no seu código, não para o computador, mas
36 Capítulo 2. Sintaxe Básica e Tipos de Dados

para você mesmo ou para outros programadores que possam ler seu trabalho no futuro.
Um código bem comentado é significativamente mais fácil de entender, depurar e manter.
Nesta seção, vamos revisar os comentários de linha única e explorar como fazer
comentários que se estendem por várias linhas, além de dar uma espiada em um tipo
especial de comentário chamado docstring.

Comentários de Linha Única (#)


Como vimos, em Python, qualquer texto que aparece após o símbolo de cerquilha (#)
em uma linha é considerado um comentário e é completamente ignorado pelo interpretador
Python quando ele executa o programa.
Usos comuns para comentários de linha única:

1. Explicar uma linha de código ou um pequeno bloco:


1 # Calcula a taxa de juros mensal
2 taxa_mensal = taxa_anual / 12

Código 2.30: Comentario explicando uma linha

2. Deixar lembretes ou tarefas (TODOs):


1 # TODO : Implementar a valida ç ã o de entrada para este campo
2 nome_cliente = input ( " Digite o nome do cliente : " )

Código 2.31: Comentario como lembrete TODO

3. “Comentar” (desabilitar temporariamente) uma linha de código: Isso é


muito útil durante a depuração, quando você quer testar seu programa sem uma
parte específica do código.
1 # print (" Esta linha ser á executada .")
2 # print (" Esta linha N Ã O ser á executada porque est á
comentada .")
3 print ( " Esta linha tamb é m ser á executada . " )

Código 2.32: Comentando uma linha de codigo

Lembre-se, o comentário se estende apenas até o final da linha onde o # aparece.

Comentários de Múltiplas Linhas


Às vezes, você precisa escrever uma explicação mais longa que não cabe confortavelmente
em uma única linha. Existem duas maneiras principais de lidar com isso em Python:

• Opção 1: Usando Múltiplos Símbolos # (Recomendado para Blocos de


Comentário) A forma mais clara e comum de criar um bloco de comentário que se
estende por várias linhas é simplesmente iniciar cada linha do comentário com um #.
1 # Este é um exemplo de um coment á rio
2 # que se estende por v á rias linhas .
3 # Ele é usado para explicar uma se ç ã o
4 # mais complexa do algoritmo ou uma decis ã o de design .
5 veloc idade_ma xima = 220 # km / h
2.6. Comentários em Python: Deixando Anotações no Seu Código 37

6 fator_seguranca = 1.5
7 l im ite _o pe rac io na l = veloci dade_maxi ma / fator_seguranca

Código 2.33: Comentario de bloco com multiplos #

Muitos editores de código e IDEs oferecem atalhos de teclado (como Ctrl+/ ou


Cmd+/) para selecionar um bloco de linhas e adicionar ou remover # de todas elas
simultaneamente, o que torna essa abordagem bastante prática.

• Opção 2: Usando Strings Multilinhas com Aspas Triplas (Uso com Res-
salvas) Python permite que você crie strings que se estendem por várias linhas
usando aspas triplas, sejam elas simples (”’...”’) ou duplas ("..."). Já vimos
isso brevemente na Seção 2.3.3 ao falar sobre como delimitar strings.
Se uma string multilinhas como essa aparece em seu código e não está atribuída a
nenhuma variável e também não é a primeira instrução dentro de uma definição de
módulo, função ou classe, o interpretador Python efetivamente a ignora. Por causa
disso, alguns programadores as utilizam como uma forma de criar comentários de
bloco:
1 ’’’
2 Este tamb é m é um tipo de " coment á rio "
3 de m ú ltiplas linhas , utilizando
4 aspas triplas simples .
5 O interpretador Python o tratar á como uma string literal
6 que n ã o é usada para nada , ent ão , na pr á tica , é ignorada .
7 ’’’
8 fator_ajuste = 0.95
9

10 """
11 E este exemplo usa
12 aspas triplas duplas .
13 A mesma l ó gica se aplica .
14 """
15 valor_corrigido = valor_original * fator_ajuste

Código 2.34: Comentario de bloco com strings multilinhas

A Diferença Crucial: Docstrings


Embora usar strings multilinhas não atribuídas funcione para “comentar” blocos, é
fundamental entender que o uso primário e mais importante de strings multilinhas com
aspas triplas, especialmente quando elas aparecem como a primeira instrução logo
após a definição de um módulo, função, classe ou método, é para criar o que
chamamos de docstrings (strings de documentação).
As docstrings são um recurso poderoso do Python. Elas são associadas ao objeto
(módulo, função, classe) como sua documentação oficial e podem ser acessadas em tempo
de execução (por exemplo, pela função help()) e por ferramentas que geram documentação
automaticamente.
Recomendação: Para comentários de bloco que são apenas explicações e não se
destinam a ser a documentação formal de uma função ou classe, é geralmente mais claro
e menos ambíguo usar o método de múltiplos #. Reserve as strings multilinhas com
aspas triplas no início de definições para as docstrings.
38 Capítulo 2. Sintaxe Básica e Tipos de Dados

Docstrings
Já que tocamos no assunto, vamos entender um pouco melhor o que é uma docstring,
mesmo que só vejamos funções e classes em detalhes bem mais à frente.
Uma docstring é uma string literal que serve para documentar um objeto Python. Ela
deve descrever o que o objeto faz, quais parâmetros ele espera (se for uma função ou
método), o que ele retorna, etc.
Veja um exemplo muito simples de uma docstring em uma função (não se preocupe
com a sintaxe da função def por enquanto, apenas observe a string com aspas triplas):
1 def calcular_media ( nota1 , nota2 ) :
2 """
3 Calcula a m é dia aritm é tica simples entre duas notas .
4
5 Args :
6 nota1 ( float ) : O valor da primeira nota .
7 nota2 ( float ) : O valor da segunda nota .
8
9 Returns :
10 float : A m é dia calculada entre as duas notas .
11 """
12 media = ( nota1 + nota2 ) / 2
13 return media
14
15 # Se voc ê executasse help ( calcular_media ) no console Python ,
16 # a docstring acima seria exibida .

Código 2.35: Exemplo de docstring em uma funcao


Discutiremos como escrever boas docstrings quando aprendermos a criar nossas próprias
funções (Capítulo 10). Por ora, apenas saiba que elas existem e são diferentes de comentários
comuns.

Boas Práticas para Comentar seu Código


Comentar não é apenas uma formalidade; é uma arte que melhora a qualidade do seu
software.
• Comente o “porquê”, não o “o quê” (se o código for óbvio):
– Ruim: # Atribui 10 para a variável x seguido de x = 10. (O código já
diz isso).
– Bom: # Usamos 10 como valor base, conforme especificação do cliente
XPTO. seguido de x = 10. (Explica a razão).
• Mantenha os comentários atualizados: Um comentário que descreve algo que o
código não faz mais é pior do que nenhum comentário, pois engana o leitor. Se você
mudar o código, revise os comentários próximos.
• Seja claro e conciso: Comentários devem ajudar, não confundir com excesso de
palavras.
• Não comente em excesso: Um código limpo, com nomes de variáveis e funções
bem escolhidos, muitas vezes é autoexplicativo e requer menos comentários. O
objetivo é clareza, não um comentário para cada linha.
2.7. Manipulando Textos: Métodos Comuns de Strings 39

Concluindo
Os comentários são uma ferramenta de comunicação essencial na programação. Eles
são escritos para seres humanos (você no futuro, ou seus colegas de equipe) entenderem
melhor o que o código faz e por que ele faz de determinada maneira. Usá-los de forma
eficaz é uma marca de um bom programador.
Com esta seção, encerramos nossa exploração inicial da sintaxe básica e dos tipos de
dados em Python. No próximo capítulo, começaremos a ver como usar operadores para
realizar cálculos e manipular esses dados!

2.7 Manipulando Textos: Métodos Comuns de Strings


No Python, as strings (str) são mais do que apenas sequências passivas de caracteres.
Elas vêm equipadas com um conjunto poderoso de “ferramentas” embutidas chamadas
métodos. Um método é, essencialmente, uma função que “pertence” a um objeto (neste
caso, a uma string) e é projetada para realizar operações específicas sobre esse objeto ou
relacionadas a ele.
A sintaxe geral para chamar um método em uma string é:
minha_string.nome_do_metodo(argumentos_se_necessario)
Onde minha_string é a variável que contém a string (ou a própria string literal),
seguida por um ponto (.), o nome do método e, finalmente, parênteses (). Alguns métodos
podem precisar de informações adicionais (argumentos) dentro dos parênteses, enquanto
outros não.
Uma característica fundamental dos métodos de string em Python é que as strings são
imutáveis. Isso significa que os métodos de string nunca modificam a string original; em
vez disso, eles retornam uma nova string com as modificações aplicadas. Se você quiser
manter a mudança, precisará atribuir o resultado do método a uma nova variável ou à
própria variável original.
Vamos explorar alguns dos métodos de string mais comuns e úteis.

2.7.1 Métodos para Alterar Maiúsculas e Minúsculas


Esses métodos são úteis para padronizar o texto, por exemplo, antes de fazer compara-
ções ou buscas insensíveis a maiúsculas/minúsculas.

• minha_string.lower(): Retorna uma nova string com todos os caracteres alfabéti-


cos convertidos para minúsculas.
1 texto_original = " Python É Divertido ! "
2 texto_minusculo = texto_original . lower ()
3 print ( f " Original : ’{ texto_original } ’ " ) # Sa í da :
Original : ’ Python É Divertido ! ’
4 print ( f " Min ú sculo : ’{ texto_minusculo } ’ " ) # Sa í da :
Min ú sculo : ’ python é divertido ! ’

Código 2.36: Usando o método lower()

• minha_string.upper(): Retorna uma nova string com todos os caracteres alfabéti-


cos convertidos para maiúsculas.
40 Capítulo 2. Sintaxe Básica e Tipos de Dados

1 saudacao = " ol á , mundo "


2 s au dac ao _m aiu sc ul a = saudacao . upper ()
3 print ( f " Sauda ç ã o em mai ú sculas : ’{ s au da cao _m ai usc ul a } ’ " ) #
Sa í da : OL Á , MUNDO

Código 2.37: Usando o método upper()

• minha_string.capitalize(): Retorna uma nova string com o primeiro caractere


convertido para maiúscula e todos os outros caracteres alfabéticos para minúsculas.
1 frase = " python é uma linguagem legal . "
2 f ra se_ ca pi tal iz ad a = frase . capitalize ()
3 print ( f " Frase capitalizada : ’{ fr ase _c ap ita li za da } ’ " ) #
Sa í da : Python é uma linguagem legal .

Código 2.38: Usando o método capitalize()

• minha_string.title(): Retorna uma nova string onde o primeiro caractere de


cada palavra é convertido para maiúscula e os demais caracteres alfabéticos de cada
palavra para minúsculas.
1 titulo_livro = " guia pr á tico de python para iniciantes "
2 titulo_formatado = titulo_livro . title ()
3 print ( f " T í tulo formatado : ’{ titulo_formatado } ’ " )
4 # Sa í da : Guia Pr á tico De Python Para Iniciantes

Código 2.39: Usando o método title()

Lembre-se da Imutabilidade:
1 minha_cidade = " s ã o mateus "
2 minha_cidade . upper () # Esta linha executa o m é todo , mas N Ã O altera
’ minha_cidade ’
3 print ( minha_cidade ) # Sa í da : s ã o mateus
4
5 cidade_maiuscula = minha_cidade . upper () # Agora sim , o resultado é
guardado
6 print ( cidade_maiuscula ) # Sa í da : S Ã O MATEUS

Código 2.40: Imutabilidade das strings e métodos

2.7.2 Métodos para Remover Espaços em Branco


Frequentemente, ao receber entradas de usuários ou ler dados de arquivos, as strings
podem vir com espaços em branco desnecessários no início ou no fim. Esses métodos
ajudam a “limpar” essas strings.
• minha_string.strip(): Retorna uma nova string com os espaços em branco (espa-
ços, tabulações \t, novas linhas \n, etc.) removidos do início e do fim da string.
1 entrada_usuario = " Python com espa ç os "
2 entrada_limpa = entrada_usuario . strip ()
3 print ( f " Original : ’{ entrada_usuario } ’ " )
4 print ( f " Limpa com strip () : ’{ entrada_limpa } ’ " ) # Sa í da :
’ Python com espa ç os ’

Código 2.41: Usando o método strip()


2.7. Manipulando Textos: Métodos Comuns de Strings 41

• minha_string.lstrip(): Retorna uma nova string com os espaços em branco


removidos apenas do início (lado esquerdo, “left strip”).
1 t e x t o _ c o m _ e s p a c o _ i n i c i o = " Indentado ? "
2 t e x t o _ s e m _ e s p a c o _ i n i c i o = t e x t o _ c o m _ e s p a c o _ i n i c i o . lstrip ()
3 print ( f " Original : ’{ t e x t o _ c o m _ e s p a c o _ i n i c i o } ’ " )
4 print ( f " Com lstrip () : ’{ t e x t o _ s e m _ e s p a c o _ i n i c i o } ’ " ) #
Sa í da : ’ Indentado ? ’

Código 2.42: Usando o método lstrip()

• minha_string.rstrip(): Retorna uma nova string com os espaços em branco


removidos apenas do fim (lado direito, “right strip”).
1 l i n h a _ co m _ e s p a c o _ f i m = " Final da linha . "
2 l i n h a _ se m _ e s p a c o _ f i m = li n h a _ c o m _ e s p a c o _ f i m . rstrip ()
3 print ( f " Original : ’{ l i n h a _c o m _ e s p a c o _ f i m } ’ " )
4 print ( f " Com rstrip () : ’{ l i n h a _ s e m _ e s p a c o _ f i m } ’ " ) # Sa í da :
’ Final da linha . ’

Código 2.43: Usando o método rstrip()

Estes métodos também podem remover outros caracteres específicos se você passá-los como
argumento (ex: minha_string.strip(’*’) removeria todos os asteriscos do início e fim).

2.7.3 Métodos para Busca e Substituição


• minha_string.find(substring): Procura pela primeira ocorrência da substring
dentro da minha_string e retorna o índice (posição) onde ela começa. Se a
substring não for encontrada, retorna -1. A contagem de índices começa em
0.
1 frase_busca = " O rato roeu a roupa do rei de Roma . "
2 posicao_rato = frase_busca . find ( " rato " )
3 print ( f " A palavra ’ rato ’ come ç a na posi ç ã o :
{ posicao_rato } " ) # Sa í da : 2
4
5 posicao_gato = frase_busca . find ( " gato " )
6 print ( f " A palavra ’ gato ’ come ç a na posi ç ã o :
{ posicao_gato } " ) # Sa í da : -1
7

8 po s ic a o_ r e i_ a po s _ 15 = frase_busca . find ( " rei " , 15) #


Procura ’ rei ’ a partir do í ndice 15
9 print ( f " Posi ç ã o de ’ rei ’ ap ó s o í ndice 15:
{ p o si c ao _ r ei _ ap o s _1 5 } " ) # Sa í da : 27

Código 2.44: Usando o método find()

• minha_string.replace(antigo, novo): Retorna uma nova string onde todas as


ocorrências da substring antigo são substituídas pela substring novo.
1 texto_original = " Eu gosto de sorvete de chocolate . "
2 texto_modificado = texto_original . replace ( " chocolate " ,
" morango " )
3 print ( f " Texto original : { texto_original } " )
4 print ( f " Texto modificado : { texto_modificado } " )
42 Capítulo 2. Sintaxe Básica e Tipos de Dados

5
6 frase_repetida = " casa casa casa "
7 frase_corrigida = frase_repetida . replace ( " casa " , " lar " )
8 print ( f " Frase corrigida : { frase_corrigida } " ) # Sa í da : lar
lar lar

Código 2.45: Usando o método replace()

2.7.4 Métodos para Dividir e Juntar Strings


Esses métodos são muito úteis para processar textos estruturados ou para construir
strings a partir de múltiplos pedaços.
• minha_string.split(delimitador_opcional): Divide (ou “quebra”) a string em
uma lista de substrings. O resultado é uma lista, um tipo de estrutura de dados
que veremos em detalhes no Capítulo 6.

– Se nenhum delimitador é especificado, o método divide a string pelos espaços


em branco (espaços, tabulações, novas linhas) e múltiplos espaços são tratados
como um único separador. Strings vazias não são incluídas no resultado.
– Se um delimitador (que é uma string) é fornecido, a string é dividida em
todas as ocorrências desse delimitador.
1 f ra se_ pa ra _di vi di r = " Python é uma linguagem muito legal "
2 palavras = fra se _p ara _d iv idi r . split () # Divide pelos
espa ç os
3 print ( f " Palavras da frase : { palavras } " )
4 # Sa í da : [ ’ Python ’, ’é ’, ’ uma ’, ’ linguagem ’, ’ muito ’,
’ legal ’]
5
6 dados_csv = " Ana ,30 , Engenheira "
7 campos = dados_csv . split ( ’ , ’) # Divide pela v í rgula
8 print ( f " Campos CSV : { campos } " )
9 # Sa í da : [ ’ Ana ’, ’30 ’ , ’ Engenheira ’]

Código 2.46: Usando o método split()

• string_separadora.join(lista_de_strings): Faz o oposto do split(). Ele


pega uma lista (ou outra sequência iterável) de strings e as junta em uma única
string, usando a string_separadora (a string na qual o método é chamado) entre
cada elemento da lista.
1 lista_palavras = [ " Python " , " é " , " poderoso " ]
2
3 # Juntando com um espa ç o como separador
4 f r a s e _ mo n t a d a _ e s p a c o = " " . join ( lista_palavras )
5 print ( f " Frase montada com espa ç o :
’{ f r a s e _ m o n ta d a _ e s p a c o } ’ " )
6
7 # Juntando com um tra ç o como separador
8 fr a se _ mo n t ad a _t r a co = " -" . join ( lista_palavras )
9 print ( f " Frase montada com tra ç o : ’{ fr a se _ mo n t ad a _t r a co } ’ " )
10
11 # Juntando sem separador
12 f r a se _ se m _ se p ar a do r = " " . join ( lista_palavras )
2.7. Manipulando Textos: Métodos Comuns de Strings 43

13 print ( f " Frase sem separador : ’{ fr a s e_ s em _ s ep a ra d or } ’ " )

Código 2.47: Usando o método join()

2.7.5 Métodos de Verificação (Retornam True ou False)


Estes métodos são muito úteis para validar o formato ou o conteúdo de uma string.
• minha_string.startswith(prefixo): Retorna True se a string começa com a
prefixo especificada.

• minha_string.endswith(sufixo): Retorna True se a string termina com o sufixo


especificado.
1 nome_arquivo = " relatorio_final . pdf "
2 print ( f " O arquivo ’{ nome_arquivo } ’ come ç a com ’ relatorio ’?
{ nome_arquivo . startswith ( ’ relatorio ’) } " )
3 print ( f " O arquivo ’{ nome_arquivo } ’ termina com ’. txt ’?
{ nome_arquivo . endswith ( ’. txt ’) } " )
4 print ( f " O arquivo ’{ nome_arquivo } ’ termina com ’. pdf ’?
{ nome_arquivo . endswith ( ’. pdf ’) } " )

Código 2.48: Usando startswith() e endswith()

• minha_string.isdigit(): Retorna True se todos os caracteres na string são dígitos


(0-9) e a string não está vazia.

• minha_string.isalpha(): Retorna True se todos os caracteres na string são letras


do alfabeto e a string não está vazia.

• minha_string.isalnum(): Retorna True se todos os caracteres na string são alfa-


numéricos (letras ou números) e a string não está vazia.

• minha_string.isspace(): Retorna True se todos os caracteres na string são espaços


em branco (espaço, tabulação, etc.) e a string não está vazia.

• minha_string.islower(): Retorna True se todos os caracteres alfabéticos na string


são minúsculos e há pelo menos um caractere alfabético.

• minha_string.isupper(): Retorna True se todos os caracteres alfabéticos na string


são maiúsculos e há pelo menos um caractere alfabético.
1 # Exemplos de m é todos de verifica ç ã o
2 valor1 = " 12345 "
3 valor2 = " Python3 "
4 valor3 = " Teste "
5 valor4 = " " # Apenas espa ç os
6 valor5 = " tudoemminusculo "
7 valor6 = " TUDO EM MAIUSCULO "
8
9 print ( f " ’{ valor1 } ’. isdigit () ? { valor1 . isdigit () } " ) #
Sa í da : True
10 print ( f " ’{ valor2 } ’. isdigit () ? { valor2 . isdigit () } " ) #
Sa í da : False
11 print ( f " ’{ valor2 } ’. isalnum () ? { valor2 . isalnum () } " ) #
Sa í da : True
44 Capítulo 2. Sintaxe Básica e Tipos de Dados

12 print ( f " ’{ valor3 } ’. isalpha () ? { valor3 . isalpha () } " ) #


Sa í da : True
13 print ( f " ’{ valor3 } ’. islower () ? { valor3 . islower () } " ) #
Sa í da : False
14 print ( f " ’{ valor4 } ’. isspace () ? { valor4 . isspace () } " ) #
Sa í da : True
15 print ( f " ’{ valor5 } ’. islower () ? { valor5 . islower () } " ) #
Sa í da : True
16 print ( f " ’{ valor6 } ’. isupper () ? { valor6 . isupper () } " ) #
Sa í da : True

Código 2.49: Usando métodos de verificação de tipo de caractere

2.7.6 Encadeando Chamadas de Métodos


Como muitos métodos de string retornam uma nova string como resultado, você pode
frequentemente encadear chamadas de métodos. Isso significa aplicar um método ao
resultado de outro método, em uma única linha.
1 # Exemplo de encadeamento de m é todos
2 entrada_bruta_usuario = " Ol á , MUNDO ! Bem - vindo . "
3
4 # Limpa espa ç os , converte para min ú sculas e depois capitaliza a
primeira letra
5 en tr ada _p ro ces sa da =
e n t r a d a _ b r u t a _ u s u a r i o . strip () . lower () . capitalize ()
6 print ( f " Entrada processada : ’{ e nt rad a_ pr oce ss ad a } ’ " )
7 # Sa í da : Entrada processada : ’ Ol á , mundo ! bem - vindo . ’
8
9 # Outro exemplo : verificar se um input ( ap ó s limpar e converter ) é
um nome espec í fico
10 nome_digitado = input ( " Digite ’ admin ’ para continuar :
" ) . strip () . lower ()
11 if nome_digitado == " admin " :
12 print ( " Acesso de administrador concedido . " )
13 else :
14 print ( " Acesso negado . " )

Código 2.50: Encadeando métodos de string


O encadeamento pode tornar o código conciso, mas evite criar linhas excessivamente longas
ou complexas que dificultem a leitura.

Conclusão da Seção sobre Métodos de Strings


Estes são apenas alguns dos métodos de string mais frequentemente utilizados em
Python. A string, como tipo de dado, é muito rica em funcionalidades. Esses métodos nos
fornecem um grande poder para limpar, transformar, analisar e validar dados textuais,
que são onipresentes na programação.
À medida que você avança, encorajo você a consultar a documentação oficial do Python
sobre métodos de string para descobrir ainda mais ferramentas úteis. Comaprática, o uso
desses métodos se tornará natural e muito valioso no seu dia a dia como programador.
Capítulo 3

Operadores e Expressões

Nos capítulos anteriores, demos nossos primeiros passos no universo Python: enten-
demos o que é a linguagem, como configurar nosso ambiente de programação, como
armazenar informações em variáveis e os tipos de dados básicos com os quais podemos
trabalhar. Agora, é hora de aprender como realizar ações e manipular esses dados. Para
isso, precisamos conhecer os operadores e como eles formam expressões.
Se as variáveis são os “substantivos” da nossa programação (os “objetos” com os quais
trabalhamos), os operadores são como os “verbos” e as “conjunções” – eles nos permitem
realizar ações sobre esses substantivos ou combiná-los de maneiras significativas.

3.1 Introdução a Operadores e Expressões


No dia a dia, usamos operadores constantemente, mesmo sem pensar neles com esse
nome. Quando você soma dois números (ex: 5 + 3), o + é um operador. Quando verifica
se uma coisa é igual a outra, você está aplicando uma operação de comparação. A
programação formaliza esses conceitos.

• O que é um Operador?
Em Python (e na maioria das linguagens de programação), um operador é um
símbolo especial ou uma palavra-chave que realiza uma operação sobre um ou mais
valores. Esses valores sobre os quais o operador atua são chamados de operandos.
Por exemplo, na expressão 10 + 5:

– 10 é um operando.
– 5 é outro operando.
– + é o operador (neste caso, o operador de adição).

• O que é uma Expressão?


Uma expressão é uma combinação de operandos (valores, variáveis) e operadores
que o Python pode avaliar para produzir um novo valor. Quase tudo que “calcula
algo” em Python é uma expressão.
Exemplos de expressões:

– 10 + 5 (resulta no valor 15)


– largura * altura (se largura e altura são variáveis com valores numéricos,
resulta no produto deles)

45
46 Capítulo 3. Operadores e Expressões

– idade >= 18 (se idade é 20, resulta no valor booleano True)


– nome_completo = primeiro_nome + “ ” + ultimo_nome (a parte à direita
do = é uma expressão que concatena strings, e o resultado é atribuído a
nome_completo)

A Importância de Entender Operadores


Compreender os diferentes tipos de operadores disponíveis em Python e como eles
funcionam é fundamental porque eles são os blocos de construção para:

• Realizar cálculos matemáticos de todos os tipos.

• Comparar valores para tomar decisões lógicas nos seus programas (por exemplo, em
estruturas if).

• Manipular textos (strings).

• Trabalhar com estruturas de dados mais complexas.

• Controlar o fluxo de execução dos seus programas.

Nosso objetivo neste capítulo é apresentar os principais operadores do Python, catego-


rizados por sua função (aritméticos, de atribuição, comparação, lógicos, etc.), e mostrar
como usá-los para formar expressões úteis e poderosas. Com esse conhecimento, seus
programas começarão a ganhar muito mais dinamismo e capacidade de realizar tarefas
complexas de forma “descomplicada”.

3.2 Operadores Aritméticos: Fazendo Contas com Python


Os operadores aritméticos são aqueles que usamos para realizar as operações matemáti-
cas básicas, como soma, subtração, multiplicação e divisão. Eles funcionam primariamente
com os tipos de dados numéricos que vimos no Capítulo 2: inteiros (int), números de
ponto flutuante (float) e também com números complexos (complex), embora nossos
exemplos aqui foquem mais nos dois primeiros para simplificar.
Vamos conhecer cada um deles:

• Adição (+)
O operador de adição soma dois operandos.
1 # Exemplos de adi ç ã o
2 soma1 = 10 + 5
3 print ( f " 10 + 5 = { soma1 } " ) # Sa í da : 10 + 5 = 15
4
5 valor_a = 25.5
6 valor_b = 10
7 resultado_soma = valor_a + valor_b # Um float + um int
resulta em float
8 print ( f " { valor_a } + { valor_b } = { resultado_soma } " ) #
Sa í da : 25.5 + 10 = 35.5

Código 3.1: Exemplos de adição


3.2. Operadores Aritméticos: Fazendo Contas com Python 47

• Subtração (-)
O operador de subtração subtrai o segundo operando do primeiro. Ele também pode
ser usado como um operador unário para indicar um número negativo.
1 # Exemplos de subtra ç ã o
2 diferenca1 = 10 - 5
3 print ( f " 10 - 5 = { diferenca1 } " ) # Sa í da : 10 - 5 = 5
4

5 credito = 100.75
6 debito = 50.25
7 saldo = credito - debito
8 print ( f " Saldo : { saldo } " ) # Sa í da : Saldo : 50.5
9
10 numero_negativo = -10 # Uso un á rio do ’-’
11 print ( f " N ú mero negativo : { numero_negativo } " ) # Sa í da :
N ú mero negativo : -10

Código 3.2: Exemplos de subtração

• Multiplicação (*)
O operador de multiplicação, como o nome sugere, multiplica dois operandos.
1 # Exemplos de multiplica ç ã o
2 produto1 = 10 * 5
3 print ( f " 10 * 5 = { produto1 } " ) # Sa í da : 10 * 5 = 50
4
5 quantidade_itens = 3
6 preco_unitario = 19.99
7 total_compra = quantidade_itens * preco_unitario
8 print ( f " Total da compra : R$ { total_compra :.2 f } " ) # Sa í da :
Total da compra : R$ 59.97

Código 3.3: Exemplos de multiplicação

• Divisão (/)
O operador de divisão divide o primeiro operando pelo segundo. Uma característica
importante do Python 3 é que o resultado da divisão / é sempre um número
de ponto flutuante (float), mesmo que a divisão seja exata entre dois inteiros.
1 # Exemplos de divis ã o
2 quociente1 = 10 / 5 # Mesmo sendo uma divis ã o exata , o
resultado é float
3 print ( f " 10 / 5 = { quociente1 } " ) # Sa í da : 10 / 5 = 2.0
4 print ( f " Tipo do resultado : { type ( quociente1 ) } " ) # Sa í da :
Tipo do resultado : < class ’ float ’>
5
6 quociente2 = 10 / 3
7 print ( f " 10 / 3 = { quociente2 } " ) # Sa í da : 10 / 3 =
3. 33 33 333 33 33 333 35
8 print ( f " 10 / 3 ( formatado ) : { quociente2 :.2 f } " ) # Sa í da : 10
/ 3 ( formatado ) : 3.33
9
10 distancia_km = 100
11 tempo_h = 1.5
12 velocidade_kmh = distancia_km / tempo_h
13 print ( f " Velocidade m é dia : { velocidade_kmh :.2 f } km / h " ) #
Sa í da : Velocidade m é dia : 66.67 km / h
48 Capítulo 3. Operadores e Expressões

Código 3.4: Exemplos de divisão

• Divisão Inteira (//)


Às vezes, estamos interessados apenas na parte inteira de uma divisão, descartando
qualquer parte fracionária. Para isso, usamos o operador de divisão inteira //. Ele
realiza a divisão e “arredonda para baixo” o resultado para o inteiro mais próximo
(também conhecido como operação “piso”).

– Se ambos os operandos forem inteiros, o resultado será um inteiro.


– Se pelo menos um dos operandos for um float, o resultado será um float (mas
ainda representando o “piso”, ex: 7.0 // 2.0 resulta em 3.0).

1 # Exemplos de divis ã o inteira


2 partes_inteiras = 10 // 3
3 print ( f " 10 // 3 = { partes_inteiras } " ) # Sa í da : 10 // 3 = 3
4
5 total_doces = 25
6 criancas = 4
7 doces _por_cri anca = total_doces // criancas
8 print ( f " Cada crian ç a recebe { do ces_por_ crianca } doces . " ) #
Sa í da : Cada crian ç a recebe 6 doces .
9
10 print ( f " 7.0 // 2.0 = {7.0 // 2.0} " ) # Sa í da : 7.0 // 2.0 =
3.0
11 print ( f " -7 // 3 = { -7 // 3} " ) # Sa í da : -7 // 3 = -3
( arredonda para baixo , -2.33... -> -3)

Código 3.5: Exemplos de divisão inteira

• Módulo ou Resto da Divisão (%)


O operador de módulo (ou resto) retorna o resto da divisão do primeiro operando
pelo segundo. Ele é incrivelmente útil em várias situações, como verificar se um
número é par ou ímpar, ou para lidar com operações cíclicas. Em Python, o sinal do
resultado da operação de módulo (a % n) é o mesmo do divisor (n).
1 # Exemplos de m ó dulo
2 resto1 = 10 % 3
3 print ( f " 10 % 3 = { resto1 } " ) # Sa í da : 10 % 3 = 1 (10
dividido por 3 é 3 com resto 1)
4
5 resto2 = 10 % 2
6 print ( f " 10 % 2 = { resto2 } " ) # Sa í da : 10 % 2 = 0 (10 é par ,
sem resto quando dividido por 2)
7

8 total_minutos = 125
9 horas = total_minutos // 60
10 minut os_restan tes = total_minutos % 60
11 print ( f " { total_minutos } minutos equivalem a { horas }
hora ( s ) e { m inutos_r estantes } minuto ( s ) . " )
12 # Sa í da : 125 minutos equivalem a 2 hora ( s ) e 5 minuto ( s ) .
13
14 # Comportamento do sinal com m ó dulo :
15 print ( f " 10 % 3 = {10 % 3} " ) # Sa í da : 1
3.2. Operadores Aritméticos: Fazendo Contas com Python 49

16 print ( f " -10 % 3 = { -10 % 3} " ) # Sa í da : 2 ( Se o divisor é


positivo , o resultado é >= 0)
17 print ( f " 10 % -3 = {10 % -3} " ) # Sa í da : -2 ( Se o divisor é
negativo , o resultado é <= 0)
18 print ( f " -10 % -3 = { -10 % -3} " ) # Sa í da : -1

Código 3.6: Exemplos de módulo

• Exponenciação (**)
O operador de exponenciação eleva o primeiro operando à potência do segundo.
1 # Exemplos de exponencia ç ã o
2 potencia1 = 2 ** 3 # 2 elevado a 3
3 print ( f " 2 ** 3 = { potencia1 } " ) # Sa í da : 2 ** 3 = 8
4
5 lado_quadrado = 5
6 area_quadrado = lado_quadrado ** 2
7 print ( f " Á rea de um quadrado de lado { lado_quadrado }:
{ area_quadrado } " ) # Sa í da : Á rea de um quadrado de lado
5: 25
8
9 raiz_ cubica_d e_27 = 27 ** (1/3) # Raiz c ú bica pode ser
feita elevando a 1/3
10 print ( f " Raiz c ú bica de 27: { rai z_cubica _de_27 :.1 f } " ) #
Sa í da : Raiz c ú bica de 27: 3.0

Código 3.7: Exemplos de exponenciação

Um Exemplo Combinando Operadores: Conversão de Temperatura


Vamos usar alguns desses operadores para um cálculo prático: converter uma tempera-
tura de Celsius para Fahrenheit. A fórmula é: F = (C × 95 ) + 32.
1 # Convertendo Celsius para Fahrenheit
2 t e mp era t ur a _ ce l si u s = 25.0
3

4 # Aplicando a f ó rmula
5 t e m p e r a t u r a _ f a h r e n h e i t = ( te m p er a tu r a_ c e ls i us * 9/5) + 32
6
7 print ( f " { te m p er a tu r a_ c e ls i us } ° C equivalem a
{ t e m p e r a t u r a _ f a h r e n h e i t :.1 f } ° F . " )
8 # Sa í da : 25.0 ° C equivalem a 77.0 ° F .

Código 3.8: Convertendo Celsius para Fahrenheit


Neste exemplo, usamos multiplicação (*), divisão (/) e adição (+). Também usamos
parênteses () para garantir que a multiplicação e a divisão ocorram antes da adição,
assim como na matemática convencional. Falaremos mais sobre a ordem das operações
(precedência) em uma seção posterior (Seção 3.8).

Tipos de Dados em Operações Aritméticas


É importante notar como os tipos de dados interagem:

• Se você opera um int com outro int, o resultado geralmente é um int (a grande
exceção é a divisão /, que sempre retorna float).
50 Capítulo 3. Operadores e Expressões

• Se você opera um int com um float, ou um float com outro float, o resultado
é sempre um float. Python “promove” o inteiro a float para realizar a operação e
manter a precisão.

Dominar os operadores aritméticos é o primeiro passo para realizar qualquer tipo de


cálculo ou processamento numérico em seus programas Python.

3.3 Operadores de Atribuição: Guardando e Atuali-


zando Valores
No universo da programação, uma das tarefas mais fundamentais é dar valores às
nossas variáveis e, subsequentemente, atualizar esses valores à medida que o programa
executa. Para isso, utilizamos os operadores de atribuição. Já conhecemos o mais
básico deles, o sinal de igual (=), mas Python oferece algumas variações úteis que podem
simplificar nosso código.

O Operador de Atribuição Simples (=)


Como vimos extensivamente desde o Capítulo 2, o operador de atribuição simples (=)
é usado para armazenar o valor de uma expressão em uma variável. A regra é simples:
variavel = expressao_ou_valor
O Python primeiro avalia completamente a expressão que está à direita do sinal = e,
somente então, o resultado dessa avaliação é armazenado na variável que está à esquerda.
1 # Relembrando a atribui ç ã o simples
2 i d a d e _ m i n i m a _ p a r a _ v o t a r = 16
3 ano_nascimento = 1990
4 ano_atual = 2025
5
6 idade_atual = ano_atual - ano_nascimento # A express ã o ( ano_atual -
ano_nascimento ) é calculada primeiro
7 print ( f " Idade atual calculada : { idade_atual } " )

Código 3.9: Relembrando a atribuição simples

Operadores de Atribuição Compostos (ou Aumentados)


Muitas vezes, queremos realizar uma operação em uma variável e, em seguida, atribuir
o resultado dessa operação de volta à mesma variável. Por exemplo, para incrementar um
contador: contador = contador + 1
Ou para adicionar um valor a um total: saldo_total = saldo_total + novo_deposito
Python, assim como muitas outras linguagens, oferece uma forma mais curta e expressiva
de escrever essas operações usando operadores de atribuição compostos (também
chamados de operadores de atribuição aumentados). Eles combinam uma operação
aritmética (ou outra) com a atribuição.
A forma geral é: variavel operador= valor que é equivalente a: variavel =
variavel operador valor
Vamos conhecer os principais:
3.3. Operadores de Atribuição: Guardando e Atualizando Valores 51

• += (Adição e Atribuição):
Soma o valor à direita à variável da esquerda e atribui o resultado de volta à variável.
x += y é o mesmo que x = x + y.
1 # Usando +=
2 contador = 0
3 contador += 1 # Equivalente a : contador = contador + 1
4 print ( f " Contador : { contador } " ) # Sa í da : Contador : 1
5

6 saldo_bancario = 500.0
7 deposito = 150.0
8 saldo_bancario += deposito # Adiciona o dep ó sito ao saldo
9 print ( f " Novo saldo : R$ { saldo_bancario :.2 f } " ) # Sa í da :
Novo saldo : R$ 650.00

Código 3.10: Usando o operador +=

• -= (Subtração e Atribuição):
Subtrai o valor à direita da variável da esquerda e atribui o resultado. x -= y é o
mesmo que x = x - y.
1 # Usando -=
2 pontos_de_vida = 100
3 dano_recebido = 25
4 pontos_de_vida -= dano_recebido # Subtrai o dano
5 print ( f " Pontos de vida restantes : { pontos_de_vida } " ) #
Sa í da : Pontos de vida restantes : 75
6
7 estoque_produto = 50
8 estoque_produto -= 5 # Vendeu 5 itens
9 print ( f " Estoque atual : { estoque_produto } " ) # Sa í da :
Estoque atual : 45

Código 3.11: Usando o operador -=

• *= (Multiplicação e Atribuição):
Multiplica a variável da esquerda pelo valor à direita e atribui o resultado. x *= y é
o mesmo que x = x * y.
1 # Usando *=
2 numero = 10
3 numero *= 3 # Multiplica por 3
4 print ( f " N ú mero multiplicado : { numero } " ) # Sa í da : N ú mero
multiplicado : 30
5
6 i n v e s t im e n t o _ i n i c i a l = 1000.0
7 fator_rendimento = 1.05 # Rendimento de 5%
8 i n v e s t im e n t o _ i n i c i a l *= fator_rendimento # Aplica o
rendimento
9 print ( f " Valor do investimento ap ó s rendimento : R$
{ i n v e s t i m e n t o _ i n i c i a l :.2 f } " )
10 # Sa í da : Valor do investimento ap ó s rendimento : R$ 1050.00

Código 3.12: Usando o operador *=

• /= (Divisão e Atribuição):
Divide a variável da esquerda pelo valor à direita e atribui o resultado (que será
sempre float). x /= y é o mesmo que x = x / y.
52 Capítulo 3. Operadores e Expressões

1 # Usando /=
2 d is tan ci a_ tot al _k m = 42.0
3 d is tan ci a_ tot al _k m /= 2 # Percorreu metade da dist â ncia
4 print ( f " Dist â ncia restante : { di sta nc ia _to ta l_ km } km " ) #
Sa í da : Dist â ncia restante : 21.0 km
5
6 soma_notas = 25.5
7 numer o_avalia coes = 3
8 # Apenas para exemplificar o operador :
9 soma_notas /= nume ro_avali acoes
10 print ( f " M é dia ( exemplo de /=) : { soma_notas :.2 f } " ) # Sa í da :
M é dia ( exemplo de /=) : 8.50

Código 3.13: Usando o operador /=

• //= (Divisão Inteira e Atribuição):


Realiza a divisão inteira da variável da esquerda pelo valor à direita e atribui o
resultado. x //= y é o mesmo que x = x // y.
1 # Usando //=
2 saldo_em_reais = 12.55
3 valor_item_reais = 1.00
4 # Quantos itens inteiros de R$1 ,00 podem ser comprados
5 q u a n t i d a d e _ i t e n s _ c o m p r a v e i s = int ( saldo_em_reais //
valor_item_reais )
6 print ( f " Pode comprar { q u a n t i d a d e _ i t e n s _ c o m p r a v e i s } itens
de R$ { valor_item_reais :.2 f }. " )
7 # Exemplo direto com //= ( menos comum neste cen á rio de
atualiza ç ã o direta )
8 numero_de_itens = 125
9 capacidade_caixa = 10
10 numero_de_itens //= capacidade_caixa # numero_de_itens
agora é o n ú mero de caixas cheias
11 print ( f " N ú mero de caixas cheias : { numero_de_itens } " )

Código 3.14: Usando o operador //=

• %= (Módulo e Atribuição):
Calcula o módulo da variável da esquerda pelo valor à direita e atribui o resto. x %=
y é o mesmo que x = x % y.
1 # Usando \%=
2 angulo_graus = 370
3 angulo_graus %= 360 # Normaliza o â ngulo para o intervalo
0 -359
4 print ( f " Â ngulo normalizado : { angulo_graus } ° " ) # Sa í da : Â
ngulo normalizado : 10 °
5
6 contador_ciclico = 5
7 limite_ciclo = 3
8 contador_ciclico %= limite_ciclo # Para o valor 5 ,
resultado é 2
9 print ( f " Valor do contador c í clico : { contador_ciclico } " ) #
Sa í da : Valor do contador c í clico : 2

Código 3.15: Usando o operador %=


3.3. Operadores de Atribuição: Guardando e Atualizando Valores 53

• **= (Exponenciação e Atribuição):


Eleva a variável da esquerda à potência do valor à direita e atribui o resultado. x
**= y é o mesmo que x = x ** y.
1 # Usando **=
2 numero_base = 2
3 numero_base **= 4 # Eleva 2 à quarta pot ê ncia
4 print ( f " 2 elevado a 4 é : { numero_base } " ) # Sa í da : 2
elevado a 4 é : 16
5

6 valor = 3
7 valor **= 3 # 3 ao cubo
8 print ( f " 3 ao cubo é : { valor } " ) # Sa í da : 3 ao cubo é : 27

Código 3.16: Usando o operador **=

Benefícios dos Operadores de Atribuição Compostos


• Concisisão: Deixam o código mais curto e direto ao ponto. x += 1 é mais rápido
de digitar e ler do que x = x + 1.

• Legibilidade: Em muitos casos, especialmente com nomes de variáveis mais longos,


eles podem tornar a intenção da operação mais clara.

• Potencial de Eficiência (Menor Relevância em Python): Em algumas lin-


guagens compiladas de baixo nível, usar esses operadores pode, às vezes, levar a
um código de máquina ligeiramente mais otimizado. Em Python, a diferença de
performance é geralmente insignificante, e o principal benefício é mesmo a clareza e
a concisão.

Exemplo Prático: Atualizando Estatísticas de um Personagem de


Jogo
Imagine que estamos controlando as estatísticas de um personagem em um jogo:
1 # Estat í sticas iniciais do personagem
2 pontuacao = 0
3 energia = 100
4 moedas = 50
5

6 print ( f " In í cio : Pontua ç ã o ={ pontuacao } , Energia ={ energia } ,


Moedas ={ moedas } " )
7
8 # O personagem encontra um item que d á pontos
9 pontos_item = 250
10 pontuacao += pontos_item # Adiciona os pontos do item
11 print ( f " Encontrou item ! Nova Pontua ç ã o : { pontuacao } " )
12
13 # O personagem usa uma habilidade especial que gasta energia
14 custo_habilidade = 30
15 energia -= custo_habilidade
16 print ( f " Usou habilidade ! Energia restante : { energia } " )
17
18 # O personagem vende itens e dobra suas moedas
19 fator_venda = 2
54 Capítulo 3. Operadores e Expressões

20 moedas *= fator_venda
21 print ( f " Vendeu itens ! Moedas agora : { moedas } " )

Código 3.17: Atualizando estatísticas de um personagem de jogo

Neste exemplo, os operadores de atribuição compostos tornam as atualizações das estatís-


ticas claras e concisas.
Dominar os operadores de atribuição, tanto o simples quanto os compostos, é essencial
para manipular efetivamente o estado das variáveis, que é o coração de como os programas
armazenam e transformam informações ao longo do tempo.

3.4 Operadores de Comparação (Relacionais): Verifi-


cando Relações entre Valores
Além de realizar cálculos aritméticos e atribuir valores, uma tarefa fundamental na
programação é comparar diferentes valores. Precisamos saber se um número é maior
que outro, se um nome é igual a um determinado texto, ou se uma condição específica
é verdadeira ou falsa. Para isso, utilizamos os operadores de comparação, também
conhecidos como operadores relacionais.
O resultado de uma operação de comparação é sempre um valor booleano: True
(Verdadeiro) ou False (Falso). Esses valores booleanos são a base para a tomada de
decisões em nossos programas, como veremos no Capítulo 4 ao estudarmos as estruturas
de controle condicional (como o if).
Vamos conhecer os operadores de comparação do Python:

• Igual a (==)
Verifica se dois operandos são iguais.
Atenção: Não confunda o operador de comparação == (dois sinais de igual) com o
operador de atribuição = (um sinal de igual)!

– = é usado para dar um valor a uma variável (ex: idade = 30).


– == é usado para perguntar se dois valores são iguais (ex: idade == 30).

1 # Exemplos com o operador " Igual a " (==)


2 x = 10
3 y = 5
4 z = 10
5
6 print ( f " x ({ x }) == y ({ y }) ? Resultado : { x == y } " ) #
Sa í da : x (10) == y (5) ? Resultado : False
7 print ( f " x ({ x }) == z ({ z }) ? Resultado : { x == z } " ) #
Sa í da : x (10) == z (10) ? Resultado : True
8
9 nome1 = " Ana "
10 nome2 = " Maria "
11 print ( f " ’{ nome1 } ’ == ’{ nome2 } ’? Resultado : { nome1 ==
nome2 } " ) # Sa í da : ’ Ana ’ == ’ Maria ’? Resultado : False
12 print ( f " ’{ nome1 } ’ == ’ Ana ’? Resultado : { nome1 == ’ Ana ’} " )
# Sa í da : ’ Ana ’ == ’ Ana ’? Resultado : True

Código 3.18: Exemplos com o operador de igualdade ==


3.4. Operadores de Comparação (Relacionais): Verificando Relações entre Valores 55

• Diferente de (!=)
Verifica se dois operandos são diferentes. É o oposto do ==.
1 # Exemplos com o operador " Diferente de " (!=)
2 x = 10
3 y = 5
4 z = 10
5
6 print ( f " x ({ x }) != y ({ y }) ? Resultado : { x != y } " ) #
Sa í da : x (10) != y (5) ? Resultado : True
7 print ( f " x ({ x }) != z ({ z }) ? Resultado : { x != z } " ) #
Sa í da : x (10) != z (10) ? Resultado : False
8
9 cor_favorita = " azul "
10 print ( f " A cor favorita é diferente de ’ verde ’?
{ cor_favorita != ’ verde ’} " ) # Sa í da : True

Código 3.19: Exemplos com o operador de diferença !=

• Maior que (>)


Verifica se o primeiro operando é maior que o segundo.
1 # Exemplos com o operador " Maior que " ( >)
2 idade_joao = 25
3 idade_maria = 30
4
5 print ( f " Idade de Jo ã o ({ idade_joao }) > Idade de Maria
({ idade_maria }) ? { idade_joao > idade_maria } " ) # Sa í da :
False
6 print ( f " 5 > 2? {5 > 2} " ) # Sa í da : True

Código 3.20: Exemplos com o operador maior que >

• Menor que (<)


Verifica se o primeiro operando é menor que o segundo.
1 # Exemplos com o operador " Menor que " ( <)
2 preco_a = 19.99
3 preco_b = 25.50
4
5 print ( f " Pre ç o A ({ preco_a }) < Pre ç o B ({ preco_b }) ?
{ preco_a < preco_b } " ) # Sa í da : True
6 print ( f " 10 < 10? {10 < 10} " ) # Sa í da : False (10 n ã o é
estritamente menor que 10)

Código 3.21: Exemplos com o operador menor que <

• Maior ou igual a (>=)


Verifica se o primeiro operando é maior ou igual ao segundo.
1 # Exemplos com o operador " Maior ou igual a " ( >=)
2 tempe ratura_a tual = 22.5
3 t em per at ur a_l im it e = 22.5
4
5 print ( f " Temperatura atual ({ t emperatu ra_atual }) >= Limite
({ t em per at ur a_l im it e }) ? { temper atura_at ual >=
te mp er atu ra _l imi te } " ) # Sa í da : True
6 print ( f " 100 >= 99? {100 >= 99} " ) # Sa í da : True
56 Capítulo 3. Operadores e Expressões

Código 3.22: Exemplos com o operador maior ou igual a >=

• Menor ou igual a (<=)


Verifica se o primeiro operando é menor ou igual ao segundo.
1 # Exemplos com o operador " Menor ou igual a " ( <=)
2 n i v e l _ a g u a _ r e s e r v a t o r i o _ m e t r o s = 5.7
3 n i v e l _ m i n i m o _ s e g u r a n c a _ m e t r o s = 6.0
4
5 print ( f " N í vel ({ n i v e l _ a g u a _ r e s e r v a t o r i o _ m e t r o s }) <= M í nimo
({ n i v e l _ m i n i m o _ s e g u r a n c a _ m e t r o s }) ?
{ n i v e l _ a g u a _ r e s e r v a t o r i o _ m e t r o s <=
n i v e l _ m i n i m o _ s e g u r a n c a _ m e t r o s } " ) # Sa í da : True
6 print ( f " 50 <= 50? {50 <= 50} " ) # Sa í da : True

Código 3.23: Exemplos com o operador menor ou igual a <=

Comparando Strings
Os operadores de comparação também podem ser usados com strings. Nesse caso, a
comparação é feita lexicograficamente (como em um dicionário), baseada na ordem dos
caracteres (geralmente seguindo a tabela Unicode/ASCII).

• Letras maiúsculas vêm antes das minúsculas (ex: "A" é menor que "a").

• A comparação é feita caractere por caractere da esquerda para a direita.

1 # Exemplos de compara ç ã o de strings


2 palavra1 = " banana "
3 palavra2 = " laranja "
4 palavra3 = " Banana " # ’B ’ mai ú sculo
5
6 print ( f " ’{ palavra1 } ’ < ’{ palavra2 } ’? { palavra1 < palavra2 } " ) #
Sa í da : True ( ’ b ’ vem antes de ’l ’)
7 print ( f " ’{ palavra1 } ’ == ’ banana ’? { palavra1 == ’ banana ’} " ) # Sa í da :
True
8 print ( f " ’{ palavra1 } ’ == ’{ palavra3 } ’? { palavra1 == palavra3 } " ) #
Sa í da : False ( Python é case - sensitive )
9 print ( f " ’{ palavra3 } ’ < ’{ palavra1 } ’? { palavra3 < palavra1 } " ) #
Sa í da : True ( ’ B ’ vem antes de ’b ’)
10 print ( f " ’ Abacaxi ’ < ’ Banana ’? { ’ Abacaxi ’ < ’ Banana ’} " ) # Sa í da :
True

Código 3.24: Exemplos de comparação de strings

A comparação de strings pode ser útil para ordenar listas de palavras ou para verificar se
uma string é igual a um valor esperado.
Os operadores de comparação são as ferramentas que nos permitem fazer perguntas
sobre nossos dados e obter respostas do tipo True ou False. Essas respostas, por sua vez,
direcionarão o fluxo de execução dos nossos programas, tornando-os inteligentes e capazes
de se adaptar a diferentes situações.
3.5. Operadores Lógicos (Booleanos): Combinando Condições 57

3.5 Operadores Lógicos (Booleanos): Combinando Con-


dições
Na seção anterior, aprendemos a usar os operadores de comparação (==, !=, >, <, >=,
<=) para fazer perguntas sobre nossos dados e obter respostas do tipo True ou False.
Essas respostas, chamadas de valores booleanos, são a base da tomada de decisão em
nossos programas.
Mas, e se precisarmos combinar várias condições? Por exemplo, “se um aluno tiver nota
maior ou igual a 7 E frequência maior ou igual a 75%”? Ou “se for sábado OU domingo”?
É aqui que entram os operadores lógicos. Eles nos permitem combinar ou modificar
expressões booleanas para criar condições mais complexas e sofisticadas.
Os principais operadores lógicos em Python são and, or e not.

O Operador and (E Lógico)


O operador and retorna True se, e somente se, ambas as expressões (ou operandos)
que ele conecta forem True. Se pelo menos uma delas for False, o resultado de A and B
será False.
Pense nele como um porteiro exigente: você só entra se atender a todos os requisitos.
Tabela Verdade para o and:

Expressão A Expressão B A and B


False False False
False True False
True False False
True True True

Exemplos Práticos:
1 # Exemplo 1: Pode dirigir ?
2 idade = 25
3 t e m _ c a r t e i r a _ m o t o r i s t a = True
4
5 # Para poder dirigir , a idade deve ser >= 18 E deve ter carteira
6 pode_dirigir = ( idade >= 18) and t e m _ c a r t e i r a _ m o t o r i s t a
7 print ( f " Tem { idade } anos e carteira ({ t e m _ c a r t e i r a _ m o t o r i s t a }) .
Pode dirigir ? { pode_dirigir } " )
8
9 idade_menor = 16
10 po de _di ri gi r_m en or = ( idade_menor >= 18) and t e m _ c a r t e i r a _ m o t o r i s t a
11 print ( f " Tem { idade_menor } anos e carteira
({ t e m _ c a r t e i r a _ m o t o r i s t a }) . Pode dirigir ? { p ode _d ir igi r_ me nor } " )
12
13 # Exemplo 2: Aprovado para um b ô nus
14 me ta _ve nd as _ba ti da = True
15 a v a l i a c a o _ d e s e m p e n h o _ b o a = False # Infelizmente , n ã o foi desta vez
na avalia ç ã o
16 recebe_bonus = me ta _ve nd as _b ati da and a v a l i a c a o _ d e s e m p e n h o _ b o a
17 print ( f " Recebe b ô nus ? { recebe_bonus } " )

Código 3.25: Exemplos do operador and


58 Capítulo 3. Operadores e Expressões

O Operador or (OU Lógico)


O operador or retorna True se pelo menos uma das expressões (ou operandos) que
ele conecta for True. Ele só retornará False se ambas as expressões forem False.
Pense nele como um porteiro mais flexível: você entra se atender a qualquer um dos
requisitos principais.
Tabela Verdade para o or:
Expressão A Expressão B A or B
False False False
False True True
True False True
True True True
Exemplos Práticos:
1 # Exemplo 1: Acesso a uma á rea VIP
2 tem_ingresso_vip = False
3 e _ c o n v i d a d o _ d a _ b a n d a = True
4
5 # Para entrar na á rea VIP , basta ser VIP OU ser convidado da banda
6 pode_entrar_vip = tem_ingresso_vip or e _ c o n v i d a d o _ d a _ b an d a
7 print ( f " Ingresso VIP ({ tem_ingresso_vip }) , Convidado
({ e _ c o n v i d ad o _ d a _ b a n d a }) . Pode entrar na á rea VIP ?
{ pode_entrar_vip } " )
8
9 # Exemplo 2: É fim de semana ?
10 dia_atual = " segunda "
11 e_fim_de_semana = ( dia_atual == " s á bado " ) or ( dia_atual ==
" domingo " )
12 print ( f " Hoje é { dia_atual }. É fim de semana ? { e_fim_de_semana } " )

Código 3.26: Exemplos do operador or

O Operador not (NÃO Lógico)


O operador not é um operador unário, o que significa que ele atua sobre um único
operando booleano. Sua função é simples: ele inverte o valor de verdade do operando. Se
o operando é True, not o torna False. Se o operando é False, not o torna True.
Tabela Verdade para o not:
Expressão A not A
False True
True False
Exemplos Práticos:
1 # Exemplo 1: Sensor de porta
2 porta_fechada = True
3 porta_aberta = not porta_fechada # Se a porta n ã o est á fechada ,
est á aberta
4 print ( f " A porta est á fechada ({ porta_fechada }) . A porta est á
aberta ? { porta_aberta } " )
5
6 # Exemplo 2: Verifica ç ã o de erro
7 o c o r r e u _ e r r o _ n o _ s i s t e m a = False
3.5. Operadores Lógicos (Booleanos): Combinando Condições 59

8 sistema_esta_ok = not o c o r r e u _ e r r o _ n o _ s i s t e m a
9 print ( f " Ocorreu erro ({ o c o r r e u _ e r r o _ n o _ s i s t e m a }) . Sistema est á OK ?
{ sistema_esta_ok } " )

Código 3.27: Exemplos do operador not

Combinando Operadores Lógicos


A verdadeira força dos operadores lógicos aparece quando os combinamos para criar
expressões lógicas mais complexas e que refletem regras de negócio ou condições do mundo
real. Assim como nas expressões aritméticas, podemos usar parênteses () para agrupar
subexpressões e garantir a ordem de avaliação desejada, ou simplesmente para tornar a
expressão mais clara.
1 # Exemplo : Verificando se um jovem pode participar de um evento
espec í fico
2 idade_jovem = 17
3 a u t o r i z a c a o _ d o s _ p a i s = True
4 evento_e_noturno = True
5
6 # Regra : Pode participar se ( idade >= 16 E tem autoriza ç ã o dos pais )
7 # OU se o evento N Ã O for noturno .
8 pode_participar = (( idade_jovem >= 16) and a u t o r i za c a o _ d o s _ p a i s ) or
( not evento_e_noturno )
9
10 print ( f " Jovem com { idade_jovem } anos , autoriza ç ã o
({ a u t o r i z a ca o _ d o s _ p a i s }) , evento noturno ({ evento_e_noturno }) .
Pode participar ? { pode_participar } " )

Código 3.28: Exemplo de combinação de operadores lógicos

Avaliação de Curto-Circuito (Short-Circuit Evaluation)


O Python é eficiente ao avaliar expressões com and e or. Ele usa uma técnica chamada
avaliação de curto-circuito:
• Para uma expressão A and B: Se o Python avalia A e descobre que A é False, ele
nem se preocupa em avaliar B, pois o resultado de A and B já será False de qualquer
maneira.

• Para uma expressão A or B: Se o Python avalia A e descobre que A é True, ele


nem se preocupa em avaliar B, pois o resultado de A or B já será True de qualquer
maneira.
Isso não apenas economiza um pouco de tempo de processamento, mas também pode ser
usado para evitar erros. Por exemplo, se você quisesse verificar se um divisor não é zero
antes de fazer uma divisão:
1 divisor = 0
2 numero = 10
3
4 # Se o divisor for zero , a segunda parte ( numero / divisor )
causaria um erro ( ZeroDiv isionErr or )
5 # Mas , como divisor != 0 é False , o ’ and ’ para e n ã o executa a
divis ã o .
60 Capítulo 3. Operadores e Expressões

6 p o d e _ d i v i d i r _ e _ m a i o r _ q u e _ u m = ( divisor != 0) and ( numero / divisor


> 1)
7 print ( f " Pode dividir e é maior que um ?
{ p o d e _ d i v i d i r _ e _ m a i o r _ q u e _ u m } " ) # Sa í da : False ( e sem erro !)

Código 3.29: Exemplo de curto-circuito para evitar erro


Não se preocupe em dominar o curto-circuito agora, mas é um comportamento interessante
e útil do Python.
Os operadores lógicos são as ferramentas que nos permitem construir a “inteligência”
dos nossos programas, definindo as condições sob as quais diferentes ações devem ser
tomadas. Eles são o coração da lógica de decisão, que exploraremos em detalhes no próximo
capítulo sobre estruturas de controle.

3.6 Operadores de Identidade (is, is not)


No Python, tudo com que trabalhamos — números, strings, listas (que veremos em
breve), etc. — é tratado como um objeto. Cada objeto que é criado na memória do
computador possui três características principais:
1. Uma identidade: Um número único que o identifica enquanto ele existir (pense
nisso como o “endereço” do objeto na memória).
2. Um tipo: Que define que tipo de dado ele é (ex: int, str, list).
3. Um valor: O conteúdo real que o objeto armazena.
Já vimos como o operador == compara os valores de dois objetos. Agora, vamos conhecer
os operadores de identidade, is e is not, que comparam as identidades dos objetos.
Ou seja, eles verificam se duas variáveis estão apontando para o exato mesmo objeto na
memória do computador.

O que é a Identidade de um Objeto? A Função id()


Para entender melhor o conceito de identidade, Python nos oferece a função embutida
id(). Quando aplicada a uma variável ou a um objeto, id() retorna um número inteiro
que é o identificador único daquele objeto durante seu tempo de vida. Dois objetos com o
mesmo id() são, de fato, o mesmo objeto.
1 # Exemplo com id ()
2 x = 100
3 y = x # y recebe a mesma refer ê ncia de x
4 z = 100 # z recebe o mesmo valor literal
5

6 print ( f " id de x : { id ( x ) } " )


7 print ( f " id de y : { id ( y ) } " ) # Deve ser igual ao id ( x )
8 print ( f " id de z : { id ( z ) } " ) # Pode ou n ã o ser igual a id ( x ) , devido
a otimiza ç õ es do Python

Código 3.30: Usando a função id() para ver a identidade de objetos


No exemplo acima, id(x) e id(y) serão iguais porque y foi instruído a apontar para o
mesmo objeto que x. O id(z) pode ser igual ao de x para números pequenos ou strings
literais idênticas, pois Python pode otimizar e reutilizar esses objetos imutáveis, mas isso
não é garantido para todos os casos ou tipos.
3.6. Operadores de Identidade (is, is not) 61

O Operador is
O operador is retorna True se os dois operandos (geralmente variáveis) referenciam o
mesmo objeto na memória (ou seja, se id(operando1) for igual a id(operando2)). Ele
retorna False caso contrário, mesmo que os valores dos objetos sejam idênticos.

O Operador is not
O operador is not faz o oposto: retorna True se os dois operandos referenciam objetos
diferentes na memória, e False se eles referenciam o mesmo objeto.

A Diferença Crucial: is versus ==


É fundamental não confundir is com ==:

• == (Igualdade de Valor): Compara se os valores dos objetos são iguais.

– Ex: [1, 2] == [1, 2] retorna True porque o conteúdo das duas listas é o
mesmo.

• is (Identidade de Objeto): Compara se as variáveis referenciam o mesmo objeto


na memória.

– Ex: Se lista1 = [1, 2] e lista2 = [1, 2], então lista1 is lista2 retor-
nará False, pois são duas listas diferentes que, por acaso, contêm os mesmos
valores. Mas se lista3 = lista1, então lista1 is lista3 retornará True.

Exemplos para Ilustrar:

• Com Tipos Imutáveis (como números e strings):


Python, por questões de otimização, pode fazer com que variáveis diferentes que
recebem o mesmo valor literal (especialmente para inteiros pequenos e strings curtas)
apontem para o mesmo objeto.
1 a = 256
2 b = 256
3 print ( f " a = { a } , b = { b } " )
4 print ( f " a == b : { a == b } " ) # Compara valores : True
5 print ( f " a is b : { a is b } " ) # Compara identidade : Pode
ser True ( otimiza ç ã o )
6
7 nome_curso1 = " Python Descomplicado "
8 nome_curso2 = " Python Descomplicado "
9 print ( f " nome_curso1 == nome_curso2 : { nome_curso1 ==
nome_curso2 } " ) # True
10 print ( f " nome_curso1 is nome_curso2 : { nome_curso1 is
nome_curso2 } " ) # Pode ser True ( otimiza ç ã o de strings )
11
12 # No entanto , se criados de formas diferentes ou com
valores maiores :
13 c = int (257)
14 d = int (257)
15 print ( f " c = { c } , d = { d } " )
16 print ( f " c == d : { c == d } " ) # True
17 print ( f " c is d : { c is d } " ) # Provavelmente False
62 Capítulo 3. Operadores e Expressões

18
19 str_a = " " . join ([ ’p ’ , ’y ’ , ’t ’ , ’h ’ , ’o ’ , ’n ’ ]) # Forma de
construir a string " python "
20 str_b = " python "
21 print ( f " str_a is str_b : { str_a is str_b } " ) #
Provavelmente False , pois str_a é constru í da
dinamicamente
22 print ( f " str_a == str_b : { str_a == str_b } " ) # True

Código 3.31: Comparando identidade com tipos imutáveis

Regra importante: Embora is possa retornar True para valores imutáveis idênticos
devido a otimizações internas do Python, você nunca deve usar is para comparar
valores de números ou strings. Para comparar valores, use sempre ==.

• Com Tipos Mutáveis (como listas):


Aqui a diferença entre is e == se torna ainda mais clara e importante. Quando você
cria dois objetos mutáveis (como listas) separadamente, eles sempre serão objetos
distintos na memória, mesmo que contenham os mesmos elementos.
1 lista_x = [10 , 20 , 30]
2 lista_y = [10 , 20 , 30] # Uma nova lista , com os mesmos
valores de lista_x
3 lista_z = lista_x # lista_z agora aponta para o MESMO
objeto que lista_x ( aliasing )
4
5 print ( f " lista_x : { lista_x } , id : { id ( lista_x ) } " )
6 print ( f " lista_y : { lista_y } , id : { id ( lista_y ) } " )
7 print ( f " lista_z : { lista_z } , id : { id ( lista_z ) } " )
8
9 # Comparando valores
10 print ( f " lista_x == lista_y : { lista_x == lista_y } " ) #
Sa í da : True
11 print ( f " lista_x == lista_z : { lista_x == lista_z } " ) #
Sa í da : True
12
13 # Comparando identidades
14 print ( f " lista_x is lista_y : { lista_x is lista_y } " ) #
Sa í da : False
15 print ( f " lista_x is lista_z : { lista_x is lista_z } " ) #
Sa í da : True
16
17 # Modificando lista_x ( que é o mesmo objeto que lista_z )
18 print ( " Modificando lista_x ... " )
19 lista_x . append (40) # Adiciona um elemento à lista_x
20
21 print ( f " Nova lista_x : { lista_x } " )
22 print ( f " Nova lista_z : { lista_z } " ) # lista_z reflete a
mudan ç a !
23 print ( f " lista_y continua : { lista_y } " ) # lista_y n ã o foi
afetada

Código 3.32: Comparando identidade com listas (tipos mutáveis)

Este exemplo com listas é crucial! Ele mostra que quando duas variáveis (lista_x e
lista_z) referenciam o mesmo objeto mutável, uma alteração feita através de uma
variável é visível através da outra.
3.7. Operadores de Associação (in, not in): Verificando Pertinência 63

Quando Usar is e is not?


1. Para comparar com None:
None é um objeto especial em Python que representa a ausência de valor. Só existe
uma instância de None no seu programa. Portanto, a maneira correta e idiomática
(o “jeito Pythonic”) de verificar se uma variável é None é usando is None ou is not
None.
1 r es ult ad o_ da_ bu sc a = None # Fun ç ã o n ã o encontrou o item
2
3 if re su lt ado _d a_ bus ca is None :
4 print ( " Nenhum resultado encontrado . " )
5
6 v a r i a v el _ c o n f i g u r a d a = " Algum valor " # Suponha que foi
definida
7 if va r i a v e l _ c o n f i g u r a d a is not None :
8 print ( " A vari á vel tem algum valor . " )

Código 3.33: Comparando com None usando is

Embora resultado_da_busca == None possa funcionar em muitos casos, is None


é mais rápido e o estilo preferido pela comunidade Python.

2. Para verificar se duas variáveis referenciam o mesmo objeto (Aliasing):


Como vimos no exemplo das listas, is é útil quando você precisa saber se está
lidando com o exato mesmo objeto na memória, especialmente com objetos mutáveis
onde alterações em um “apelido” (alias) afetam o objeto original.

Em Resumo
• Use == e != para comparar os valores dos objetos. Esta é a forma mais comum de
comparação.

• Use is e is not para comparar as identidades dos objetos (ou seja, se são o mesmo
objeto na memória). Seu uso mais comum é para checar contra None.

Entender a diferença entre igualdade de valor (==) e identidade de objeto (is) ajuda a
evitar bugs sutis e a escrever um código Python mais preciso, especialmente quando você
começar a trabalhar com objetos mais complexos e tipos de dados mutáveis.

3.7 Operadores de Associação (in, not in): Verificando


Pertinência
Muitas vezes, em nossos programas, precisamos verificar se um determinado valor ou
item está presente dentro de um conjunto maior de itens, como uma lista de nomes, uma
frase, ou qualquer outra coleção de dados. Para realizar essa verificação de “pertinência”
ou “associação”, o Python nos oferece dois operadores bem intuitivos: in e not in.
Assim como os operadores de comparação e identidade, o resultado de uma operação
de associação é sempre um valor booleano: True ou False.
64 Capítulo 3. Operadores e Expressões

O Operador in
O operador in verifica se o operando da esquerda (o item que estamos procurando) é
encontrado dentro do operando da direita (que geralmente é uma sequência como uma
string, uma lista, uma tupla, ou uma coleção como um conjunto ou as chaves de um
dicionário).

• Se o item for encontrado, in retorna True.

• Se o item não for encontrado, in retorna False.

O Operador not in
O operador not in faz exatamente o oposto:

• Se o item não for encontrado na sequência/coleção, not in retorna True.

• Se o item for encontrado, not in retorna False.

Usando in e not in com Strings


Um dos usos mais diretos e fáceis de entender dos operadores de associação é com
strings. Podemos usá-los para verificar se uma string menor (uma substring) está contida
dentro de uma string maior. É importante lembrar que essa verificação é sensível a
maiúsculas e minúsculas (case-sensitive).
1 # Exemplos com operadores de associa ç ã o em strings
2 frase_principal = " Python é uma linguagem de programa ç ã o muito
vers á til e divertida ! "
3
4 # Verificando com ’ in ’
5 tem_python = " Python " in frase_principal # " Python " est á na frase
6 print ( f " A frase cont é m ’ Python ’? { tem_python } " ) # Sa í da : True
7
8 tem_java = " Java " in frase_principal # " Java " n ã o est á na frase
9 print ( f " A frase cont é m ’ Java ’? { tem_java } " ) # Sa í da : False
10
11 # Verificando com ’ not in ’
12 nao_tem_facil = " f á cil " not in frase_principal # " f á cil " ( com
acento e min ú scula ) n ã o est á na frase
13 print ( f " A frase N Ã O cont é m ’f á cil ’? { nao_tem_facil } " ) # Sa í da : True
14

15 # Demonstra ç ã o de case - sensitive


16 t e m _ p y t h o n _ m i n u s c u lo = " python " in frase_principal # " python "
( min ú sculo )
17 print ( f " A frase cont é m ’ python ’ ( min ú sculo ) ?
{ t e m _ p y t h o n _ m i n u s c u l o } " ) # Sa í da : False
18

19 letra_a_presente = ’a ’ in frase_principal # Verifica se a letra ’a ’


est á presente
20 print ( f " A letra ’a ’ est á presente na frase ? { letra_a_presente } " ) #
Sa í da : True

Código 3.34: Operadores de associação com strings


3.7. Operadores de Associação (in, not in): Verificando Pertinência 65

Usando com Outras Sequências (como Listas - uma prévia)


Embora ainda não tenhamos estudado listas em detalhes (isso virá no Capítulo 6), é
bom saber que os operadores in e not in são extremamente úteis com elas e com outras
estruturas de dados que armazenam coleções de itens.
Uma lista em Python é uma coleção ordenada de itens, que podem ser de tipos diferentes.
Vamos ver um exemplo simples:
1 # Exemplo com uma lista de n ú meros
2 numeros_s orteados = [7 , 13 , 22 , 36 , 49] # Uma lista de n ú meros
inteiros
3
4 m e u_ num e ro _ d a_ s or t e = 22
5 outro_numero = 10
6
7 # Verificando com ’ in ’
8 ganhei_na _loteria = m eu _ n um e ro _ da _ s or t e in num eros_sor teados
9 print ( f " O n ú mero { m e u_ n um e ro _ d a_ s or t e } est á entre os sorteados ?
{ ganhei_ na_loteri a } " )
10

11 # Verificando com ’ not in ’


12 n a o _ g a n h e i _ c o m _ o u t ro = outro_numero not in nume ros_sort eados
13 print ( f " O n ú mero { outro_numero } N Ã O est á entre os sorteados ?
{ nao_ganhei_com_outro }")
14
15 # Exemplo com uma lista de strings
16 c or e s_p r im a r ia s _l u z = [ " vermelho " , " verde " , " azul " ]
17 cor_teste = " amarelo "
18
19 e_primaria_luz = cor_teste in c o re s _ pr i ma r ia s _ lu z
20 print ( f " A cor ’{ cor_teste } ’ é uma cor prim á ria da luz ?
{ e_primaria_luz } " )

Código 3.35: Operadores de associação com listas (prévia)

Como você pode ver, a lógica é a mesma: verificar se um item específico faz parte da
coleção. Você usará in e not in extensivamente quando trabalhar com listas, tuplas,
conjuntos e para verificar se uma chave existe em um dicionário.

A Importância dos Operadores de Associação


Os operadores in e not in são ferramentas muito expressivas e são frequentemente
usados em:

• Estruturas condicionais (if): Para tomar decisões baseadas na presença ou


ausência de um item.
1 email_usuario = input ( " Digite seu email : " )
2 if " @ " not in email_usuario :
3 print ( " Aten ç ã o : formato de email inv á lido ! " )

Código 3.36: Uso de not in em um if (prévia)

• Laços de repetição (for): O laço for em Python usa in de uma maneira muito
natural para iterar (percorrer) sobre os itens de uma sequência. Veremos isso em
detalhes no Capítulo 5.
66 Capítulo 3. Operadores e Expressões

1 # Apenas uma pequena pr é via , n ã o se preocupe em entender


tudo agora
2 for letra in " Python " :
3 print ( letra ) # Ir á imprimir P , y , t , h , o , n , cada um em
uma linha

Código 3.37: Uso de in em um laço for (prévia)

Em resumo, os operadores de associação in e not in nos fornecem uma maneira clara


e concisa de perguntar se algo faz parte de um grupo maior, tornando nosso código mais
legível e nossa lógica mais direta.

3.8 Precedência de Operadores: Definindo a Ordem das


Coisas
Até agora, vimos diversos tipos de operadores: aritméticos, de atribuição, de compara-
ção, lógicos, de identidade e de associação. Muitas vezes, em nossos programas, vamos
construir expressões que utilizam vários desses operadores juntos em uma única linha. Por
exemplo:
resultado = 10 + 5 * 2 - 1
condicao_valida = idade >= 18 and possui_documento
Quando o Python encontra uma expressão assim, como ele decide qual operação realizar
primeiro? Ele faz a multiplicação antes da adição? O and lógico é avaliado antes ou depois
da comparação >=?
A resposta está nas regras de precedência de operadores. Assim como na matemá-
tica, onde aprendemos que a multiplicação e a divisão vêm antes da adição e subtração
(lembra do PEMDAS/BODMAS?), as linguagens de programação também têm uma
hierarquia que define a ordem em que os operadores são avaliados.

O que é Precedência de Operadores?


A precedência de operadores é um conjunto de regras que determina a prioridade com
que os diferentes tipos de operadores são executados em uma expressão. Operadores com
maior precedência são avaliados antes daqueles com menor precedência.

Uma Visão Geral da Precedência em Python (Simplificada)


A tabela completa de precedência de operadores em Python pode ser bastante detalhada.
No entanto, para os operadores que vimos até agora e para a maioria das necessidades do
dia a dia, podemos resumir a ordem geral (do mais alto para o mais baixo precedente):
1. () (Parênteses): Têm a maior precedência de todas. Expressões dentro de parên-
teses são sempre avaliadas primeiro.
2. ** (Exponenciação): Avaliado da direita para a esquerda.
3. +x, -x (Operadores unários de positivo e negativo).
4. *, /, //, % (Multiplicação, Divisão, Divisão Inteira, Módulo): Geralmente
avaliados da esquerda para a direita.
3.8. Precedência de Operadores: Definindo a Ordem das Coisas 67

5. +, - (Adição e Subtração): Geralmente avaliados da esquerda para a direita.


6. Operadores de Comparação, Identidade e Associação:
• in, not in (Associação)
• is, is not (Identidade)
• <, <=, >, >=, !=, == (Comparação)
Estes geralmente têm níveis de precedência similares e são avaliados da esquerda
para a direita.
7. not (NÃO Lógico).
8. and (E Lógico).
9. or (OU Lógico).
(Nota: A associatividade, que é a ordem para operadores de mesma precedência, como da
esquerda para a direita para * e /, ou da direita para a esquerda para **, é um detalhe
adicional. Focar na ordem entre os grupos acima é o principal.)

A Regra de Ouro: Use Parênteses () para Clareza (e Segurança)!


Mesmo que você decore a tabela de precedência, a prática mais recomendada, especial-
mente para iniciantes (e até para programadores experientes!), é usar parênteses para
agrupar explicitamente as subexpressões e deixar clara a ordem de avaliação
desejada.
Usar parênteses oferece duas grandes vantagens:
1. Evita Erros: Você não precisa se preocupar em lembrar todas as regras de prece-
dência. Os parênteses garantem que a expressão será calculada na ordem que você
intenciona.
2. Melhora a Legibilidade: Expressões com parênteses são muito mais fáceis de ler
e entender, tanto para você no futuro quanto para outras pessoas que possam ler
seu código. Não há ambiguidade.
Lembre-se: “Na dúvida, ou para clareza, use parênteses!”

Exemplos Práticos Demonstrando a Precedência


• Exemplo 1: Aritmético
1 # Sem par ê nteses , a multiplica ç ã o (*) tem maior
preced ê ncia que a adi ç ã o (+)
2 resultado1 = 10 + 5 * 2
3 # Python calcula : 5 * 2 = 10 , depois 10 + 10 = 20
4 print ( f " 10 + 5 * 2 = { resultado1 } " ) # Sa í da : 20
5
6 # Com par ê nteses , for ç amos a adi ç ã o primeiro
7 resultado2 = (10 + 5) * 2
8 # Python calcula : 10 + 5 = 15 , depois 15 * 2 = 30
9 print ( f " (10 + 5) * 2 = { resultado2 } " ) # Sa í da : 30

Código 3.38: Precedência em expressões aritméticas


68 Capítulo 3. Operadores e Expressões

• Exemplo 2: Combinando Operadores Aritméticos e de Comparação


1 a = 5
2 b = 3
3 c = 2
4 d = 4
5
6 # Forma clara e segura usando par ê nteses :
7 condicao = ( a + b ) > ( c * d )
8 # Python calcula : ( a + b ) = (5 + 3) = 8
9 # Python calcula : ( c * d ) = (2 * 4) = 8
10 # Python compara : 8 > 8 , que é False
11 print ( f " ({ a } + { b }) > ({ c } * { d }) = { condicao } " ) # Sa í da :
(5 + 3) > (2 * 4) = False

Código 3.39: Precedência com aritméticos e comparação

• Exemplo 3: Combinando Operadores de Comparação e Lógicos


Os operadores lógicos (not, and, or) têm precedência menor que os de comparação.
1 idade = 25
2 possui_diploma = True
3 experiencia_anos = 3
4
5 # Queremos verificar se : ( idade > 22 E possui_diploma ) OU
experiencia_anos > 5
6 # O ’ and ’ é avaliado antes do ’ or ’ por padr ã o .
7 q u a l i f i c a d o _ s e m _ p a r e n t e s e s _ e x t r a s = idade > 22 and
possui_diploma or experiencia_anos > 5
8 print ( f " Qualificado ( sem par ê nteses extras ) ?
{ qualificado_sem_parenteses_extras }")
9
10 # Com par ê nteses para clareza ( e para garantir a inten ç ã o )
11 q u a l i f i c a d o _ c o m _ p a r e n t e s e s = (( idade > 22) and
possui_diploma ) or ( experiencia_anos > 5)
12 print ( f " Qualificado ( com par ê nteses extras ) ?
{ qualificado_com_parenteses }")
13 # Ambas as sa í das ser ã o : True , neste caso espec í fico .

Código 3.40: Precedência com comparação e lógicos

Neste caso, o resultado foi o mesmo, mas a versão com parênteses extras é muito mais
fácil de entender à primeira vista. Se a lógica fosse diferente, por exemplo, idade
> 22 and (possui_diploma or experiencia_anos > 5), os parênteses seriam es-
senciais para mudar a ordem de avaliação do and e or.

Entender a precedência dos operadores é importante para ler e escrever corretamente


expressões complexas. No entanto, a melhor prática é sempre usar parênteses para tornar
suas intenções explícitas e seu código mais robusto e legível.

3.9 Expressões: Combinando Tudo para Obter Resulta-


dos
Ao longo deste capítulo, exploramos o fascinante mundo dos operadores em Python.
Vimos como os operadores aritméticos nos permitem fazer cálculos, os de atribuição nos
3.9. Expressões: Combinando Tudo para Obter Resultados 69

ajudam a armazenar e atualizar valores, os de comparação nos deixam verificar relações, e


os lógicos, de identidade e associação nos dão o poder de construir condições e verificações
mais complexas.
Agora, vamos dar um passo atrás e olhar para o quadro geral: como todos esses
elementos se unem para formar expressões, que são, em essência, as “frases” que o Python
avalia para produzir resultados e realizar trabalho útil.

A Anatomia de uma Expressão: Uma Breve Revisão


Lembre-se, uma expressão em Python é qualquer combinação de:

• Operandos: Estes são os dados sobre os quais as operações são realizadas. Podem ser
valores literais (como 10, 3.14, "Olá", True), variáveis (como idade, nome_usuario,
preco_total), ou até mesmo o resultado de outras expressões ou chamadas de
funções.

• Operadores: São os símbolos ou palavras-chave (como +, *, >=, and, is) que


especificam a operação a ser executada.

O ponto fundamental é que toda expressão em Python, quando avaliada pelo


interpretador, resulta em um único valor. E esse valor resultante também terá um
tipo (por exemplo, a expressão 5 + 3 resulta no valor 8, que é do tipo int).

Como o Python Avalia Expressões: Um Olhar Passo a Passo Mental


Quando o Python encontra uma expressão, especialmente uma que contém múltiplos
operadores, ele segue um processo metódico para chegar ao resultado final. Esse processo
é governado pelas regras de precedência de operadores (que vimos na Seção 3.8) e pelo
uso de parênteses () para agrupar subexpressões.
Vamos imaginar como o Python “desmontaria” uma expressão um pouco mais elaborada.
Considere o seguinte:
1 # Exemplo de express ã o para avalia ç ã o
2 a = 10
3 b = 3
4 c = 2
5 status_ok = True
6

7 # Express ã o complexa :
8 # resultado_final = ( a + b * c - 7 >= 12) and ( c ** 2 < b or not
status_ok )
9
10 # An á lise mental da avalia ç ã o :
11 # 1. Dentro do primeiro par ê ntese ( a + b * c - 7 >= 12) :
12 # a . Multiplica ç ã o : b * c = > 3 * 2 = 6
13 # b . Adi ç ã o : a + 6 = > 10 + 6 = 16
14 # c . Subtra ç ã o : 16 - 7 = > 9
15 # d . Compara ç ã o : 9 >= 12 = > False ( primeira parte do ’ and ’)
16
17 # 2. Dentro do segundo par ê ntese ( c ** 2 < b or not status_ok ) :
18 # a . Exponencia ç ã o : c ** 2 = > 2 ** 2 = 4
19 # b . Operador not : not status_ok = > not True = False
20 # c . Compara ç ã o : 4 < b = > 4 < 3 = False
70 Capítulo 3. Operadores e Expressões

21 # d . Operador or : False or False = > False ( segunda parte do


’ and ’)
22
23 # 3. Finalmente , o operador ’ and ’ principal :
24 # a . False and False = > False
25

26 # Portanto , resultado_final ser á False


27 resultado_final = ( a + b * c - 7 >= 12) and ( c ** 2 < b or not
status_ok )
28 print ( f " O resultado final da express ã o é : { resultado_final } " )
29 # Sa í da : O resultado final da express ã o é : False

Código 3.41: Exemplo de expressão para avaliação passo a passo


Entender essa avaliação passo a passo, respeitando a precedência e os parênteses, é crucial
para prever o comportamento do seu código e para depurar expressões que não estão
produzindo o resultado esperado.

Onde Usamos Expressões? Em Quase Todo Lugar!


As expressões não são apenas para cálculos isolados; elas são a espinha dorsal de muitas
construções em Python:
• No lado direito de uma atribuição:
area_circulo = 3.14159 * raio ** 2
• Como condições em estruturas de controle (que veremos no próximo capítulo):
if (idade_cliente >= 18) and (possui_credito_aprovado):
• Como argumentos passados para funções:
print(f"A soma é: {numero1 + numero2}")
resultado_raiz = calcular_raiz_quadrada(valor_x / 2)
• Dentro de laços de repetição para controlar iterações ou calcular valores (Capítulo
5).
• Em instruções de retorno de funções (Capítulo 10):
return subtotal_compra + taxa_entrega - desconto_aplicado
Basicamente, sempre que você precisa que o Python “calcule” ou “determine” um valor,
você estará usando uma expressão.

Dicas para Escrever Boas Expressões


1. Clareza em Primeiro Lugar: Como já enfatizamos, use parênteses () para tornar
a ordem das operações explícita, mesmo que as regras de precedência já dessem o
resultado esperado. Seu “eu do futuro” (e outros colegas) agradecerão.
Compare: x * y + z / 2 vs. (x * y) + (z / 2). A segunda é muito mais
fácil de ler.
2. Não Complique Demais em Uma Única Linha: Se uma expressão se tornar
excessivamente longa ou tiver muitas partes aninhadas, ela pode ficar difícil de
entender e depurar. Nesses casos, é uma boa prática quebrá-la em várias etapas,
usando variáveis intermediárias para armazenar resultados parciais. Isso melhora a
legibilidade e facilita a depuração.
3.9. Expressões: Combinando Tudo para Obter Resultados 71

1 # Suponha uma express ã o complexa original ( fict í cia ) :


2 # resultado_final = ( x * y / z + ( a - b ) **2) > 100 and not
erro_detectado
3
4 # Quebrando em partes para maior clareza :
5 # ( Valores de x , y , z , a , b , erro_detectado seriam
definidos antes )
6 # x , y , z , a , b , erro_detectado = 10 , 2 , 5 , 7 , 4 , False #
Exemplo
7
8 termo_multiplicativo_divisivo = x * y / z
9 d i f e r e n c a _ a o _ q u a d r a d o = ( a - b ) **2
10 soma_dos_termos = t e r m o _ m u l t i p l i c a t i v o _ d i v i s i v o +
diferenca_ao_quadrado
11

12 c o n d i c a o _ p r i n c i p a l _ m a i o r _ q u e _ 1 0 0 = soma_dos_termos > 100


13 resultado_final_legivel = condicao_principal_maior_que_100
and not erro_detectado
14
15 # print ( f " Resultado leg í vel : { r e s u l t a d o _ f i n a l _ l e g i v e l }")

Código 3.42: Quebrando uma expressão complexa em partes

Embora o segundo exemplo tenha mais linhas, cada passo é mais simples e o fluxo
lógico é mais fácil de acompanhar e verificar.

3. Use Nomes de Variáveis Significativos: Se suas variáveis têm nomes claros


(ex: preco_unitario, quantidade_em_estoque), as expressões que as utilizam se
tornam quase autoexplicativas.

Concluindo o Capítulo sobre Operadores e Expressões


Neste capítulo, desvendamos uma parte crucial da programação em Python: os ope-
radores e como eles são usados para construir expressões. Aprendemos sobre operadores
aritméticos para cálculos, de atribuição para definir e atualizar variáveis, de comparação
para verificar relações, lógicos para combinar condições, de identidade para checar se
objetos são os mesmos, e de associação para verificar pertinência. Também vimos a
importância da precedência de operadores e, acima de tudo, do uso de parênteses para
garantir clareza e correção.
As expressões são a forma como “conversamos com o computador” para que ele realize
cálculos, tome decisões e manipule os dados da maneira que precisamos. Com essa
base, estamos agora muito bem preparados para o próximo grande passo: aprender como
controlar o fluxo de execução dos nossos programas usando estruturas de decisão e repetição,
que é o tema do nosso próximo capítulo!
Capítulo 4

Estruturas de Controle Condicional

Nos capítulos anteriores, construímos uma base sólida: aprendemos a armazenar dados
em variáveis, conhecemos os tipos de dados fundamentais do Python e exploramos como
usar operadores para formar expressões que calculam novos valores. Nossos programas,
até agora, seguem uma execução linear, ou seja, as instruções são executadas uma após a
outra, na ordem em que aparecem.
No entanto, para criar programas verdadeiramente úteis e inteligentes, precisamos
de mais do que apenas uma sequência fixa de passos. Precisamos que nossos programas
possam:

• Tomar decisões: Avaliar certas condições e escolher qual caminho seguir, qual
conjunto de instruções executar.

• Repetir tarefas: Executar um bloco de código múltiplas vezes, seja um número


fixo de vezes ou enquanto uma condição específica for atendida.

É aqui que entram as estruturas de controle de fluxo. Elas são comandos especiais da
linguagem Python que nos permitem alterar a ordem sequencial padrão de execução das
instruções, dando aos nossos programas a capacidade de serem dinâmicos e reativos.

4.1 Introdução às Estruturas de Controle: Dando Inte-


ligência aos Programas
Imagine que você está dando instruções a um robô para preparar seu café. Uma
instrução simples seria “pegue a xícara”. Mas e se a xícara estiver suja? Você precisaria
adicionar uma decisão: “SE a xícara estiver suja, ENTÃO lave-a, SENÃO, pegue a xícara”.
Essa lógica de “SE...ENTÃO...SENÃO...” é o cerne do controle condicional.
Ou, se você quisesse que o robô colocasse três colheres de açúcar, você poderia dizer
“repita 3 vezes: coloque uma colher de açúcar”. Essa é a base do controle de repetição.
As estruturas de controle de fluxo são, portanto, os mecanismos que permitem que
nossos programas:

• Selecionem diferentes blocos de código para execução com base em condições


(Estruturas Condicionais ou de Seleção).

• Repitam a execução de blocos de código (Estruturas de Repetição ou Laços/Loops).

72
4.2. A Estrutura if (Se): Tomando Decisões Simples 73

Neste capítulo, nosso foco será nas Estruturas de Controle Condicional. Aprenderemos
como usar os comandos if, elif e else do Python para embutir lógica de decisão em
nossos programas, permitindo que eles se comportem de maneiras diferentes dependendo
dos valores dos dados que manipulam ou das entradas fornecidas pelo usuário.
No Capítulo 5, exploraremos as estruturas de repetição. Dominar as estruturas de
controle é um passo fundamental para se tornar um programador proficiente, pois são elas
que transformam simples sequências de comandos em algoritmos flexíveis e capazes de
resolver problemas complexos do mundo real de forma “descomplicada”. Vamos começar!

4.2 A Estrutura if (Se): Tomando Decisões Simples


A estrutura condicional mais básica e fundamental em Python (e em muitas outras
linguagens de programação) é o comando if. A palavra “if”, em inglês, significa “se”. E é
exatamente isso que essa estrutura faz: ela permite que um determinado bloco de código
seja executado se, e somente se, uma condição específica for verdadeira.
Pense no if como um porteiro que verifica sua credencial: se a credencial for válida
(True), você pode passar e acessar uma área (o bloco de código). Se não for válida (False),
você simplesmente segue em frente, ignorando aquela área.

Sintaxe da Estrutura if
A forma geral de um comando if em Python é:

if condicao:
# Bloco de código a ser executado
# se a ’condicao’ for Verdadeira (True).
# Instrução 1 pertencente ao if
# Instrução 2 pertencente ao if
# ... mais instruções ...
# Próxima instrução fora do bloco if (executada independentemente da condição)

Vamos analisar cada parte:

1. if: É a palavra-chave que inicia a estrutura condicional.

2. condicao: Esta é uma expressão que o Python avalia. O resultado dessa avaliação
deve ser um valor booleano (True ou False). Frequentemente, esta condição usará os
operadores de comparação (ex: idade >= 18, nota == 10) e/ou operadores lógicos
(ex: usuario_valido and senha_correta) que vimos no Capítulo 3.

3. Dois Pontos (:): Após a condicao, é obrigatório colocar dois pontos. Eles
sinalizam ao Python que o que vem a seguir é um bloco de código associado a esta
condição if.

4. Bloco de Código Indentado: As linhas de código que devem ser executadas se a


condicao for True formam um bloco. Em Python, os blocos de código são definidos
pela indentação (recuo).

• A Indentação é Crucial em Python! Diferentemente de muitas linguagens


que usam chaves {} ou palavras como begin e end para delimitar blocos, Python
74 Capítulo 4. Estruturas de Controle Condicional

usa o recuo do texto. Todas as instruções que pertencem ao mesmo bloco devem
ter o mesmo nível de indentação.
• Convenção: A convenção universal em Python é usar 4 espaços para cada
nível de indentação. A maioria dos editores de código e IDEs pode ser confi-
gurada para inserir 4 espaços automaticamente quando você pressiona a tecla
Tab. É fundamental ser consistente com a indentação, pois erros de indentação
(IndentationError) são comuns para iniciantes e impedem a execução do
programa.

Como Funciona o if
O fluxo de execução é simples:

1. O Python avalia a condicao especificada após a palavra if.

2. Se o resultado da condicao for True, o Python executa todas as instruções que estão
no bloco de código indentado logo abaixo do if.

3. Se o resultado da condicao for False, o Python ignora completamente o bloco de


código indentado e pula diretamente para a primeira instrução que estiver fora desse
bloco (ou seja, a próxima instrução que não estiver indentada ou que tiver o mesmo
nível de indentação do if).

Exemplos Práticos
• Exemplo 1: Verificando se um número é positivo
1 # Solicitando um n ú mero ao usu á rio
2 numero_str = input ( " Digite um n ú mero qualquer : " )
3 numero = float ( numero_str ) # Convertendo para float para
aceitar decimais
4
5 # Verificando se o n ú mero é positivo
6 if numero > 0:
7 print ( f " O n ú mero { numero } é positivo . " )
8 print ( " Essa mensagem s ó aparece para n ú meros
positivos ! " )
9
10 print ( " A verifica ç ã o de n ú mero positivo foi conclu í da . " ) #
Esta linha sempre ser á executada

Código 4.1: Verificando se um número é positivo

Se o usuário digitar 15, a condição numero > 0 (ou 15 > 0) será True. As duas linhas
indentadas dentro do if serão executadas, e depois a linha final. Se o usuário digitar
-5, a condição numero > 0 (ou -5 > 0) será False. As duas linhas indentadas serão
ignoradas, e apenas a linha final será executada. Se o usuário digitar 0, a condição 0
> 0 também é False, e as linhas do if serão puladas.

• Exemplo 2: Verificando a maioridade


Este exemplo mostra um bloco if com múltiplas instruções indentadas.
4.2. A Estrutura if (Se): Tomando Decisões Simples 75

1 idade_str = input ( " Por favor , digite sua idade : " )


2 idade = int ( idade_str ) # Convertendo para inteiro
3

4 if idade >= 18:


5 print ( " Voc ê é maior de idade . " )
6 print ( " Isso significa que voc ê tem certas
respo nsabilid ades legais . " )
7 print ( " E tamb é m alguns privil é gios , como poder votar
( se for cidad ã o ) . " )
8
9 print ( f " Obrigado por informar sua idade : { idade } anos . " ) #
Esta linha executa sempre

Código 4.2: Verificando a maioridade

Se idade for 18 ou mais, todas as três mensagens dentro do if serão exibidas. Caso
contrário, elas serão puladas.

• Exemplo 3: Verificação simples de uma senha (apenas ilustrativo)


1 senha_armazenada = " Python É Legal123 " # Senha " correta "
2
3 senha_digitada = input ( " Digite sua senha de acesso : " )
4
5 if senha_digitada == senha_armazenada :
6 print ( " Acesso concedido ! " )
7 print ( " Bem - vindo ( a ) ao sistema . " )
8 # Aqui poderiam vir outras a ç õ es permitidas ap ó s o login
9
10 print ( " --- Tentativa de login finalizada ---" ) # Executa
sempre

Código 4.3: Verificação simples de senha

Neste caso, se a senha digitada não for exatamente igual à senha_armazenada, nada
dentro do bloco if acontece, e o programa apenas informa que a tentativa de login
foi finalizada. Veremos mais adiante como dar um feedback ao usuário caso a senha
esteja errada (usando else).

O que acontece se a condição for False?


Como já mencionado, se a condição do if avaliar para False, todo o bloco de código
indentado associado a ele é simplesmente pulado. A execução do programa continua na
primeira linha de código que estiver no mesmo nível de indentação do if (ou seja, que
não faz parte do bloco if). Se não houver mais código após o bloco if, o programa
simplesmente termina.
A estrutura if simples é o primeiro e mais importante passo para adicionar lógica de
decisão aos seus programas. Ela permite que seu código comece a reagir de forma diferente
a diferentes entradas ou estados, tornando-o muito mais dinâmico e útil. Nas próximas
seções, veremos como expandir essa capacidade com as estruturas else e elif.
76 Capítulo 4. Estruturas de Controle Condicional

4.3 A Estrutura if-else (Se-Senão): Escolhendo entre


Duas Alternativas
Na seção anterior, vimos como a estrutura if nos permite executar um bloco de código
somente se uma condição for verdadeira. Mas e se quisermos que o programa faça algo
diferente caso a condição seja falsa? É aí que entra a estrutura if-else.
A combinação if-else nos dá uma forma de escolher entre dois caminhos de
execução: um caminho se a condição for True, e um caminho alternativo se a condição
for False. O programa sempre seguirá um desses dois caminhos, nunca ambos e nunca
nenhum (assumindo que a estrutura if-else seja alcançada).

Sintaxe da Estrutura if-else


A forma geral de um comando if-else em Python é:
1 if condicao :
2 # Bloco de c ó digo do if
3 # Este bloco é executado se a ’ condicao ’ for Verdadeira ( True ) .
4 # Instru ç ã o 1 A
5 # Instru ç ã o 2 A
6 else :
7 # Bloco de c ó digo do else
8 # Este bloco é executado se a ’ condicao ’ do if for Falsa
( False ) .
9 # Instru ç ã o 1 B
10 # Instru ç ã o 2 B
11 # Pr ó xima instru ç ã o fora do bloco if - else

Código 4.4: Estrutura básica do comando if-else

Vamos detalhar:

1. if condicao:: Funciona exatamente como na estrutura if simples que já vimos. A


condicao é avaliada. Se for True, o bloco de código indentado logo abaixo do if é
executado.

2. else:: Esta palavra-chave introduz o bloco de código alternativo. Ela deve estar no
mesmo nível de indentação que o if correspondente e também é seguida por dois
pontos (:).

3. Bloco de Código Indentado do else: As instruções que devem ser executadas


se a condicao do if for False formam este segundo bloco, que também deve ser
indentado (com 4 espaços, seguindo a convenção).

Como Funciona o if-else


O fluxo de execução é o seguinte:

1. O Python avalia a condicao da cláusula if.

2. Se a condicao for True:

• O bloco de código indentado sob o if é executado.


4.3. A Estrutura if-else (Se-Senão): Escolhendo entre Duas Alternativas 77

• O bloco de código indentado sob o else é ignorado.


3. Se a condicao for False:
• O bloco de código indentado sob o if é ignorado.
• O bloco de código indentado sob o else é executado.
4. Após a execução de um dos blocos (seja do if ou do else), o programa continua
com a primeira instrução que estiver fora e após toda a estrutura if-else.

Exemplos Práticos
• Exemplo 1: Verificando se um número é positivo ou não positivo
1 numero_str = input ( " Digite um n ú mero : " )
2 numero = float ( numero_str )
3
4 if numero > 0:
5 print ( f " O n ú mero { numero } é positivo . " )
6 else :
7 print ( f " O n ú mero { numero } n ã o é positivo ( ele pode ser
zero ou negativo ) . " )
8
9 print ( " Verifica ç ã o completa . " ) # Esta linha sempre ser á
executada

Código 4.5: Verificando se um número é positivo ou não

Neste caso, se o usuário digitar 7, a primeira mensagem será exibida. Se digitar -3


ou 0, a segunda mensagem (do bloco else) será exibida.
• Exemplo 2: Aprovado ou Reprovado
Vamos determinar a situação de um aluno com base na nota e frequência.
1 nota_str = input ( " Digite a nota final do aluno (0 a 10) : " )
2 nota = float ( nota_str )
3
4 frequencia_str = input ( " Digite a frequ ê ncia do aluno (0.0
a 1.0 , ex : 0.75 para 75%) : " )
5 frequencia = float ( frequencia_str )
6
7 # Crit é rios para aprova ç ã o
8 n o t a _ m i n i m a _ a p r o v a c a o = 6.0
9 f r e q u e n c i a _ m i n i m a _ a p r o v a c a o = 0.75
10
11 if nota >= n o t a _ m i n i m a _ a p r o v a c a o and frequencia >=
frequencia_minima_aprovacao :
12 situacao_aluno = " Aprovado ( a ) "
13 print ( " Parab é ns pela sua aprova ç ã o ! " )
14 else :
15 situacao_aluno = " Reprovado ( a ) "
16 print ( " Infelizmente n ã o foi desta vez . Dedique - se mais
aos estudos ! " )
17
18 print ( f " A situa ç ã o final do ( a ) aluno ( a ) é :
{ situacao_aluno }. " )

Código 4.6: Verificando aprovação de aluno com if-else


78 Capítulo 4. Estruturas de Controle Condicional

Aqui, o programa analisa se ambas as condições (nota e frequência) são satisfeitas. Se


sim, executa o bloco do if; caso contrário (se uma ou ambas as condições falharem),
executa o bloco do else.

• Exemplo 3: Verificação de senha com feedback para falha


Agora, nosso exemplo de senha pode informar ao usuário se a tentativa falhou.
1 s e n h a _ c o r r e t a _ s i s t e m a = " Python É Legal123 " # Nossa senha
" super secreta "
2
3 s e n h a _ d i g i t a d a _ u s u a r i o = input ( " Digite sua senha para
acesso : " )
4
5 if s e n h a _ d i g i t a d a _ u s u a r i o == s e n h a _ c o r r e t a _ s i s t e m a :
6 print ( " Acesso concedido ! " )
7 print ( " Bem - vindo ( a ) ao painel de controle . " )
8 # Aqui poderiam vir as funcionalidades do sistema
9 else :
10 print ( " Senha incorreta . " )
11 print ( " Acesso negado . Verifique sua senha e tente
novamente . " )
12
13 print ( " --- Valida ç ã o de acesso finalizada ---" ) # Sempre
executa

Código 4.7: Verificação de senha com if-else

Com o else, o usuário recebe um feedback claro, independentemente do resultado


da tentativa de login.

Fluxograma de um if-else (Representação Visual)


Se fôssemos desenhar um fluxograma para uma estrutura if-else, teríamos:

1. Um losango representando a condicao.

2. Duas setas saindo do losango:

• Uma seta (geralmente rotulada “Sim” ou “True”) levando ao bloco de código do


if.
• Outra seta (rotulada “Não” ou “False”) levando ao bloco de código do else.

3. Após a execução do bloco if ou do bloco else, ambas as setas de fluxo convergiriam


para o ponto onde o código continua após a estrutura if-else.

A Figura 4.1 ilustra visualmente que exatamente um dos dois caminhos será tomado.
A estrutura if-else é uma ferramenta poderosa para criar programas que respondem
de forma diferente a diversas situações. Ela nos permite lidar com dois cenários possíveis
baseados em uma única condição. Mas e se tivermos mais de duas possibilidades? É o que
veremos na próxima seção com a estrutura if-elif-else.
4.3. A Estrutura if-else (Se-Senão): Escolhendo entre Duas Alternativas 79

Figura 4.1: Fluxograma representando a estrutura condicional if-else, mostrando a


decisão e os dois possíveis caminhos de execução.
80 Capítulo 4. Estruturas de Controle Condicional

4.4 A Estrutura if-elif-else (Se-Senão Se-Senão): Múl-


tiplas Condições
Já vimos como o if simples nos permite executar um código se uma condição for
verdadeira, e como o if-else nos dá duas alternativas (uma para True e outra para
False). Mas e quando temos várias condições diferentes que queremos testar, e para cada
uma delas, queremos executar um bloco de código específico? É aqui que a estrutura
if-elif-else se torna extremamente útil.
A palavra-chave elif é uma contração de “else if” (“senão se”, em português). Ela nos
permite encadear múltiplas verificações condicionais de forma organizada.

Sintaxe da Estrutura if-elif-else


A forma geral desta estrutura é:
1 if pr imeira_c ondicao :
2 # Bloco de c ó digo executado se a primeir a_condic ao for True
3 # ...
4 elif segunda_condicao :
5 # Bloco de c ó digo executado se prime ira_cond icao for False
6 # E ( AND ) segunda_condicao for True
7 # ...
8 elif ter ceira_co ndicao :
9 # Bloco de c ó digo executado se as duas condi ç õ es anteriores
forem False
10 # E ( AND ) ter ceira_con dicao for True
11 # ...
12 # Voc ê pode ter quantos blocos ’ elif ’ forem necess á rios .
13 else : # O bloco ’ else ’ é opcional
14 # Bloco de c ó digo executado se NENHUMA das condi ç õ es anteriores
15 # ( do if e de todos os elifs ) for True .
16 # ...
17 # C ó digo que continua ap ó s toda a estrutura if - elif - else

Código 4.8: Estrutura básica do comando if-elif-else

Vamos analisar os componentes:

1. if primeira_condicao:: Começa como uma estrutura if normal. Se primeira_condicao


for True, seu bloco é executado e toda a estrutura elif-else restante é ignorada.

2. elif alguma_outra_condicao:: Se a primeira_condicao (e qualquer elif ante-


rior) for False, o Python então avalia a alguma_outra_condicao deste elif. Se
ela for True, o bloco de código associado a este elif é executado, e o restante da
estrutura (outros elifs e o else final) é ignorado. Você pode ter quantos elifs
precisar.

3. else: (Opcional): Se todas as condições do if e de todos os elifs anteriores


forem False, o bloco de código dentro do else (se ele existir) será executado. Se
não houver um else e todas as condições falharem, nenhum dos blocos condicionais
será executado.

Lembre-se que cada bloco de código (if, elif, else) deve ser corretamente indentado.
4.4. A Estrutura if-elif-else (Se-Senão Se-Senão): Múltiplas Condições 81

Como Funciona o if-elif-else


A execução ocorre na seguinte ordem:
1. A primeira_condicao do if é testada.

• Se for True, seu bloco é executado, e o Python salta para o final de toda a
estrutura if-elif-else.

2. Se a primeira_condicao for False, a segunda_condicao do primeiro elif é tes-


tada.

• Se for True, seu bloco é executado, e o Python salta para o final.

3. Este processo continua para cada elif subsequente: a condição só é testada se todas
as anteriores (do if e dos elifs precedentes) foram falsas.

4. Se todas as condições do if e de todos os elifs forem False:

• Se houver um bloco else, ele é executado.


• Se não houver else, nada dentro da estrutura if-elif-else é executado, e o
programa continua após ela.

Importante: Em uma cadeia if-elif-else, no máximo um bloco de código será


executado – aquele correspondente à primeira condição (de cima para baixo) que for
encontrada como verdadeira.

Exemplos Práticos
• Exemplo 1: Classificando uma nota escolar
Vamos atribuir um conceito (A, B, C, D, F) com base na nota de um aluno.
1 nota_str = input ( " Digite a nota do aluno (0 a 10) : " )
2 nota = float ( nota_str )
3

4 conceito_final = " " # Vari á vel para armazenar o conceito


5
6 if nota >= 9.0 and nota <= 10.0: # Verifica tamb é m se a
nota é v á lida no topo
7 conceito_final = " A ( Excelente ) "
8 elif nota >= 7.5: # S ó é verificado se nota < 9.0
9 conceito_final = " B ( Muito Bom ) "
10 elif nota >= 6.0: # S ó é verificado se nota < 7.5
11 conceito_final = " C ( Satisfat ó rio ) "
12 elif nota >= 4.0: # S ó é verificado se nota < 6.0
13 conceito_final = " D ( Recupera ç ã o ) "
14 elif nota >= 0.0: # S ó é verificado se nota < 4.0
15 conceito_final = " F ( Reprovado ) "
16 else : # Se a nota for menor que 0 ou maior que 10
( considerando a primeira condi ç ã o )
17 conceito_final = " Nota inv á lida ! "
18
19 print ( f " A nota do aluno foi { nota } e seu conceito é :
{ conceito_final } " )

Código 4.9: Classificando notas com if-elif-else


82 Capítulo 4. Estruturas de Controle Condicional

Neste exemplo, se a nota for 9.5, o primeiro if é True, conceito_final se torna “A


(Excelente)”, e todos os elifs e o else são ignorados. Se a nota for 7.0, o primeiro
if é False, o primeiro elif (nota >= 7.5) é False, mas o segundo elif (nota
>= 6.0) é True, então conceito_final se torna “C (Satisfatório)”, e o restante é
ignorado.

• Exemplo 2: Determinando a faixa etária de uma pessoa


1 idade_str = input ( " Digite a idade da pessoa : " )
2 idade = int ( idade_str )
3
4 fa i xa _ et a r ia _ pe s s oa = " "
5
6 if idade < 0:
7 f ai x a_ e t ar i a_ p e ss o a = " Idade Inv á lida "
8 elif idade <= 2: # Se n ã o for < 0 , testa se é <= 2
9 f ai x a_ e t ar i a_ p e ss o a = " Beb ê "
10 elif idade <= 12: # Se n ã o for <= 2 , testa se é <= 12
11 f ai x a_ e t ar i a_ p es s o a = " Crian ç a "
12 elif idade <= 17: # E assim por diante ...
13 f ai x a_ e t ar i a_ p es s o a = " Adolescente "
14 elif idade <= 59:
15 f ai x a_ e t ar i a_ p es s o a = " Adulto ( a ) "
16 else : # Se idade for >= 60 ( e n ã o negativa )
17 f ai x a_ e t ar i a_ p es s o a = " Idoso ( a ) "
18

19 print ( f " Com { idade } anos , a pessoa é classificada como :


{ f a ix a _e t a ri a _p e s so a }. " )

Código 4.10: Determinando faixa etária com if-elif-else

A ordem dos elifs aqui é importante. Começamos com as idades menores e vamos
subindo.

• Exemplo 3: Menu de opções simples


1 print ( " Op ç õ es do Menu : " )
2 print ( " 1 - Ver Saldo " )
3 print ( " 2 - Fazer Dep ó sito " )
4 print ( " 3 - Fazer Saque " )
5 print ( " 4 - Sair " )
6

7 opcao_str = input ( " Digite o n ú mero da op ç ã o desejada : " )


8 opcao = int ( opcao_str )
9
10 if opcao == 1:
11 print ( " Voc ê escolheu : Ver Saldo . " )
12 # Aqui viria o c ó digo para mostrar o saldo
13 elif opcao == 2:
14 print ( " Voc ê escolheu : Fazer Dep ó sito . " )
15 # C ó digo para dep ó sito
16 elif opcao == 3:
17 print ( " Voc ê escolheu : Fazer Saque . " )
18 # C ó digo para saque
19 elif opcao == 4:
20 print ( " Saindo do sistema . Obrigado ! " )
21 else :
4.4. A Estrutura if-elif-else (Se-Senão Se-Senão): Múltiplas Condições 83

22 print ( " Op ç ã o inv á lida ! Por favor , escolha um n ú mero


entre 1 e 4. " )

Código 4.11: Menu de opções com if-elif-else

Este processo de avaliação sequencial e excludente pode ser visualizado no fluxograma


da Figura 4.2.

Figura 4.2: Fluxograma da estrutura condicional if-elif-else, demonstrando a avaliação


sequencial das condições e os múltiplos caminhos de execução.

A Opcionalidade do else
É importante reforçar que o bloco else ao final de uma cadeia if-elif-...-elif é
completamente opcional. Se você o omitir, e nenhuma das condições do if ou dos elifs
84 Capítulo 4. Estruturas de Controle Condicional

for verdadeira, simplesmente nenhum dos blocos condicionais será executado, e o programa
continuará após a estrutura.
A estrutura if-elif-else é uma ferramenta extremamente poderosa e flexível para
construir lógicas de decisão com múltiplos caminhos em seus programas. Ela permite que
você crie programas que respondem de forma muito mais granular e específica a diferentes
entradas e situações.

4.5 Estruturas Condicionais Aninhadas: Decisões Den-


tro de Decisões
Até agora, vimos como usar if, if-else e if-elif-else para tomar decisões baseadas
em uma ou mais condições. Mas e se, após uma decisão ser tomada, precisarmos tomar
outra decisão que depende do resultado da primeira? É aqui que entram as estruturas
condicionais aninhadas.
“Aninhar”, neste contexto, significa simplesmente colocar uma estrutura if (ou if-else,
ou if-elif-else) dentro de um dos blocos de código de outra estrutura if, elif ou else
externa. Isso nos permite criar lógicas de decisão mais granulares e hierárquicas.

O que são Estruturas Condicionais Aninhadas?


A ideia é simples: se uma condição externa é verdadeira (ou falsa, dependendo de como
você estrutura), um bloco de código é executado. Dentro desse bloco, você pode ter outra
estrutura if completa para testar subcondições.
Veja uma estrutura genérica:
1 if c o n d i c a o _ e x t e r n a _ p r i n c i p a l :
2 # C ó digo executado se c o n d i c a o _ e x t e r n a _ p r i n c i p a l for True
3 print ( " Condi ç ã o externa principal é verdadeira . " )
4
5 if co nd ic ao_ in te rna _A :
6 # C ó digo executado se c on dic ao _i nte rn a_ A for True
7 print ( " Condi ç ã o interna A tamb é m é verdadeira . " )
8 elif co nd ica o_ in ter na _B :
9 # C ó digo executado se c on dic ao _i nte rn a_ A for False ,
10 # mas c ond ic ao _in te rn a_B for True
11 print ( " Condi ç ã o interna A é falsa , mas a B é verdadeira . " )
12 else :
13 # C ó digo executado se ambas as condi ç õ es internas A e B
forem False
14 print ( " Nenhuma das condi ç õ es internas ( A ou B ) é
verdadeira . " )
15
16 print ( " De volta ao bloco da condi ç ã o externa principal . " )
17 else :
18 # C ó digo executado se c o n d i c a o _ e x t e r n a _ p r i n c i p a l for False
19 print ( " Condi ç ã o externa principal é falsa . " )
20
21 print ( " Fim da estrutura aninhada . " )

Código 4.12: Estrutura genérica de if aninhado


O ponto mais crucial ao trabalhar com estruturas aninhadas em Python é a indentação.
Cada novo nível de aninhamento requer um novo nível de indentação (geralmente mais
4.5. Estruturas Condicionais Aninhadas: Decisões Dentro de Decisões 85

4 espaços). É a indentação que diz ao Python qual bloco de código pertence a qual if,
elif ou else.

Quando Usar o Aninhamento?


O aninhamento é útil quando:

• Uma decisão subsequente só faz sentido ou só deve ser considerada se uma decisão
anterior teve um resultado específico.

• Você precisa verificar uma hierarquia de condições, onde subgrupos de regras se


aplicam dependendo de uma condição mais geral.

Exemplos Práticos
• Exemplo 1: Verificação de acesso a um sistema com múltiplos níveis
Imagine um sistema onde primeiro se verifica o login e senha, e depois, se o login for
bem-sucedido, verifica-se o nível de permissão do usuário.
1 # Simula ç ã o de dados do sistema
2 u su ari o_ ca das tr ad o = " ana . silva "
3 senha_cadastrada = " segredo123 "
4 n i v e l _ ac e s s o _ u s u a r i o = " editor " # Pode ser " leitor " ,
" editor " ou " admin "
5
6 # Solicita dados ao usu á rio
7 usuar io_tenta tiva = input ( " Digite seu usu á rio : " )
8 senha_tentativa = input ( " Digite sua senha : " )
9

10 # Primeira verifica ç ã o : login e senha


11 if usuario _tentativ a == us ua rio _c ad ast ra do and
senha_tentativa == senha_cadastrada :
12 print ( f " Login bem - sucedido ! Bem - vindo ( a ) ,
{ usuario_ tentativ a }. " )
13

14 # Segunda verifica ç ã o ( aninhada ) : n í vel de acesso


15 if n i v e l _ a c e ss o _ u s u a r i o == " admin " :
16 print ( " Voc ê tem acesso total de Administrador . " )
17 # Aqui poderiam estar as funcionalidades de admin
18 elif n i v e l _ a ce s s o _ u s u a r i o == " editor " :
19 print ( " Voc ê tem acesso de Editor . Pode criar e
modificar conte ú do . " )
20 # Funcionalidades de editor
21 elif n i v e l _ a ce s s o _ u s u a r i o == " leitor " :
22 print ( " Voc ê tem acesso de Leitor . Pode apenas
visualizar o conte ú do . " )
23 # Funcionalidades de leitor
24 else :
25 print ( " N í vel de acesso desconhecido . Contate o
suporte . " )
26 else : # Se a primeira condi ç ã o ( login e senha ) for falsa
27 print ( " Usu á rio ou senha incorretos . Acesso negado . " )
28

29 print ( " --- Fim da tentativa de acesso ---" )

Código 4.13: Sistema de login com permissões aninhadas


86 Capítulo 4. Estruturas de Controle Condicional

• Exemplo 2: Desconto em uma compra baseado no tipo de cliente e valor


Um cliente VIP tem regras de desconto diferentes de um cliente comum.

1 valor_compra = float ( input ( " Digite o valor total da


compra : R$ " ) )
2 tipo_cliente_str = input ( " O cliente é VIP ? ( digite ’ sim ’
ou ’ nao ’) : " ) . lower ()
3
4 pe r ce n tu a l _d e sc o n to = 0.0 # Inicializa o desconto
5
6 if tipo_cliente_str == " sim " :
7 print ( " Cliente é VIP . Verificando regras de desconto
VIP ... " )
8 if valor_compra > 500.0:
9 p e rc e n tu a l_ d e sc o nt o = 0.15 # 15% para compras VIP
altas
10 print ( f " Aplicando desconto VIP de
{ p e rc e nt u a l_ d es c o nt o *100:.0 f }%. " )
11 elif valor_compra > 100.0:
12 p e rc e n tu a l_ d es c o nt o = 0.10 # 10% para compras VIP
m é dias
13 print ( f " Aplicando desconto VIP de
{ p e rc e nt u a l_ d es c o nt o *100:.0 f }%. " )
14 else :
15 p e rc e n tu a l_ d es c o nt o = 0.05 # 5% para todas as
compras VIP ( valor baixo )
16 print ( f " Aplicando desconto VIP de
{ p e rc e nt u a l_ d es c o nt o *100:.0 f }%. " )
17 elif tipo_cliente_str == " nao " :
18 print ( " Cliente n ã o é VIP . Verificando regras de
desconto padr ã o ... " )
19 if valor_compra > 1000.0:
20 p e rc e n tu a l_ d es c o nt o = 0.07 # 7% para compras
n ão - VIP muito altas
21 print ( f " Aplicando desconto de
{ p e rc e nt u a l_ d es c o nt o *100:.0 f }%. " )
22 elif valor_compra > 300.0:
23 p e rc e n tu a l_ d es c o nt o = 0.03 # 3% para compras
n ão - VIP m é dias
24 print ( f " Aplicando desconto de
{ p e rc e nt u a l_ d es c o nt o *100:.0 f }%. " )
25 else :
26 print ( " Nenhum desconto padr ã o aplic á vel para este
valor de compra . " )
27 else :
28 print ( " Tipo de cliente inv á lido . Nenhum desconto ser á
aplicado . " )
29

30 valor_a_pagar = valor_compra * (1 - p e rc e nt u a l_ d es c o nt o )
31 print ( f " Valor final com desconto : R$ { valor_a_pagar :.2 f } " )

Código 4.14: Cálculo de desconto com ifs aninhados

A Figura 4.3 ilustra um exemplo de como uma estrutura if pode ser aninhada dentro
de outra, permitindo uma lógica de decisão mais detalhada.
4.5. Estruturas Condicionais Aninhadas: Decisões Dentro de Decisões 87

Figura 4.3: Fluxograma exemplificando uma estrutura if aninhada, onde uma condição
interna é avaliada após uma condição externa ser satisfeita.
88 Capítulo 4. Estruturas de Controle Condicional

Cuidados com o Aninhamento Excessivo


Embora as estruturas condicionais aninhadas sejam poderosas, é preciso ter cuidado
para não exagerar. Muitos níveis de aninhamento (um if dentro de um if, que está
dentro de outro if, e assim por diante) podem tornar o código:
• Difícil de ler e entender: A indentação excessiva cria uma “pirâmide” de código
que pode ser visualmente confusa.
• Difícil de depurar: Rastrear a lógica através de muitos níveis aninhados pode ser
complicado.
• Propenso a erros: É mais fácil cometer erros lógicos ou de indentação.
Se você se deparar com um código que está ficando com muitos níveis de aninhamento,
considere algumas alternativas:
1. Combinar Condições com Operadores Lógicos: Muitas vezes, um if aninhado
pode ser simplificado. Por exemplo:

# Aninhado:
if condicao1:
if condicao2:
# Faça algo
pass # ’pass’ é uma instrução que não faz nada

# Pode ser reescrito como:


if condicao1 and condicao2:
# Faça algo
pass

2. Usar elif para Achatar a Estrutura: Se as condições aninhadas forem mutua-


mente excludentes.
3. Dividir em Funções (Antecipando o Capítulo 10): Se um bloco if ou else
contém uma lógica complexa com mais ifs aninhados, essa lógica interna pode ser
extraída para uma função separada.
4. Cláusulas de Guarda (“Guard Clauses” ou “Early Exit”): Usar ifs para
tratar casos especiais ou de erro no início de uma função ou bloco e sair cedo (usando
return em funções, por exemplo), simplificando o fluxo principal.
O aninhamento é uma ferramenta válida e necessária para certas lógicas hierárquicas, mas
use-o com bom senso, sempre priorizando a clareza e a legibilidade do seu código.

4.6 Múltiplos ifs Independentes vs. if-elif-else: Qual


Usar?
Ao começar a trabalhar com estruturas condicionais, uma dúvida comum surge:
“Quando devo usar vários comandos if separados, e quando devo optar por uma es-
trutura if-elif-else?” Embora ambos envolvam a palavra-chave if, eles se comportam
4.6. Múltiplos ifs Independentes vs. if-elif-else: Qual Usar? 89

de maneiras fundamentalmente diferentes e são usados para cenários lógicos distintos.


Entender essa diferença é um passo importante para evitar erros conceituais e escrever um
código que faça exatamente o que você espera.

Múltiplos ifs Independentes: Verificando Condições Separadas


Quando você escreve uma sequência de comandos if separados, cada um deles é tratado
como uma decisão independente. O Python avaliará a condição de cada if um após o
outro, independentemente do resultado dos ifs anteriores.

• Como Funciona:
1. O primeiro if é avaliado. Se sua condição for True, seu bloco de código é
executado.
2. Em seguida, o segundo if é avaliado. Se sua condição for True, seu bloco de
código é executado.
3. Isso continua para todos os ifs subsequentes na sequência.
• Consequência Principal: É possível que múltiplos blocos if sejam executados
se suas respectivas condições forem verdadeiras. Também é possível que nenhum
seja executado, ou apenas alguns.
• Quando Usar: Use múltiplos ifs independentes quando você tem várias condições
distintas e quer que uma ação específica seja tomada para cada condição que for
verdadeira, sem que uma afete a verificação da outra. É útil quando as condições não
são mutuamente excludentes ou quando você quer acumular resultados de diferentes
verificações.

Exemplo Prático: Verificando características para um bônus em um jogo


Imagine que um jogador pode receber bônus por diferentes conquistas, e essas conquistas
são independentes.
1 # Exemplo com m ú ltiplos if ’s independentes
2 pontos_jogador = 1500
3 tem_item_raro = True
4 c o m p l e t o u _ m i s s a o _ s e c r e t a = True
5 b o n u s _ t o t a l _ c a l c u l a d o = 0 # Come ç a com zero b ô nus
6
7 print ( " Calculando b ô nus do jogador ... " )
8
9 if pontos_jogador > 1000:
10 print ( " - Conquista : Pontua ç ã o Elevada ! (+100 de b ô nus ) " )
11 bo n u s _ t o t a l _ c a l c u l a d o += 100
12
13 if tem_item_raro :
14 print ( " - Conquista : Possui Item Raro ! (+50 de b ô nus ) " )
15 bo n u s _ t o t a l _ c a l c u l a d o += 50
16
17 if c o m p l e t o u _ m i s s a o _ s e c r e t a :
18 print ( " - Conquista : Miss ã o Secreta Conclu í da ! (+200 de b ô nus ) " )
19 bo n u s _ t o t a l _ c a l c u l a d o += 200
20
21 print ( f " B ô nus total final do jogador : { b o n u s _ t o t a l _ c a l c u l a d o }
pontos . " )
90 Capítulo 4. Estruturas de Controle Condicional

Código 4.15: Exemplo com múltiplos ifs independentes para bônus


Neste exemplo, se o jogador tiver mais de 1000 pontos, possuir o item raro E tiver
completado a missão secreta, todos os três blocos if serão executados, e todos os bônus
serão somados.

Estrutura if-elif-else: Escolhendo um ÚNICO Caminho entre


Vários
Como vimos na Seção 4.4, a estrutura if-elif-else é projetada para lidar com um
conjunto de condições mutuamente excludentes, onde você quer que apenas um bloco
de código seja executado entre várias alternativas.

• Como Funciona (Revisão): O Python avalia as condições na ordem em que apare-


cem (if, depois o primeiro elif, etc.). Assim que uma condição é encontrada como
True, o bloco de código associado a ela é executado, e toda a restante estrutura
if-elif-else é ignorada (pulada). Se nenhuma condição for verdadeira, o bloco
else (se presente) é executado.

• Consequência Principal: **No máximo um bloco** de código dentro de toda a


cadeia if-elif-else será executado.

• Quando Usar: Use if-elif-else quando você precisa escolher uma única ação ou
atribuir um único valor com base em qual de várias condições possíveis é a primeira
a ser satisfeita.

Exemplo Prático: Classificando um número


1 # Exemplo com if - elif - else ( condi ç õ es mutuamente exclusivas para um
ú nico resultado )
2 numero = int ( input ( " Digite um n ú mero inteiro : " ) )
3 classificacao = " "
4
5 if numero > 0:
6 classificacao = " Positivo "
7 elif numero < 0: # S ó é verificado se ’ numero > 0 ’ for False
8 classificacao = " Negativo "
9 else : # S ó é executado se ’ numero > 0 ’ for False E ’ numero < 0 ’ for
False
10 classificacao = " Zero "
11
12 print ( f " O n ú mero { numero } é classificado como : { classificacao }. " )

Código 4.16: Exemplo com if-elif-else para classificação


Aqui, o número será classificado como “Positivo”, “Negativo” ou “Zero”, mas nunca como
mais de uma dessas coisas ao mesmo tempo.
O que aconteceria se usássemos múltiplos ifs soltos (incorretamente) aqui?
1 # Exemplo INCORRETO para classifica ç ã o de notas usando m ú ltiplos
if ’s soltos
2 nota_teste = 95
3 c o n c e i t o _ f i n a l _ e r r a d o = " " # Inicializa a vari á vel
4 if nota_teste >= 70: # Para nota 95 , isso é True
4.6. Múltiplos ifs Independentes vs. if-elif-else: Qual Usar? 91

5 co n c e i t o _ f i n a l _ e r r a d o = " C "
6 if nota_teste >= 80: # Para nota 95 , isso tamb é m é True !
Sobrescreve para " B "
7 co n c e i t o _ f i n a l _ e r r a d o = " B "
8 if nota_teste >= 90: # Para nota 95 , isso tamb é m é True !
Sobrescreve para " A "
9 co n c e i t o _ f i n a l _ e r r a d o = " A "
10 # Se n ã o houvesse um if para >= 90 , e a nota fosse 95 , o conceito
final seria " B ".
11 # Se a ordem dos if ’s fosse invertida ( ex : if >= 70 , if >= 80 , if
>=90) ,
12 # o resultado para uma nota 95 seria ’A ’ ( correto por acaso , devido
à ú ltima sobrescrita ) .
13 # Mas se a ordem fosse if >= 90 , if >= 80 , if >= 70 , uma nota 95
resultaria em ’C ’!
14 print ( f " Conceito ( calculado de forma potencialmente errada para
nota { nota_teste }) : { c o n c e i t o _ f i n a l _ e r r a d o } " )

Código 4.17: Exemplo INCORRETO de classificação com múltiplos ifs

Resumo da Diferença em Pontos Chave

Característica Múltiplos ifs Indepen- Estrutura if-elif-else


dentes
Avaliação das Condições Cada if é sempre avaliado. Condições são avaliadas em
ordem até uma ser True.
Execução de Blocos Múltiplos blocos podem ser No máximo um bloco é exe-
executados. cutado.
Exclusividade Mútua Condições não precisam ser Ideal para condições mutu-
mutuamente excludentes. amente excludentes.
Uso Típico Checar várias característi- Escolher uma opção entre
cas ou aplicar múltiplos bô- várias alternativas.
nus/penalidades indepen-
dentes.

Tabela 4.1: Comparativo entre múltiplos ifs e a estrutura if-elif-else.

Uma Dica para Lembrar


• Pense em múltiplos ifs como uma lista de verificação (checklist): você passa
por cada item da lista e marca (executa a ação) se ele se aplica. Vários itens podem
ser marcados.
• Pense na estrutura if-elif-else como um caminho com várias bifurcações:
você escolhe a primeira bifurcação que te serve e segue por ela, ignorando todas as
outras bifurcações daquele ponto em diante.

A escolha entre usar múltiplos ifs ou uma estrutura if-elif-else depende inteira-
mente da lógica do problema que você está tentando resolver. Compreender a diferença
no comportamento de execução é fundamental para garantir que seu programa tome as
decisões corretas e evite bugs lógicos.
92 Capítulo 4. Estruturas de Controle Condicional

A distinção fundamental no comportamento de execução – onde múltiplos ifs indepen-


dentes testam todas as condições e a estrutura if-elif-else seleciona um único caminho –
pode ser mais facilmente compreendida visualmente. A Figura 4.4 apresenta um fluxo-
grama comparativo, ilustrando lado a lado como o Python avalia as condições e executa
os blocos de código em cada um desses cenários.

Figura 4.4: Comparação visual do fluxo de execução: à esquerda, o comportamento de


múltiplos ifs independentes, onde cada condição é testada; à direita, o fluxo de uma
estrutura if-elif-else, onde apenas um bloco é executado.

4.7 O Operador Condicional Ternário (Expressão Con-


dicional)
Já vimos como as estruturas if, if-else e if-elif-else nos permitem controlar
o fluxo de nossos programas e executar blocos de código diferentes com base em certas
condições. Python, sendo uma linguagem que preza pela concisão e legibilidade (quando
possível), oferece uma maneira mais curta de expressar uma decisão simples do tipo
if-else em uma única linha. Essa construção é conhecida como expressão condicional
4.7. O Operador Condicional Ternário (Expressão Condicional) 93

ou, mais comumente, operador condicional ternário (ternário porque envolve três
operandos/partes).
Ele é especialmente útil quando você quer atribuir um valor a uma variável com base
em uma condição, ou quando precisa escolher entre dois valores simples para usar em uma
expressão maior.

Sintaxe da Expressão Condicional


A sintaxe da expressão condicional em Python é a seguinte:
valor_se_verdadeiro if condicao else valor_se_falso
Vamos entender cada parte:
1. condicao: É a expressão booleana que será testada (exatamente como no comando
if normal).
2. valor_se_verdadeiro: É o valor que toda a expressão condicional resultará se a
condicao for avaliada como True.
3. valor_se_falso: É o valor que toda a expressão condicional resultará se a condicao
for avaliada como False.
4. As palavras-chave if e else são usadas para separar as três partes.
O mais importante é que esta construção é uma expressão, o que significa que ela sempre
avalia para um valor. Esse valor pode então ser atribuído a uma variável, passado como
argumento para uma função, etc.

Comparando com a Estrutura if-else Tradicional


Una estrutura if-else tradicional que atribui um valor a uma variável, como:
1 if condicao :
2 variavel = valor_X
3 else :
4 variavel = valor_Y

Pode ser reescrita de forma mais concisa usando o operador ternário:


1 variavel = valor_X if condicao else valor_Y

Ambas as formas produzem o mesmo resultado, mas a segunda é mais enxuta.

Exemplos Práticos
• Exemplo 1: Definindo o status de maioridade
1 idade_str = input ( " Digite sua idade : " )
2 idade = int ( idade_str )
3
4 # Usando o operador condicional tern á rio
5 status = " Maior de idade " if idade >= 18 else " Menor de
idade "
6
7 print ( f " Com { idade } anos , seu status é : { status }. " )

Código 4.18: Definindo maioridade com operador ternário


94 Capítulo 4. Estruturas de Controle Condicional

• Exemplo 2: Determinando um valor absoluto simples (sem usar abs())


1 numero = -10
2 # Se numero < 0 , absoluto é - numero , sen ã o é o pr ó prio
numero
3 absoluto = - numero if numero < 0 else numero
4 print ( f " O valor absoluto de { numero } é { absoluto }. " ) #
Sa í da : 10

Código 4.19: Valor absoluto com operador ternário

• Exemplo 3: Mensagem de desconto


1 tem_cupom = True
2 desconto = 0.10 if tem_cupom else 0.00 # Desconto de 10%
se tem cupom , sen ã o 0%
3
4 # Usando o tern á rio diretamente em uma f - string
5 mensa gem_desc onto = f " Voc ê tem um desconto de
{ desconto *100:.0 f }%. " if tem_cupom else " Nenhum cupom
de desconto aplicado . "
6 print ( mensage m_descont o )

Código 4.20: Mensagem de desconto com operador ternário

Vantagens do Operador Ternário


• Concisão: Pode tornar o código mais curto e, em casos simples, mais direto.

• Elegância (em alguns casos): Quando usado apropriadamente para condições e


valores simples, pode resultar em um código que é lido de forma bastante natural.

Desvantagens e Quando Evitar


Apesar de sua concisão, o operador ternário deve ser usado com cautela:

• Legibilidade Comprometida: Se a condicao, o valor_se_verdadeiro, ou o


valor_se_falso forem expressões muito longas ou complexas, a linha única do
operador ternário pode se tornar difícil de ler e entender. Nesses casos, a estrutura
if-else tradicional, com seus blocos bem definidos, é muito mais clara.

• Não Substitui if-elif-else Complexos: O operador ternário é estritamente


para uma única condição com duas alternativas. Não é adequado para múltiplas
condições.

• Não é para Blocos de Múltiplas Instruções: Se você precisa executar múltiplas


instruções diferentes para o caso True e para o caso False, você deve usar a estrutura
if-else completa. O ternário é para atribuir ou resultar em um valor.

Recomendação: Use com Moderação e Bom Senso


A regra de ouro é: se a expressão condicional ternária torna seu código mais claro e
conciso sem sacrificar a legibilidade, use-a. Se ela começa a parecer uma linha de código
muito densa, confusa ou difícil de decifrar rapidamente, opte pela estrutura if-else
4.8. Valores “Truthy” e “Falsy”: O que Python Considera Verdadeiro ou Falso 95

tradicional. A legibilidade (lembre-se do Zen de Python!) geralmente triunfa sobre a


concisão extrema.
O operador condicional ternário é mais uma ferramenta útil no seu arsenal Python,
especialmente para atribuições rápidas baseadas em uma condição simples.

4.8 Valores “Truthy” e “Falsy”: O que Python Considera


Verdadeiro ou Falso
Ao longo deste capítulo, vimos que as estruturas condicionais como if, elif e else
dependem de uma condicao que precisa ser avaliada como True (Verdadeiro) ou False
(Falso) para determinar qual bloco de código será executado. Normalmente, usamos
operadores de comparação (ex: idade >= 18) ou lógicos (ex: cond1 and cond2) que
explicitamente resultam em True ou False.
No entanto, Python tem uma característica flexível e poderosa: em um contexto
booleano (ou seja, onde um valor True ou False é esperado), ele não exige que a expressão
seja literalmente True ou False. Em vez disso, Python considera certos valores como
inerentemente “falsos” (chamados de valores Falsy) e a maioria dos outros valores como
inerentemente “verdadeiros” (valores Truthy).
Entender esse conceito pode tornar seu código mais conciso e, em muitos casos, mais
legível e idiomático (ou seja, mais alinhado com o “jeito Python de programar”).

Quais Valores são Considerados “Falsy” em Python?


A lista de valores que são tratados como False em um contexto booleano é bem
definida e relativamente curta. São eles:

• O próprio valor booleano: False

• O objeto especial que representaaausência de valor: None

• Zero de qualquer tipo numérico:

– Inteiro: 0
– Ponto flutuante: 0.0
– Complexo: 0j

• Qualquer sequência ou coleção vazia:

– String vazia: ou ”
– Lista vazia: []
– Tupla vazia: ()
– Dicionário vazio: {}
– Conjunto vazio: set() (lembre-se que {} sozinho cria um dicionário vazio, não
um conjunto vazio)
96 Capítulo 4. Estruturas de Controle Condicional

Quais Valores são Considerados “Truthy”?


Simples: todos os outros valores que não estão na lista Falsy são considerados
Truthy. Isso inclui:

• O próprio valor booleano: True

• Qualquer número diferente de zero (ex: 1, -10, 3.14, 0.0001).

• Qualquer string não vazia (ex: "Olá", (uma string com um espaço), "False" (a
string “False” é Truthy!)).

• Qualquer lista, tupla, dicionário ou conjunto que não esteja vazio.

• A maioria dos objetos que você criar.

Como Isso Simplifica as Condições?


Essa característica do Python permite escrever condições de forma mais enxuta. Veja-
mos alguns exemplos comuns:

• Verificando se uma string não está vazia:


Forma mais longa (e explícita): if nome_usuario != “”:
Forma Pythônica usando Truthiness: if nome_usuario: (Se nome_usuario for
uma string vazia, é Falsy; se tiver qualquer caractere, é Truthy).

• Verificando se uma lista não está vazia:


Forma mais longa: if len(minha_lista_de_itens) > 0:
Forma Pythônica: if minha_lista_de_itens: (Se a lista estiver vazia, é Falsy; se
tiver itens, é Truthy).

• Verificando se um número não é zero (usado com cautela):


Forma mais longa: if contador != 0:
Forma Pythônica: if contador: (Se contador for 0, é Falsy; se for qualquer outro
número, é Truthy).
Atenção aqui: se contador pudesse ser None e você quisesse tratar None de forma
diferente de 0, então if contador: poderia não ser a melhor escolha, pois tanto 0
quanto None são Falsy. Ser explícito (if contador is not None and contador != 0)
seria mais claro nesse caso.

Mais Exemplos Práticos de Truthiness e Falsiness


1 # Exemplo com Truthiness e Falsiness
2 nome_arquivo = input ( " Digite o nome do arquivo ( ou pressione Enter
se n ã o houver ) : " )
3
4 if nome_arquivo : # Verdadeiro se ’ nome_arquivo ’ n ã o for uma string
vazia
5 print ( f " Processando o arquivo : { nome_arquivo }... " )
6 # L ó gica para processar o arquivo
7 else : # Falso se ’ nome_arquivo ’ for uma string vazia ""
8 print ( " Nenhum nome de arquivo fornecido . Usando configura ç õ es
padr ã o . " )
4.8. Valores “Truthy” e “Falsy”: O que Python Considera Verdadeiro ou Falso 97

9
10 itens_carrinho = [] # Uma lista vazia ( Falsy )
11 # Para testar o lado Truthy , voc ê poderia ter :
12 # itens_carrinho = [" produto1 " , " produto2 "]
13
14 if not itens_carrinho : # ’ not lista_vazia ’ é ’ not False ’, que é True
15 print ( " Seu carrinho de compras est á vazio . Que tal adicionar
algo ? " )
16 else :
17 print ( " Voc ê tem itens no seu carrinho : " )
18 for item in itens_carrinho : # Uma pequena pr é via do la ç o ’ for ’
19 print ( f " - { item } " )
20
21 saldo_disponivel = 0.0
22 if saldo_disponivel : # Falso , porque saldo_disponivel é 0.0
23 print ( " Voc ê tem saldo para realizar a transa ç ã o . " )
24 else :
25 print ( " Saldo insuficiente para a transa ç ã o . " )

Código 4.21: Exemplos de Truthiness e Falsiness em Python

A Função bool() para Conversão Explícita


Se você quiser ver explicitamente o valor booleano de qualquer objeto ou valor em
Python, pode usar a função bool():
1 print ( f " bool (0) é : { bool (0) } " ) # Sa í da : False
2 print ( f " bool (100) é : { bool (100) } " ) # Sa í da : True
3 print ( f " bool (0.0) é : { bool (0.0) } " ) # Sa í da : False
4 print ( f " bool ( ’ ’) é : { bool ( ’ ’) } " ) # Sa í da : False ( string
vazia )
5 print ( f " bool ( ’ Python ’) é : { bool ( ’ Python ’) } " ) # Sa í da : True ( string
n ã o vazia )
6 print ( f " bool ( ’ ’) é : { bool ( ’ ’) } " ) # Sa í da : True ( string com
um espa ç o )
7 print ( f " bool ([]) é : { bool ([]) } " ) # Sa í da : False ( lista
vazia )
8 print ( f " bool ([1 , 2]) é : { bool ([1 , 2]) } " ) # Sa í da : True ( lista n ã o
vazia )
9 print ( f " bool ( None ) é : { bool ( None ) } " ) # Sa í da : False
10 print ( f " bool ( True ) é : { bool ( True ) } " ) # Sa í da : True

Código 4.22: Usando a função bool()

Clareza vs. Concisão Pythônica


Embora usar os valores Truthy e Falsy diretamente nas condições if seja muito comum
e considerado Pythônico, a clareza do seu código deve ser sempre a prioridade. Se você
achar que uma condição como if nome_usuario: pode ser ambígua para alguém que está
lendo seu código (ou para você mesmo no futuro), não hesite em ser mais explícito, como
if nome_usuario != : ou if len(lista) > 0:.
Com o tempo e a prática, você desenvolverá uma intuição para quando a forma mais
concisa é também a mais clara.
98 Capítulo 4. Estruturas de Controle Condicional

Concluindo o Capítulo sobre Estruturas Condicionais


Neste capítulo, exploramos as ferramentas essenciais que o Python nos oferece para
adicionar lógica de decisão aos nossos programas. Começamos com o if simples, expandi-
mos para o if-else para duas alternativas, e depois para o if-elif-else para múltiplas
condições excludentes. Discutimos a diferença crucial entre usar múltiplos ifs indepen-
dentes e a estrutura if-elif-else, vimos como aninhar essas estruturas para lógicas
mais complexas, como usar o operador ternário para expressões condicionais concisas, e
finalmente, como Python interpreta diferentes valores como True ou False em contextos
booleanos através do conceito de Truthiness e Falsiness.
Com essas estruturas, seus programas agora podem reagir a diferentes entradas, tomar
decisões e seguir caminhos diferentes, tornando-se muito mais inteligentes e flexíveis. O
próximo passo em nossa jornada é aprender como fazer nossos programas repetirem tarefas,
e isso será o foco do Capítulo 5: Estruturas de Repetição!
Capítulo 5

Estruturas de Repetição (Laços)

No capítulo anterior, exploramos como as estruturas condicionais (if, elif, else)


permitem que nossos programas tomem decisões e sigam caminhos diferentes com base em
certas condições. Essa capacidade de decisão é crucial, mas muitas vezes, na programação,
nos deparamos com a necessidade de executar um mesmo bloco de código várias vezes
seguidas.
Imagine ter que imprimir os números de 1 a 100. Você poderia escrever print(1),
print(2), ..., print(100)? Tecnicamente sim, mas seria extremamente tedioso, repetitivo
e nada prático se precisasse ir até 1000 ou um milhão! Da mesma forma, se você tivesse
uma lista de nomes de alunos e quisesse exibir uma saudação para cada um, não seria
eficiente escrever uma linha de print() para cada aluno individualmente, especialmente
se a lista pudesse mudar de tamanho.
É para resolver esse tipo de problema que existem as estruturas de repetição,
também conhecidas como laços ou loops, em inglês. Elas nos permitem executar um bloco
de código múltiplas vezes de forma automática e controlada.

5.1 Introdução aos Laços de Repetição: Automatizando


Tarefas Repetitivas
A ideia central por trás de um laço de repetição é simples: em vez de escrever a mesma
instrução (ou conjunto de instruções) várias vezes, você a escreve uma única vez dentro
de uma estrutura de laço e define uma condição ou um critério que determinará quantas
vezes esse bloco de instruções será repetido. Cada execução do bloco de código dentro do
laço é chamada de iteração.

Por que Precisamos de Laços?


Os laços de repetição são fundamentais na programação por diversos motivos:

1. Evitar Redundância de Código: Permitem que você escreva menos código para
realizar tarefas que se repetem, tornando seus programas mais curtos, mais fáceis de
ler e de dar manutenção.

2. Processar Coleções de Dados: São essenciais para trabalhar com conjuntos de


dados, como listas de itens, caracteres em uma string, linhas em um arquivo, etc.
Com um laço, você pode aplicar uma mesma operação a cada elemento da coleção.

99
100 Capítulo 5. Estruturas de Repetição (Laços)

3. Executar Simulações e Cálculos Iterativos: Muitos problemas em ciência e


engenharia envolvem processos que precisam ser repetidos até que uma certa condição
seja alcançada ou um resultado seja obtido com a precisão desejada.

4. Manter Programas em Execução: Alguns programas, como servidores web


ou interfaces gráficas, rodam em um laço principal que continuamente espera por
eventos ou entradas do usuário.

Tipos Principais de Laços em Python


Python oferece duas estruturas principais de laços de repetição, cada uma adequada
para diferentes tipos de cenários:

1. O Laço while (Enquanto):


Este tipo de laço executa um bloco de código repetidamente enquanto uma de-
terminada condição continuar sendo verdadeira. A condição é verificada antes de
cada iteração. Se a condição se tornar falsa, o laço para. É ideal quando você não
sabe exatamente quantas vezes precisa repetir o bloco, mas sabe sob qual condição a
repetição deve continuar ou parar.

2. O Laço for (Para):


Este laço é usado para iterar sobre os itens de uma sequência (como uma string,
uma lista, uma tupla) ou qualquer outro objeto iterável. Para cada item na sequência,
o laço executa o bloco de código uma vez. É ideal quando você quer percorrer todos
os elementos de uma coleção ou quando quer executar um bloco de código um número
específico de vezes (usando, por exemplo, a função range()).

Neste capítulo, vamos explorar em detalhes como usar o laço while e o laço for,
aprender sobre comandos que nos dão mais controle sobreaexecução dos laços (como break
e continue), e ver como podemos até mesmo aninhar laços (colocar um laço dentro de
outro) para resolver problemas mais complexos.
Dominar as estruturas de repetição abrirá um novo leque de possibilidades para seus
programas, permitindo que eles realizem tarefas muito mais poderosas e automatizadas de
forma “descomplicada”. Vamos começar com o laço while!

5.2 O Laço while (Enquanto): Repetindo com Base em


uma Condição
O laço while é uma das estruturas de repetição fundamentais em Python. A palavra
“while”, em inglês, significa “enquanto”. E é exatamente essa a lógica: o laço while
executa um bloco de código repetidamente enquanto uma determinada condição que
você especifica continuar sendo verdadeira (True).
Ele é ideal para situações onde você não sabe de antemão quantas vezes o bloco de
código precisa ser repetido, mas sabe sob qual condição a repetição deve continuar ou
cessar.

Sintaxe do Laço while


A estrutura básica de um laço while é a seguinte:
5.2. O Laço while (Enquanto): Repetindo com Base em uma Condição 101

1 while condicao :
2 # Bloco de c ó digo do while ( corpo do la ç o )
3 # Instru ç ã o 1 a ser repetida
4 # Instru ç ã o 2 a ser repetida
5 # ...
6 # É crucial que algo dentro deste bloco , ou uma influ ê ncia
externa ,
7 # eventualmente fa ç a a ’ condicao ’ se tornar False .
8 # C ó digo que continua ap ó s o la ç o ’ while ’ terminar

Código 5.1: Estrutura básica do laço while


Vamos analisar os componentes:
1. while: A palavra-chave que inicia o laço.
2. condicao: Uma expressão que é avaliada antes de cada potencial execução do corpo
do laço. Enquanto esta condição resultar em True, o corpo do laço é executado. Se
a condição resultar em False (ou um valor Falsy), o laço termina.
3. Dois Pontos (:): Obrigatórios apósacondicao, indicam o início do bloco de código
que será repetido.
4. Bloco de Código Indentado (Corpo do Laço): Assim como nas estruturas if,
o bloco de código que pertence ao while (e que será repetido) deve estar indentado
(geralmente com 4 espaços).

Como Funciona o Laço while


O fluxo de execução de um laço while acontece da seguinte forma:
1. A condicao é avaliada pela primeira vez.
2. Se a condicao for True:
(a) O corpo do laço (o bloco de código indentado) é executado completamente.
(b) Ao final da execução do corpo, o fluxo volta para o passo 1, e a condicao é
avaliada novamente.
3. Se a condicao for False (seja na primeira avaliação ou em qualquer reavaliação
subsequente):
(a) O corpo do laço é ignorado (ou não é mais executado).
(b) A execução do programa continua comaprimeira instrução que estiver após o
bloco while.

A Importância Crítica de Alterar a Condição (Evitando Laços


Infinitos!)
Este é um ponto vital para o funcionamento correto dos laços while: para que o laço
eventualmente termine, algo dentro do corpo do laço (ou alguma condição externa
que ele verifica) deve, em algum momento, fazer com que a condicao do while
se torne False.
102 Capítulo 5. Estruturas de Repetição (Laços)

Se a condição começar como True e nada dentro do laço a modificar para False,
o laço continuará executando indefinidamente. Isso é chamado de laço infinito. Um
programa preso em um laço infinito geralmente para de responder e precisa ser interrompido
manualmente (por exemplo, pressionando Ctrl+C no terminal).
Frequentemente, usamos uma variável de controle (muitas vezes chamada de contador
ou flag) para gerenciar a condição do laço:

1. Inicializamos a variável de controle antes do laço.

2. Usamos essa variável na condicao do while.

3. Atualizamos o valor dessa variável dentro do corpo do laço, de forma que, eventu-
almente, a condição se torne False.

Exemplos Práticos
• Exemplo 1: Contando de 1 até um limite
Vamos usar um contador para imprimir números de 1 até 5.
1 contador = 1 # 1. Inicializa ç ã o da vari á vel de controle
2 limite = 5
3
4 print ( f " Iniciando contagem at é { limite }: " )
5 while contador <= limite : # 2. Condi ç ã o usando a vari á vel
de controle
6 print ( contador )
7 contador += 1 # 3. Atualiza ç ã o da vari á vel de controle
( MUITO IMPORTANTE !)
8
9 print ( " Contagem finalizada ! " )

Código 5.2: Contagem com laço while

Se esquecêssemos da linha contador += 1, contador seria sempre 1, a condição 1


<= 5 seria sempre True, e teríamos um laço infinito imprimindo “1” sem parar!

• Exemplo 2: Repetindo até o usuário decidir parar


Aqui, a condição depende da entrada do usuário.
1 e nt rad a_ do _us ua ri o = " " # Inicializamos para garantir que
o la ç o comece
2
3 print ( " Digite mensagens . Digite ’ sair ’ para terminar o
programa . " )
4 while en tr ada _d o_ usu ar io . lower () != " sair " : # . lower ()
torna a compara ç ã o insens í vel a mai ú sculas / min ú sculas
5 en tra da _d o_u su ar io = input ( " Sua mensagem : " )
6 if e nt ra da_ do _u sua ri o . lower () != " sair " : # Evita
imprimir a palavra ’ sair ’
7 print ( f " Voc ê disse : { en tr ada _d o_ us uar io } " )
8
9 print ( " Programa encerrado . At é logo ! " )

Código 5.3: Laço while controlado por entrada do usuário


5.2. O Laço while (Enquanto): Repetindo com Base em uma Condição 103

Neste caso, o laço continua enquanto o usuário não digitar “sair” (ou “SAIR”, “Sair”,
etc.). A atualização da condição ocorre com a nova leitura do input() a cada
iteração.

• Exemplo 3: Simulação simples de uma bateria descarregando


1 nivel_bateria = 100 # Bateria come ç a em 100%
2 uso_por_operacao = 15 # Gasta 15% por opera ç ã o
3 o p e r a c oe s _ r e a l i z a d a s = 0
4

5 print ( f " N í vel inicial da bateria : { nivel_bateria }% " )


6 # O dispositivo opera enquanto a bateria estiver acima de
20%
7 while nivel_bateria > 20:
8 print ( f " Opera ç ã o { o p e r a c o e s _ r e a l i z a da s + 1} em
andamento ... Bateria : { nivel_bateria }% " )
9 # Simula o gasto de bateria
10 nivel_bateria -= uso_por_operacao
11 o p e r a c o e s _ r e a l iz a d a s += 1
12 # Em uma simula ç ã o real , poderia haver uma pausa aqui
( ex : time . sleep (1) )
13

14 print ( f " Opera ç ã o encerrada . N í vel final da bateria :


{ nivel_bateria }%. " )
15 print ( f " Total de opera ç õ es realizadas :
{ o p e r a c o e s _ r e a l i z a d a s }. " )

Código 5.4: Simulação de bateria com laço while

Laços Infinitos Controlados com while True: e break


Às vezes, a lógica para sair de um laço é mais complexa e não se encaixa facilmente
em uma única condição no início do while. Nesses casos, pode ser útil criar um laço que,
aparentemente, seria infinito, usando while True:, e então usar a instrução break dentro
do corpo do laço para sair quando uma condição específica for atendida.
A instrução break interrompe imediatamente a execução do laço mais interno em que
se encontra. Veremos o break em mais detalhes na Seção 5.4, mas aqui está um exemplo
comum:
1 # Exemplo de la ç o " infinito " controlado por ’ break ’
2 print ( " Calculadora de Soma Interativa ( digite ’ fim ’ para sair ) " )
3 soma_acumulada = 0.0
4
5 while True : # Este la ço , por si s ó , rodaria para sempre
6 entrada = input ( " Digite um n ú mero para somar ou ’ fim ’ para
resultado : " )
7

8 if entrada . lower () == ’ fim ’:


9 break # ESTA É A CONDI Ç Ã O DE SA Í DA DO LA Ç O !
10
11 # Tenta converter a entrada para um n ú mero
12 # Usamos try - except para lidar com o caso do usu á rio n ã o digitar um
n ú mero
13 # ( N ã o se preocupe com try - except agora , veremos no Cap . 17)
14 try :
15 numero = float ( entrada )
104 Capítulo 5. Estruturas de Repetição (Laços)

16 soma_acumulada += numero
17 print ( f " Soma parcial : { soma_acumulada } " )
18 except ValueError :
19 print ( " Entrada inv á lida . Por favor , digite um n ú mero ou a
palavra ’ fim ’. " )
20

21 print ( f " A soma total dos n ú meros digitados é : { soma_acumulada }. " )

Código 5.5: Laço while True com break

Cuidado: Se você usar while True:, é absolutamente essencial que exista um caminho
lógico dentro do laço que leve a uma instrução break, caso contrário, você terá um laço
infinito real!
O laço while é uma ferramenta poderosa para criar repetições cujo número de iterações
não é necessariamente conhecido de antemão, mas que depende de uma condição que é
verificada a cada passo. A chave para usá-lo corretamente é garantir que essa condição de
parada seja, em algum momento, alcançada.

5.3 O Laço for (Para): Iterando sobre Sequências


Enquanto o laço while é ideal para repetir um bloco de código enquanto uma condição
genérica é verdadeira, o laço for em Python brilha quando precisamos iterar sobre os
itens de uma sequência (como os caracteres de uma string, os elementos de uma lista
ou tupla) ou quando queremos executar um bloco de código um número predefinido de
vezes.
O laço for em Python é um pouco diferente do for em algumas outras linguagens
(como C ou Java), que geralmente envolvem inicializar um contador, verificar uma condição
e incrementar o contador. Em Python, o laço for é mais elegante e direto para percorrer
coleções.

Sintaxe do Laço for


A estrutura básica de um laço for é:
1 for variavel_item in s e q u e n c i a _ o u _ i t e r a v e l :
2 # Bloco de c ó digo ( corpo do la ç o )
3 # Este bloco é executado uma vez para cada item
4 # na s e q u e n c i a _ o u _ i t e r a v e l .
5 # A ’ variavel_item ’ assume o valor do item atual
6 # da s e q u e n c i a _ o u _ i t e r a v e l a cada nova itera ç ã o .
7 # C ó digo que continua ap ó s o la ç o ’ for ’ terminar todas as itera ç õ es

Código 5.6: Estrutura básica do laço for

Vamos analisar os componentes:

1. for: A palavra-chave que inicia o laço.

2. variavel_item: Uma variável que você define (você escolhe o nome). A cada
iteração do laço, esta variável receberá automaticamente o próximo item da
sequencia_ou_iteravel.

3. in: A palavra-chave que separa a variavel_item da sequencia_ou_iteravel.


5.3. O Laço for (Para): Iterando sobre Sequências 105

4. sequencia_ou_iteravel: É a coleção de itens sobre a qual o laço vai iterar. Pode


ser uma string, uma lista, uma tupla, um objeto range(), ou outros tipos de objetos
que o Python sabe como “percorrer” um item de cada vez (chamados de iteráveis).

5. Dois Pontos (:): Obrigatórios ao final da linha, indicando o início do bloco de


código.

6. Bloco de Código Indentado (Corpo do Laço): As instruções que serão execu-


tadas para cada item da sequência. Como sempre, a indentação é crucial.

Como Funciona o Laço for


O fluxo é bastante intuitivo:

1. O laço for pega o primeiro item da sequencia_ou_iteravel e o atribui à


variavel_item.

2. O corpo do laço (o bloco indentado) é executado. Dentro deste bloco, você pode
usar variavel_item para se referir ao item atual.

3. Após o corpo do laço ser executado para o primeiro item, o laço for pega o segundo
item da sequencia_ou_iteravel, atribui-o à variavel_item, e o corpo do laço é
executado novamente.

4. Este processo continua até que todos os itens da sequencia_ou_iteravel tenham


sido processados.

5. Quando não há mais itens na sequência, o laço for termina automaticamente,


eaexecução do programa continua comaprimeira instrução após o bloco for.

Iterando sobre Strings


Uma string é uma sequência de caracteres. Podemos usar um laço for para processar
cada caractere individualmente:
1 # Iterando sobre os caracteres de uma string
2 palavra = " Python "
3 print ( f " As letras da palavra ’{ palavra } ’ s ã o : " )
4 for letra_atual in palavra :
5 print ( letra_atual )

Código 5.7: Iterando sobre os caracteres de uma string

A saída será:

As letras da palavra ’Python’ são:


P
y
t
h
o
n
106 Capítulo 5. Estruturas de Repetição (Laços)

A Função range(): Gerando Sequências de Números para Repetição


Muitas vezes, queremos repetir um bloco de código um número específico de vezes,
ou iterar sobre uma sequência de números. A função embutida range() é perfeita para
isso em conjunto com o laço for. Ela gera uma sequência de números que o for pode
percorrer.
A função range() pode ser usada de algumas formas:
1. range(fim): Gera uma sequência de números começando do 0 (zero) e indo até fim
- 1.
1 # Exemplo com range ( fim )
2 print ( " Contando de 0 a 4: " )
3 for numero in range (5) : # Gera n ú meros 0 , 1 , 2 , 3 , 4
4 print ( numero )

Código 5.8: Usando range(fim)

2. range(inicio, fim): Gera uma sequência de números começando de inicio e


indo até fim - 1.
1 # Exemplo com range ( inicio , fim )
2 print ( " Contando de 1 a 5: " )
3 for numero in range (1 , 6) : # Gera n ú meros 1 , 2 , 3 , 4 , 5
4 print ( numero )

Código 5.9: Usando range(inicio, fim)

3. range(inicio, fim, passo): Gera uma sequência de números começando de


inicio, indo até fim - 1, e incrementando (ou decrementando, se o passo for
negativo) pelo valor do passo.
1 # Exemplo com range ( inicio , fim , passo )
2 print ( " N ú meros pares de 0 a 10: " )
3 for numero in range (0 , 11 , 2) : # Gera 0 , 2 , 4 , 6 , 8 , 10
4 print ( numero )
5
6 print ( " Contagem regressiva de 5 a 1: " )
7 for numero in range (5 , 0 , -1) : # Gera 5 , 4 , 3 , 2 , 1
8 print ( numero )
9 print ( " Fogo ! " )

Código 5.10: Usando range(inicio, fim, passo)

Exemplos Práticos com for e range()


• Exemplo 1: Repetir uma ação N vezes
1 n_repeticoes = int ( input ( " Quantas vezes devo repetir a
mensagem ? " ) )
2
3 for i in range ( n_repeticoes ) :
4 # ’i ’ aqui ser á 0 , 1 , 2 , ... , n_repeticoes -1
5 # Usamos i +1 para exibir uma contagem mais natural
para o usu á rio ( de 1 at é N )
6 print ( f " Esta é a repeti ç ã o n ú mero { i + 1}. " )

Código 5.11: Repetindo uma ação N vezes com for


5.3. O Laço for (Para): Iterando sobre Sequências 107

É comum usar a variável i (ou j, k) como nome para o contador em laços for que
usam range() apenas para controlar o número de repetições, quando o valor do
contador em si não é o mais importante dentro do laço.

• Exemplo 2: Calculando a soma dos N primeiros números inteiros


1 n = int ( input ( " Calcular a soma dos n ú meros inteiros de 1
at é : " ) )
2 soma_total = 0 # Come ç amos com a soma igual a zero
3
4 for numero_corrente in range (1 , n + 1) : # range (1 , n + 1)
vai de 1 at é n
5 soma_total += numero_corrente # Acumula o n ú mero
corrente na soma_total
6
7 print ( f " A soma dos n ú meros de 1 at é { n } é : { soma_total }. " )

Código 5.12: Soma dos N primeiros inteiros com for

• Exemplo 3: Exibindo a tabuada de um número


1 numero_tabuada = int ( input ( " Qual tabuada voc ê gostaria de
ver ( ex : 7) ? " ) )
2
3 print ( f " --- Tabuada do { numero_tabuada } ---" )
4 for multiplicador in range (1 , 11) : # Queremos a tabuada de
1 a 10
5 resultado = numero_tabuada * multiplicador
6 print ( f " { numero_tabuada } x { multiplicador } = { resultado } " )

Código 5.13: Tabuada com for

Iterando sobre Listas (Uma Pequena Prévia)


Embora o Capítulo 6 seja dedicado inteiramente às listas, é útil ver agora como um
laço for pode percorrer os elementos de uma lista de forma natural. Uma lista é uma
coleção ordenada de itens.
1 # Exemplo de itera ç ã o sobre uma lista
2 fr ut as_ pa ra _sa la da = [ " Ma ç ã " , " Banana " , " Laranja " , " Uva " , " Morango " ]
3

4 print ( " Ingredientes para a salada de frutas : " )


5 for fruta_da_vez in fru ta s_ par a_ sa lad a :
6 print ( f " - { fruta_da_vez . capitalize () } " ) # . capitalize () para
primeira letra mai ú scula

Código 5.14: Iterando sobre uma lista de frutas

Este código imprimirá cada nome de fruta da lista, um por linha. É uma forma muito
comum e legível de processar cada item de uma coleção.

Quando Usar for versus while? (Uma Breve Comparação)


• Laço for:
108 Capítulo 5. Estruturas de Repetição (Laços)

– Ideal para: Iterar sobre uma sequência conhecida de itens (strings, listas,
tuplas, etc.) ou quando você sabe exatamente quantas vezes quer que o laço
execute (usando range()).
– A variável de iteração (variavel_item) é atualizada automaticamente a cada
passo.
– Geralmente mais conciso e menos propenso a erros de laço infinito para esses
cenários.

• Laço while:

– Ideal para: Situações onde o número de iterações não é conhecido de antemão,


e o laço deve continuar enquanto uma condição genérica for verdadeira.
– Você precisa gerenciar manualmenteainicialização eaatualização da(s) variável(is)
que controlamacondição do laço para evitar laços infinitos.

Muitas vezes, um problema pode ser resolvido com qualquer um dos laços, mas um deles
geralmente se encaixa de forma mais natural e leva a um código mais claro.
O laço for, especialmente em combinação com range() e ao iterar diretamente sobre
sequências, é uma das ferramentas mais poderosas e “Pythônicas” (típicas do estilo Python)
para realizar repetições de forma elegante e eficiente.

5.4 Comandos de Controle de Laço: break e continue


Os laços while e for que aprendemos nas seções anteriores são poderosos para repetir
blocos de código. Eles têm suas condições normais de término: o while para quando
sua condição se torna falsa, e o for para quando todos os itens de uma sequência foram
processados.
No entanto, há situações em que precisamos de um controle mais granular sobre o fluxo
de execução dentro de um laço. Podemos querer:

• Interromper o laço completamente, mesmo que sua condição normal de término


ainda não tenha sido atingida.

• Pular a iteração atual e ir diretamente paraapróxima, sem executar todo o código


restante no corpo do laço para aquela iteração específica.

Python nos oferece duas instruções para esses cenários: break e continue.

A Instrução break: Interrompendo o Laço Prematuramente


Quando o interpretador Python encontra a instrução break dentro de um laço (for
ou while), ele interrompe imediatamente a execução do laço mais interno em
que o break se encontra. Qualquer código restante no corpo do laço naquela iteração é
ignorado, e o programa continua executandoaprimeira instrução que vier após o bloco do
laço que foi interrompido.
Quando usar break?

• Para sair de um laço que seria “infinito” (como while True:) assim que uma condição
específica de parada é atingida dentro do corpo do laço.
5.4. Comandos de Controle de Laço: break e continue 109

• Para interromper um laço for ou while antes que ele complete todas as suas iterações
normais, por exemplo, porque um item específico foi encontrado em uma busca, ou
porque uma condição de erro crítica ocorreu.

Exemplos Práticos com break:

• Exemplo 1: Laço while True: para entrada de usuário (revisitado)


1 # Calculadora de soma interativa que para com ’ fim ’
2 print ( " Digite n ú meros para somar . Digite ’ fim ’ para ver o
total . " )
3 s om a_t ot al _nu me ro s = 0.0
4 while True : # Um la ç o que , sem ’ break ’, seria infinito
5 en tra da _d o_u su ar io = input ( " N ú mero ( ou ’ fim ’) : " )
6 if e nt ra da_ do _u sua ri o . lower () == ’ fim ’:
7 break # Encerra o la ç o ’ while ’ imediatamente
8

9 try :
10 numero_digitado = float ( e nt rad a_ do _us ua ri o )
11 som a_ to tal _n um ero s += numero_digitado
12 print ( f " Soma at é agora : { s oma _t ot al_ nu me ros } " )
13 except ValueError :
14 print ( " Entrada inv á lida . Por favor , digite um n ú mero
ou a palavra ’ fim ’. " )
15
16 print ( f " A soma final de todos os n ú meros é :
{ so ma _t ota l_ nu mer os } " )

Código 5.15: Usando break em um laço while True

• Exemplo 2: Procurando um item específico em uma lista com for


1 lista_de_numeros = [11 , 24 , 7 , 42 , 15 , 8 , 30 , 42 , 50]
2 numero_alvo = 42
3 p os ica o_ en con tr ad a = -1 # Valor inicial indicando que n ã o
foi encontrado
4
5 print ( f " Procurando o n ú mero { numero_alvo } na lista ... " )
6 for indice in range ( len ( lista_de_numeros ) ) : # Iterando
pelos í ndices da lista
7 numero_atual = lista_de_numeros [ indice ]
8 print ( f " Verificando posi ç ã o { indice } , valor
{ numero_atual }... " )
9 if numero_atual == numero_alvo :
10 po si ca o_e nc on tra da = indice
11 print ( f " N ú mero { numero_alvo } encontrado na posi ç ã o
{ po si ca o_e nc on tra da }! " )
12 break # Encontramos , n ã o precisamos continuar o
la ç o
13
14 if po si ca o_e nc on tra da != -1:
15 print ( " Busca finalizada ap ó s encontrar o item . " )
16 else :
17 print ( f " N ú mero { numero_alvo } n ã o encontrado na lista . " )

Código 5.16: Usando break em um laço for para busca


110 Capítulo 5. Estruturas de Repetição (Laços)

A Instrução continue: Pulando para a Próxima Iteração


Quando o Python encontra a instrução continue dentro de um laço, ele interrompe
a execução da iteração atual e imediatamente pula para o início da próxima
iteração.

• Em um laço while, isso significa que a condicao do while é reavaliada.

• Em um laço for, isso significa que a variavel_item recebe o próximo item da


sequência.

Qualquer código que viesse apósainstrução continue dentro do corpo do laço naquela
iteração específica não será executado.
Quando usar continue?

• Para pular o processamento de certos itens em uma sequência que não atendem a
um critério, mas você quer continuar processando os outros itens.

• Para ignorar entradas inválidas ou condições específicas dentro de uma iteração sem
ter que aninhar o resto da lógica do laço dentro de um if.

Exemplos Práticos com continue:

• Exemplo 1: Processando apenas números positivos de uma lista com for


1 numeros_variados = [10 , -2 , 0 , 25 , -7 , 8 , -1 , 100]
2 s om a_d os _p osi ti vo s = 0
3

4 print ( " Somando apenas os n ú meros positivos da lista ... " )


5 for numero in numeros_variados :
6 if numero <= 0: # Se o n ú mero for zero ou negativo
7 print ( f " Ignorando o n ú mero { numero } ( n ã o é
positivo ) . " )
8 continue # Pula o resto desta itera ç ã o e vai
para o pr ó ximo n ú mero
9
10 # Este c ó digo s ó executa para n ú meros positivos
11 print ( f " Adicionando { numero } à soma . " )
12 s oma _d os _p osi ti vo s += numero
13

14 print ( f " A soma dos n ú meros positivos é :


{ so ma _d os_ po si tiv os } " )

Código 5.17: Usando continue para processar itens específicos

• Exemplo 2: Solicitando N nomes válidos com while


1 # Pedir 3 nomes v á lidos ao usu á rio
2 nomes_coletados = []
3 numero_nomes_desejado = 3
4
5 print ( f " Por favor , digite { n u m e r o _ n o m e s _ d e s e j a d o } nomes
v á lidos ( n ã o vazios ) . " )
6 while len ( nomes_coletados ) < n u m e r o _ n o m e s _ d e s e j a d o :
7 nome_digitado = input ( f " Digite o { len ( nomes_coletados )
+ 1} nome : " )
8
5.5. A Cláusula else em Laços (Opcional e Menos Comum) 111

9 if not nome_digitado : # Se o nome for uma string vazia


( Falsy )
10 print ( " Nome inv á lido ( vazio ) . Por favor , digite
novamente . " )
11 continue # Volta para o in í cio do while , pedindo
um novo nome
12
13 nomes_coletados . append ( nome_digitado )
14 print ( f " Nome ’{ nome_digitado } ’ adicionado . " )
15
16 print ( f " Nomes coletados com sucesso : { nomes_coletados } " )

Código 5.18: Usando continue para ignorar entradas inválidas

Onde break e continue Podem Ser Usados


As instruções break e continue só podem ser usadas legalmente dentro de um laço
for ou while. Tentar usá-las fora de um laço resultará em um SyntaxError.
Se você tiver laços aninhados (um laço dentro de outro), break e continue afetam
apenas o laço mais interno em que estão contidos.
As instruções break e continue oferecem um controle mais refinado sobreaexecução
dos seus laços. O break permite uma saída antecipada e completa, enquanto o continue
permite pular partes de uma iteração específica para seguir paraapróxima. Usadas com
sabedoria, elas podem tornar seu código mais eficiente e direto ao lidar com certas lógicas.

5.5 A Cláusula else em Laços (Opcional e Menos Co-


mum)
Python possui uma característica interessante e, para muitos, um pouco surpreendente
quando a encontram pela primeira vez: tanto os laços while quanto os laços for podem
ter uma cláusula else associada a eles.
É importante notar que este else se comporta de maneira diferente do else que
usamos com as estruturas if. Em um laço, o bloco else não está ligado a uma condição
if dentro do laço, mas sim à forma como o próprio laço termina.

Sintaxe do else em Laços


• Com o laço while:
1 while condicao :
2 # Corpo do la ç o while
3 # ...
4 # if a l g u m a _ c o n d i c a o _ p a r a _ i n t e r r o m p e r :
5 # break
6 else :
7 # Bloco de c ó digo do else
8 # Executado SOMENTE se o la ç o while terminar
NORMALMENTE
9 # ( ou seja , quando ’ condicao ’ se tornar False ) ,
10 # e N Ã O por um ’ break ’.
11 # C ó digo ap ó s toda a estrutura while - else
112 Capítulo 5. Estruturas de Repetição (Laços)

Código 5.19: Sintaxe do while...else

• Com o laço for:


1 for item in sequencia :
2 # Corpo do la ç o for
3 # ...
4 # if a l g u m a _ c o n d i c a o _ p a r a _ i n t e r r o m p e r :
5 # break
6 else :
7 # Bloco de c ó digo do else
8 # Executado SOMENTE se o la ç o for completar TODAS as
itera ç õ es
9 # ( ou seja , a ’ sequencia ’ for esgotada ) ,
10 # e N Ã O por um ’ break ’.
11 # C ó digo ap ó s toda a estrutura for - else

Código 5.20: Sintaxe do for...else

Quando o Bloco else é Executado (e Quando Não É)


A regra chave é:

• O bloco else associado a um laço (while ou for) é executado se, e somente se, o
laço terminar sua execução de forma normal:

– Para um laço while: quando sua condição de teste se torna False.


– Para um laço for: quando todos os itens da sequência foram iterados.

• O bloco else NÃO é executado se o laço for encerrado prematuramente por uma
instrução break.

(Nota: Se um erro ocorrer dentro do laço e o interromper, o bloco else também não será
executado.)

Casos de Uso e Exemplos Práticos


O uso mais comum e idiomático da cláusula else em laços é para executar um bloco
de código quando uma busca por um item em uma sequência não foi bem-sucedida (ou
seja, o laço terminou sem encontrar o item e, portanto, sem executar um break).

• Exemplo 1 (com for): Procurando um número em uma lista


1 nu m er o s_ d i sp o ni v e is = [2 , 4 , 6 , 8 , 10 , 12]
2 n u m e r o _p r o c u r a d o _ s t r = input ( " Qual n ú mero par ( entre 2 e
12) voc ê procura ? " )
3 numero_procurado = int ( nu m e r o _ p r o c u r a d o _ s t r )
4
5 for numero_na_lista in n u me r os _ d is p on i ve i s :
6 if numero_na_lista == numero_procurado :
7 print ( f " Ó timo ! O n ú mero { numero_procurado } foi
encontrado na lista . " )
8 break # Sai do la ç o assim que encontrar
5.5. A Cláusula else em Laços (Opcional e Menos Comum) 113

9 else :
10 # Este bloco S Ó executa se o ’ break ’ NUNCA for atingido
11 print ( f " Que pena ! O n ú mero { numero_procurado } N Ã O foi
encontrado na lista . " )
12
13 print ( " --- Fim da busca ---" )

Código 5.21: Buscando item com for...else

Se o numero_procurado estiver na lista, o break será executado, e o bloco else do


for será pulado. Se não estiver, o laço completará, e o else será executado.

• Exemplo 2 (com while): Contagem com possível interrupção


1 contador = 0
2 limite_contagem = 5
3
4 print ( f " Iniciando contagem at é { limite_contagem }. " )
5 i n t e r r o m p e r _ a g o r a _ s t r = input ( " Deseja interromper a
contagem no n ú mero 3? ( sim / nao ) : " ) . lower ()
6
7 while contador < limite_contagem :
8 contador += 1
9 print ( f " Contador atual : { contador } " )
10 if i n t e r r o m p e r _ a g o r a _ s t r == " sim " and contador == 3:
11 print ( " Interrompendo a contagem no 3 , como
solicitado ! " )
12 break # Interrompe o la ç o while
13 else :
14 # Este bloco s ó ser á executado se o la ç o while terminar
15 # porque ’ contador < limite_contagem ’ se tornou False ,
16 # e n ã o por causa de um ’ break ’.
17 print ( " A contagem foi conclu í da normalmente , sem
interrup ç õ es . " )
18
19 print ( " --- Programa de contagem finalizado ---" )

Código 5.22: Contagem com while...else e interrupção

Se o usuário digitar “sim” para interromper, o break será executado, e a mensagem


do else não aparecerá. Se digitar “nao”, o laço completará, e a mensagem do else
será exibida.

Clareza vs. “Surpresa”: Usar com Moderação


A cláusula else em laços é uma característica do Python que não existe em muitas ou-
tras linguagens de programação populares. Por isso, ela pode ser um pouco “surpreendente”
ou menos intuitiva para programadores que não estão familiarizados com ela.
Embora possa levar a um código elegante em certas situações (como no exemplo da
busca), às vezes, usar uma variável de controle (uma “flag” booleana) para indicar se um
laço foi interrompido ou não pode resultar em um código que é mais fácil de entender para
uma audiência mais ampla. Por exemplo, o Exemplo 1 com for poderia ser reescrito com
uma flag:
1 # Alternativa ao for ... else usando uma flag
2 n u me ros _ di s p on i ve i s = [2 , 4 , 6 , 8 , 10 , 12]
114 Capítulo 5. Estruturas de Repetição (Laços)

3 numero_procurado = int ( input ( " Qual n ú mero voc ê procura ? " ) )


4 encontrado_flag = False # Come ç amos assumindo que n ã o foi encontrado
5
6 for numero_na_lista in nu m e ro s _d i s po n iv e is :
7 if numero_na_lista == numero_procurado :
8 encontrado_flag = True
9 break
10
11 if encontrado_flag :
12 print ( f " Ó timo ! O n ú mero { numero_procurado } foi encontrado na
lista ( usando flag ) . " )
13 else :
14 print ( f " Que pena ! O n ú mero { numero_procurado } N Ã O foi
encontrado na lista ( usando flag ) . " )

Código 5.23: Alternativa ao for...else usando uma flag booleana

A escolha entre usar o else do laço ou uma flag depende do contexto e da preferência
pela clareza. Se o else do laço tornar sua intenção óbvia e o código mais limpo, use-o.
Se parecer que pode confundir, a alternativa com flag é perfeitamente aceitável e muitas
vezes mais explícita.
Em resumo, a cláusula else em laços while e for é mais uma ferramenta no seu cinto
de utilidades Python. Ela é particularmente útil para executar um bloco de código quando
um laço termina sua execução “naturalmente”, sem ter sido interrompido por um break.

5.6 Laços Aninhados: Repetições Dentro de Repetições


Assim como as estruturas condicionais if podem ser colocadas umas dentro das outras
(aninhamento), os laços de repetição (for e while) também podem ser aninhados. Um
laço aninhado é simplesmente um laço que está contido dentro do corpo de outro laço.
Essa capacidade de aninhar laços nos permite lidar com problemas que têm uma
estrutura de repetição dentro de outra. Por exemplo, se você quer processar cada célula
de uma planilha (que tem linhas e colunas), você poderia usar um laço para percorrer as
linhas e, dentro dele, outro laço para percorrer as colunas de cada linha.

O que são Laços Aninhados?


A estrutura é conceitualmente simples. Veja um exemplo genérico com laços for:
1 for item_externo in se quencia_ externa :
2 # C ó digo do la ç o externo - Parte 1
3 # Este c ó digo executa uma vez para cada ’ item_externo ’
4 # ...
5
6 for item_interno in sequenc ia_inter na :
7 # C ó digo do la ç o interno ( corpo do la ç o interno )
8 # Este bloco de c ó digo executa COMPLETAMENTE
9 # ( todas as itera ç õ es da ’ sequenc ia_inter na ’)
10 # para CADA itera ç ã o do la ç o externo .
11 # ...
12

13 # C ó digo do la ç o externo - Parte 2 ( ap ó s o la ç o interno


completar )
14 # Este c ó digo tamb é m executa uma vez para cada ’ item_externo ’
5.6. Laços Aninhados: Repetições Dentro de Repetições 115

15 # ...
16 # C ó digo que continua ap ó s o la ç o externo ter completado todas as
suas itera ç õ es

Código 5.24: Estrutura genérica de laços for aninhados


Você pode aninhar for dentro de for (como acima), while dentro de while, for dentro
de while, ou while dentro de for. A lógica é a mesma.
A indentação continua sendo a chave para definir qual bloco de código pertence a qual
laço. Cada nível de aninhamento adiciona um novo nível de indentação (mais 4 espaços).

Como Funcionam os Laços Aninhados


A dinâmica de execução é a seguinte:

1. O laço externo inicia sua primeira iteração. A variável de iteração do laço externo
assume o valor do primeiro item da sua sequência.

2. Qualquer código no corpo do laço externo que venha antes do laço interno é executado.

3. O laço interno é então iniciado. Ele executará todas as suas iterações (percorrendo
todos os itens da sua própria sequência) do início ao fim.

4. Somente após o laço interno ter completado todas as suas iterações, o controle retorna
para o laço externo.

5. Qualquer código no corpo do laço externo que venha depois do laço interno é
executado.

6. O laço externo então prossegue para a sua próxima iteração (se houver). A variável
de iteração do laço externo assume o valor do próximo item.

7. O processo se repete: para esta nova iteração do laço externo, o laço interno é
novamente executado por completo.

Pense nos ponteiros de um relógio analógico: para cada volta completa que o ponteiro dos
minutos dá (laço externo), o ponteiro dos segundos deu 60 voltas completas (laço interno).
Ou pense em um calendário: para cada mês (laço externo), você percorre todos os dias
daquele mês (laço interno).

Exemplos Práticos
• Exemplo 1: Imprimindo todas as tabuadas de 1 até N
1 # Gerando tabuadas usando la ç os aninhados
2 l i m i t e _ d a _ t a b u a d a _ g e r a l = 3 # Queremos as tabuadas do 1 , 2
e 3
3
4 for n u me r o_ b a se _ ta b u ad a in range (1 ,
l i m i t e _ d a _ t a b u a d a _ g e r a l + 1) : # La ç o externo
5 print ( f " --- Tabuada do { nu m er o _b a s e_ t ab u a da } ---" )
6 for multiplicador in range (1 , 11) : # La ç o interno
( multiplicadores de 1 a 10)
7 resulta do_produ to = n u me r o_ b a se _ ta b u ad a *
multiplicador
116 Capítulo 5. Estruturas de Repetição (Laços)

8 print ( f " { n um e ro _ b as e _t a b ua d a } x { multiplicador } =


{ resultad o_produt o } " )
9 print ( " -" * 25) # Uma linha para separar as tabuadas

Código 5.25: Gerando tabuadas com laços for aninhados

• Exemplo 2: Iterando sobre coordenadas de uma “grade” ou “matriz”


(simulada)
1 # Simulando uma grade de 3 linhas e 4 colunas
2 numero_de_linhas = 3
3 numer o_de_col unas = 4
4

5 print ( " Coordenadas da grade ( linha , coluna ) : " )


6 for indice_linha in range ( numero_de_linhas ) : # La ç o
externo para as linhas (0 , 1 , 2)
7 for indice_coluna in range ( numero _de_colu nas ) : # La ç o
interno para as colunas (0 , 1 , 2 , 3)
8 # Imprime a coordenada e usa end =" " para n ã o pular
linha imediatamente
9 print ( f " ({ indice_linha } , { indice_coluna }) " , end = "
")
10 print () # Pula para uma nova linha ap ó s imprimir todas
as colunas da linha atual

Código 5.26: Iterando sobre coordenadas de uma grade

• Exemplo 3: Gerando todas as combinações de pares de itens de duas


listas
1 # Gerando combina ç õ es de cumprimentos e nomes
2 cumprimentos = [ " Ol á " , " Bom dia " , " Boa tarde " ]
3 nomes = [ " Ana " , " Bruno " ]
4
5 print ( " \ nPoss í veis sauda ç õ es : " )
6 for cumprim ento_atua l in cumprimentos : # La ç o externo
7 for nome_atual in nomes : # La ç o interno
8 print ( f " { cu mpriment o_atual } , { nome_atual }! " )

Código 5.27: Gerando combinações de pares com laços aninhados

Cuidados com Laços Aninhados


Embora poderosos, laços aninhados exigem alguns cuidados:

• Complexidade Lógica: A lógica pode se tornar mais difícil de acompanhar à


medida que você adiciona mais níveis de aninhamento. Tente limitar a profundidade
do aninhamento (2 ou 3 níveis é geralmente gerenciável).

• Performance: O número total de iterações do corpo do laço mais interno é o


produto do número de iterações de todos os laços. Se você tem um laço externo
que roda 1000 vezes e um interno que roda 1000 vezes, o bloco mais interno será
executado 1.000.000 de vezes! Para grandes volumes de dados, isso pode levar a um
desempenho lento.
5.6. Laços Aninhados: Repetições Dentro de Repetições 117

• Legibilidade: Indentação excessiva pode tornar o código visualmente poluído. Se


um laço aninhado estiver ficando muito grande ou complexo, considere se parte da
lógica do laço interno pode ser movida para uma função separada (veremos funções
no Capítulo 10).

Concluindo o Capítulo sobre Estruturas de Repetição


Neste capítulo, mergulhamos no mundo das repetições em Python. Vimos o laço
while, que repete enquanto uma condição é verdadeira, e o laço for, ideal para percorrer
sequências ou repetir um número definido de vezes com range(). Aprendemos sobre os
comandos break e continue para um controle mais fino do fluxo dos laços, exploramos a
curiosa cláusula else que pode ser usada com laços, e finalmente, vimos como aninhar
laços para lidar com repetições dentro de repetições.
As estruturas de repetição, junto com as estruturas condicionais que vimos no capítulo
anterior, formam a espinha dorsal da lógica de qualquer programa não trivial. Elas nos
dão o poder de criar programas que não apenas executam instruções, mas que também
tomam decisões e realizam tarefas complexas e repetitivas de forma eficiente.
Com esses fundamentos, estamos muito bem preparados para explorar as estruturas de
dados mais ricas que o Python oferece, como listas, tuplas e dicionários, que serão o foco
da nossa próxima parte do livro!
Parte II

Estruturas de Dados Fundamentais

118
Capítulo 6

Listas (Lists)

Nos capítulos anteriores, aprendemos os blocos de construção essenciais do Python:


como criar variáveis para armazenar dados individuais (como números, strings ou boo-
leanos), como usar operadores para manipular esses dados, e como controlar o fluxo de
nossos programas com estruturas condicionais e laços de repetição.
Até agora, cada variável que usamos guardava um único valor (ex: idade = 25, nome
= "Carlos"). Mas, e se precisarmos lidar com uma coleção de valores relacionados? Por
exemplo, uma lista de notas de um aluno, uma lista de nomes de convidados para uma
festa, ou uma sequência de temperaturas medidas ao longo do dia?
É aqui que entram as estruturas de dados. Elas são formas especializadas de
organizar, armazenar e gerenciar coleções de dados para que possam ser acessados e
modificados eficientemente. Python oferece várias estruturas de dados embutidas poderosas,
e a primeira (e talvez a mais fundamental) que vamos explorar é a lista.

6.1 Introdução às Listas: Coleções Ordenadas e Mutá-


veis
Imagine que você tem uma lista de compras para o supermercado escrita em um papel.
Essa lista tem algumas características importantes:

• Ela contém vários itens (ex: “maçãs”, “leite”, “pão”).

• Os itens têm uma certa ordem na qual você os escreveu.

• Você pode adicionar novos itens à lista, riscar itens que já pegou, ou até mesmo
mudar um item (ex: trocar “leite integral” por “leite desnatado”).

Em Python, uma lista (list) é uma estrutura de dados muito similar a essa ideia. Ela é
uma coleção ordenada e mutável de itens. Vamos entender o que cada termo significa:

• Coleção: Uma lista pode agrupar múltiplos itens (elementos) sob um único nome
de variável.

• Ordenada: Os itens em uma lista mantêm uma ordem específica. Cada item tem
uma posição (ou índice) definida, e essa ordem não muda a menos que você a altere
explicitamente. Você pode acessar os itens pela sua posição.

119
120 Capítulo 6. Listas (Lists)

• Mutável: “Mutável” significa que, após uma lista ser criada, você pode alterar seu
conteúdo. Você pode adicionar novos itens, remover itens existentes, ou modificar os
itens que já estão na lista.

Por que as Listas são Tão Úteis?


As listas são incrivelmente versáteis e usadas em uma infinidade de situações na
programação:

• Agrupar Dados Relacionados: Guardar uma série de leituras de um sensor, as


notas de todos os alunos de uma turma, os nomes dos arquivos em uma pasta, etc.

• Flexibilidade de Tipos: Uma única lista em Python pode conter itens de diferentes
tipos de dados (embora seja comum que listas contenham itens do mesmo tipo para
facilitar o processamento). Por exemplo, uma lista pode ter números, strings e até
mesmo outras listas dentro dela!

• Estrutura Dinâmica: Listas podem crescer e encolher dinamicamente à medida


que seu programa executa, adicionando ou removendo itens conforme necessário.

Diferentemente de uma variável simples que armazena apenas um valor por vez, uma
variável do tipo lista pode conter uma sequência inteira de valores, tornando muito mais
fácil gerenciar e operar sobre conjuntos de dados.
Neste capítulo, vamos aprender como criar listas, acessar e modificar seus elementos,
usar diversos métodos úteis que as listas oferecem e, finalmente, explorar uma forma Pythô-
nica muito elegante de criar listas chamada “compreensão de listas” (list comprehensions).
Prepare-se para desbloquear um novo nível de organização e poder para seus programas!

6.2 Criando Listas: Dando Vida às Nossas Coleções


Agora que entendemos o que é uma lista em Python – uma coleção ordenada e mutável
de itens – vamos aprender as diferentes maneiras de criá-las. Felizmente, Python torna
esse processo bastante simples e intuitivo.

A Sintaxe Principal: Usando Colchetes []


A forma mais comum e direta de criar uma lista em Python é colocar os itens desejados
separados por vírgulas, tudo dentro de um par de colchetes [].
A sintaxe geral é:
nome_da_lista = [item1, item2, item3, ...]

• Criando uma Lista Vazia:


Muitas vezes, você precisará criar uma lista que inicialmente não contém nenhum
item, para depois adicionar elementos a ela conforme seu programa executa. Para
criar uma lista vazia, basta usar os colchetes sem nada dentro:
1 # Criando uma lista vazia para tarefas pendentes
2 lista_de_tarefas = []
3 print ( f " Minha lista de tarefas inicial :
{ lista_de_tarefas } " )
6.2. Criando Listas: Dando Vida às Nossas Coleções 121

4 print ( f " O tipo da vari á vel ’ lista_de_tarefas ’ é :


{ type ( lista_de_tarefas ) } " )

Código 6.1: Criando uma lista vazia

A saída do type() confirmará que lista_de_tarefas é do tipo <class ’list’>.

• Criando Listas com Elementos:


Você pode popular uma lista com elementos desde o momento da sua criação. Esses
elementos podem ser de qualquer tipo de dado, e uma mesma lista pode até conter
tipos de dados diferentes.
1 # Lista de n ú meros inteiros ( representando notas de alunos )
2 n ot as_ pr ov a_p yt ho n = [8 , 7 , 9 , 10 , 6 , 8 , 9]
3 print ( f " Notas da turma : { no ta s_p ro va _py th on } " )
4
5 # Lista de strings ( representando nomes de cidades )
6 c i d a d e s _ c a p i t a i s _ s u d e s t e = [ " S ã o Paulo " , " Rio de Janeiro " ,
" Belo Horizonte " , " Vit ó ria " ]
7 print ( f " Capitais do Sudeste : { c i d a d e s _ c a p i t a i s _ s u d e s t e } " )
8

9 # Lista com tipos de dados mistos ( poss í vel , mas use com
modera ç ã o )
10 dados _funciona rio = [ " Jo ã o Silva " , 35 , 1.82 , True ,
" Analista de Sistemas " ]
11 # Aqui temos string , int , float , bool , string novamente
12 print ( f " Dados do funcion á rio : { dad os_funci onario } " )

Código 6.2: Criando listas com diferentes tipos de elementos

• Itens da Lista Podem ser Expressões:


Os elementos que você coloca dentro de uma lista durante sua criação podem ser
valores literais, variáveis, ou até mesmo expressões mais complexas. O Python
avaliará essas expressões e armazenará seus resultados na lista.
1 valor_x = 10
2 valor_y = 5
3 opera coes_bas icas = [ valor_x , valor_y , valor_x + valor_y ,
valor_x - valor_y , valor_x * valor_y ]
4 # A lista ser á : [10 , 5 , 15 , 5 , 50]
5 print ( f " Resultados das opera ç õ es : { oper acoes_ba sicas } " )

Código 6.3: Lista contendo expressões

Usando a Função Construtora list()


Outra maneira de criar listas é usandoafunção embutida list(). Ela é especialmente
útil quando você quer converter outros tipos de sequências (ou, de forma mais geral, outros
objetos “iteráveis”) em uma lista.

• Criando uma Lista Vazia com list():


1 print ( f " Outra lista vazia : { o u t r a _ l i s t a _ d e _ t a r e f a s _ v a z i a } " )

Código 6.4: Lista vazia com list()


122 Capítulo 6. Listas (Lists)

• Convertendo uma String em uma Lista de Caracteres:


1 palavra_chave = " Python "
2 li s ta _ de _ c ar a ct e r es = list ( palavra_chave )
3 print ( f " A string ’{ palavra_chave } ’ convertida em lista :
{ l i st a _d e _ ca r ac t e re s } " )
4 # Sa í da : A string ’ Python ’ convertida em lista : [ ’ P ’, ’y ’,
’t ’, ’h ’, ’o ’, ’n ’]

Código 6.5: Convertendo string para lista de caracteres

• Convertendo uma Tupla em Lista (Pequena Prévia):


As tuplas (que veremos em detalhes no Capítulo 7) são sequências ordenadas como
as listas, mas são imutáveis. A função list() pode convertê-las facilmente em listas.
1 # Uma tupla é definida com par ê nteses ( veremos no pr ó ximo
cap í tulo )
2 t u p l a _ de _ c o o r d e n a d a s = (10 , 20 , 30)
3 l i s t a _ a _ p a r t i r _ d e _ t u p l a = list ( t u p l a _ d e _ c o o r d e na d a s )
4 print ( f " Tupla original : { t u p l a _ d e _ c o o r d e n a d a s } " )
5 print ( f " Lista criada a partir da tupla :
{ lista_a_partir_de_tupla }")

Código 6.6: Convertendo tupla para lista

• Convertendo um Objeto range() em Lista:


A função range(), que usamos com laços for, não retorna uma lista diretamente.
Se você precisar de uma lista explícita dos números, use list(range(...)).
1 n u m e r o s _ d e _ z e r o _ a _ q u a t r o = list ( range (5) ) # range (5) gera
0, 1, 2, 3, 4
2 print ( f " Lista criada com range (5) :
{ numeros_de_zero_a_quatro }")
3
4 n u m e r o s _ i m p a r e s _ a t e _ d e z = list ( range (1 , 10 , 2) ) # De 1 a
9 , pulando de 2 em 2
5 print ( f " Lista de í mpares at é 10:
{ numeros_impares_ate_dez }")

Código 6.7: Convertendo range() para lista

Listas e Quebras de Linha no Código Fonte


Quando você está definindo uma lista muito longa no seu código, pode ser difícil de
ler se todos os itens estiverem em uma única linha. Python permite que você quebre a
definição da lista em múltiplas linhas, desde que as quebras ocorram dentro dos colchetes
[] (ou parênteses () para tuplas, ou chaves {} para dicionários e conjuntos). Isso melhora
muito a legibilidade.
1 # Lista de compras extensa , formatada para melhor leitura
2 lista_de_compras_semanal = [
3 " Arroz tipo 1 (5 kg ) " , " Feij ã o carioca (1 kg ) " , " A ç ú car refinado
(1 kg ) " ,
4 " Caf é em p ó tradicional (500 g ) " , " Ó leo de soja " , " Sal refinado " ,
5 " Leite integral UHT ( caixa com 12) " , # Coment á rio sobre um item
6 " P ã o de forma integral " , " Manteiga com sal " ,
6.3. Acessando Elementos da Lista: Índices 123

7 " Queijo mussarela fatiado (200 g ) " , " Presunto cozido fatiado (200 g ) " ,
8 " Ma ç ã s Gala (6 unidades ) " , " Bananas Prata (1 d ú zia ) " , " Laranjas
Pera (1 kg ) " ,
9 " Tomates " , " Alface crespa " , " Cebolas " ,
10 ]
11

12 print ( f " Minha lista de compras semanal tem


{ len ( l i s t a _ d e _ c o m p r a s _ s e m a n a l ) } itens . " )
13 # Podemos at é imprimir alguns itens para verificar
14 print ( f " Primeiro item : { l i s t a _ d e _ c o m p r a s _ s e m a n a l [0]} " ) # Acesso ao
primeiro item
15 print ( f " Ú ltimo item : { l i s t a _ d e _ c o m p r a s _ s e m a n a l [ -1]} " ) # Acesso ao ú
ltimo item

Código 6.8: Definindo uma lista longa em múltiplas linhas


A indentação dos itens nas linhas subsequentes é uma questão de estilo para legibilidade;
o importante é que as quebras de linha estejam dentro dos colchetes.
Com estas técnicas, você já pode criar uma grande variedade de listas para começar a
organizar e agrupar os dados em seus programas Python. Na próxima seção, aprenderemos
como acessar os itens individuais que guardamos dentro dessas listas.

6.3 Acessando Elementos da Lista: Índices


Uma vez que temos uma lista criada e populada com itens, como fazemos para
acessar um item específico dentro dela? Se a lista frutas contém ["maçã", "banana",
"laranja"], como pegamos apenas a “banana”? A resposta está no uso de índices.
Como vimos, as listas em Python são coleções ordenadas. Essa ordem significa que
cada item na lista tem uma “posição” bem definida. Essa posição é representada por um
número inteiro chamado índice.

6.3.1 Indexação Baseada em Zero: O Ponto de Partida


Este é um dos conceitos mais fundamentais (e às vezes uma pequena fonte de confusão
para iniciantes) na maioria das linguagens de programação, incluindo Python: a indexação
é baseada em zero.
Isso significa que:

• O primeiro elemento de uma lista está no índice 0.

• O segundo elemento está no índice 1.

• O terceiro elemento está no índice 2.

• E assim por diante...

• Se uma lista tem N elementos, o último elemento estará no índice N-1.

Pode parecer um pouco estranho no começo se você está acostumado a contar a partir do
1, mas essa é uma convenção muito comum em computação. Pense nisso como o térreo
de um prédio sendo o andar “0” em alguns lugares, ou o ponto de partida de uma régua.
Entender e se acostumar comaindexação baseada em zero é crucial para trabalhar com
listas (e outras sequências) em Python.
124 Capítulo 6. Listas (Lists)

6.3.2 Acessando Elementos com Índices Positivos


Para acessar um elemento específico de uma lista, você usa o nome da variável da lista
seguido por colchetes [], e dentro dos colchetes você coloca o índice do elemento desejado.
A sintaxe é: nome_da_lista[indice_positivo]
Vamos ver alguns exemplos:
1 # Lista de planetas para nossos exemplos
2 planetas_ rochosos = [ " Merc ú rio " , " V ê nus " , " Terra " , " Marte " ]
3 # Í ndices Positivos : 0 1 2 3
4

5 # Acessando o primeiro planeta ( í ndice 0)


6 primeiro_planeta = p lanetas_ rochosos [0]
7 print ( f " O primeiro planeta rochoso é : { primeiro_planeta } " ) # Sa í da :
Merc ú rio
8
9 # Acessando o planeta Terra ( í ndice 2)
10 nosso_planeta = planetas _rochoso s [2]
11 print ( f " Nosso planeta é : { nosso_planeta } " ) # Sa í da : Terra
12
13 # Acessando o ú ltimo planeta usando len ()
14 # Se a lista tem 4 elementos , o ú ltimo í ndice é len ( lista ) - 1 = 4
- 1 = 3
15 ul ti mo_ pl an eta _i dx = len ( pla netas_ro chosos ) - 1
16 ultimo_planeta = planetas_ rochosos [ ul ti mo _pl an et a_i dx ]
17 print ( f " O ú ltimo planeta rochoso ( í ndice { u lt im o_p la ne ta_ id x }) é :
{ ultimo_planeta } " ) # Sa í da : Marte

Código 6.9: Acessando elementos da lista com índices positivos

6.3.3 Acessando Elementos com Índices Negativos


Python oferece uma maneira muito conveniente, elegante e poderosa de acessar os
elementos de uma lista contando a partir do final da lista. Para isso, utilizamos índices
negativos:

• nome_da_lista[-1]: Refere-se ao último elemento da lista.

• nome_da_lista[-2]: Refere-se ao penúltimo elemento.

• nome_da_lista[-3]: Refere-se ao antepenúltimo elemento, e assim por diante.

Essa forma de indexação é incrivelmente útil, especialmente quando você quer manipular
os últimos itens de uma lista sem precisar calcular explicitamente seus índices positivos
usando len().
Como Funciona a Lógica Negativa?
Você pode pensar no índice -n como sendo equivalente ao índice len(lista) - n. Por
exemplo, em uma lista com 5 elementos:

• lista[-1] é o mesmo que lista[len(lista) - 1] ou lista[5 - 1], que é lista[4].

• lista[-2] é o mesmo que lista[len(lista) - 2] ou lista[5 - 2], que é lista[3].

E o primeiro elemento, que é lista[0], também pode ser acessado por lista[-len(lista)].
Vamos ver mais exemplos:
6.3. Acessando Elementos da Lista: Índices 125

1 # Lista de etapas de um projeto


2 etapas_projeto = [ " Planejamento " , " Desenvolvimento " , " Testes " ,
" Implanta ç ã o " , " Manuten ç ã o " ]
3 # Í ndices Positivos : 0 1 2
3 4
4 # Í ndices Negativos : -5 -4 -3
-2 -1
5
6 # Acessando o ú ltimo elemento ( fase de Manuten ç ã o )
7 ultima_etapa = etapas_projeto [ -1]
8 print ( f " A ú ltima etapa do projeto é : ’{ ultima_etapa } ’ " )
9
10 # Acessando o pen ú ltimo elemento ( fase de Implanta ç ã o )
11 penultima_etapa = etapas_projeto [ -2]
12 print ( f " A pen ú ltima etapa é : ’{ penultima_etapa } ’ " )
13
14 # Acessando a primeira etapa usando í ndice negativo
15 pr im eir a_ et apa _n eg = etapas_projeto [ - len ( etapas_projeto ) ] #
Equivalente a etapas_projeto [ -5]
16 print ( f " A primeira etapa ( usando í ndice negativo ) é :
’{ p ri me ira _e ta pa_ ne g } ’ " )
17
18 # Acessando o terceiro elemento ( ’ Testes ’) usando í ndice negativo
19 te rc eir a_ et apa _n eg = etapas_projeto [ -3]
20 print ( f " A terceira etapa ( usando í ndice -3) é :
’{ t er ce ira _e ta pa_ ne g } ’ " )
21

22 # Modificando o ú ltimo elemento usando í ndice negativo ( listas s ã o


mut á veis !)
23 print ( f " Lista antes da modifica ç ã o : { etapas_projeto } " )
24 etapas_projeto [ -1] = " Monitoramento Cont í nuo " # Substituindo
" Manuten ç ã o "
25 print ( f " Lista ap ó s modificar o ú ltimo item : { etapas_projeto } " )

Código 6.10: Mais exemplos de indexação negativa em listas

Benefícios da Indexação Negativa:

• Conveniência: É muito mais direto escrever minha_lista[-1] do que


minha_lista[len(minha_lista) - 1].

• Legibilidade: Em certos contextos, pode tornar o código mais claro ao expressar a


intenção de “pegar o último” ou “o segundo do final”.

• Robustez: Funciona bem mesmo que o tamanho da lista mude, desde que a lista
não esteja vazia e o índice negativo esteja dentro dos limites.

6.3.4 Cuidado: Erro de Índice Fora do Intervalo (IndexError)


Seja usando índices positivos ou negativos, o que acontece se você tentar acessar uma
posição que não existe na lista? Por exemplo, se uma lista tem 3 elementos (índices
positivos 0, 1, 2; e índices negativos -1, -2, -3) e você tenta acessar minha_lista[3]
(positivo) ou minha_lista[-4] (negativo)?
Nesses casos, o Python levantará um erro chamado IndexError, e seu programa será
interrompido.
126 Capítulo 6. Listas (Lists)

1 # Exemplo que causaria um IndexError


2 el em ent os _q uim ic os = [ " Hidrog ê nio " , " H é lio " , " L í tio " ]
3 # Í ndices positivos v á lidos : 0 , 1 , 2
4 # Í ndices negativos v á lidos : -1 ( L í tio ) , -2 ( H é lio ) , -3 ( Hidrog ê nio )
5
6 # As linhas abaixo , se descomentadas e executadas , GERARIAM UM ERRO
( IndexError ) :
7 # print ( e le me nto s_ qu imi co s [3]) # Tentando acessar o í ndice 3 , que
n ã o existe .
8 # print ( e le me nto s_ qu imi co s [ -4]) # Tentando acessar o í ndice -4 , que
n ã o existe .

Código 6.11: Exemplo que causaria IndexError (código comentado)

É fundamental garantir que os índices que você utiliza para acessar elementos de uma
lista estejam sempre dentro do intervalo válido para o tamanho atual da lista. Um índice
positivo idx é válido se: 0 <= idx < len(lista). Um índice negativo neg_idx é válido
se: -len(lista) <= neg_idx < 0.

6.3.5 Usando Elementos Acessados da Lista


Uma vez que você acessa um elemento de uma lista usando seu índice (seja ele positivo
ou negativo), esse elemento se comporta exatamente como qualquer outro valor do seu
respectivo tipo:

• Você pode atribuí-lo a outra variável.

• Usá-lo em expressões aritméticas (se for um número).

• Concatená-lo (se for uma string, usando + com outra string).

• Passá-lo como argumento para funções.

• Usá-lo em condições if.

1 # Usando elementos de uma lista em diversas opera ç õ es


2 pontuacoe s_rodada = [150 , 220 , 90 , 310 , 180]
3
4 # Pegando a maior pontua ç ã o ( ú ltimo item , supondo que queremos o ú
ltimo registrado )
5 m a i o r _ p o n t u a c a o _ r e g i s t r a d a = pontu acoes_ro dada [ -1]
6 print ( f " Maior pontua ç ã o registrada na ú ltima rodada :
{ maior_pontuacao_registrada }")
7
8 # Somando a primeira e a segunda pontua ç ã o
9 s o ma _du a s_ p r im e ir a s = po ntuacoes _rodada [0] + pont uacoes_r odada [1]
10 print ( f " Soma das duas primeiras pontua ç õ es : { so m a_ d ua s _ pr i me i r as } " )
11
12 # Verificando se a pontua ç ã o do meio é maior que uma meta
13 meta = 100
14 # Para uma lista de 5 itens , len é 5 , 5//2 é 2 ( í ndice do meio ) .
15 indice_meio = len ( p ontuacoe s_rodada ) // 2
16 pontuacao_meio = pontuacoe s_rodada [ indice_meio ]
17
18 if pontuacao_meio > meta :
6.4. Fatiamento de Listas (Slicing): Obtendo Sublistas 127

19 print ( f " A pontua ç ã o do meio ({ pontuacao_meio }) superou a meta


de { meta }! " )
20 else :
21 print ( f " A pontua ç ã o do meio ({ pontuacao_meio }) n ã o superou a
meta de { meta }. " )

Código 6.12: Utilizando elementos de lista em diversas operações


A indexação, tanto positiva quanto negativa, é a ferramenta fundamental para resgatar
e trabalhar com itens específicos guardados dentro de suas listas. Compreender bem
como ela funciona, especialmente a contagem a partir do zero e a flexibilidade dos índices
negativos, é essencial para manipular listas de forma eficaz em seus programas Python.
Na próxima seção, veremos como pegar não apenas um, mas vários elementos de uma vez:
as “fatias” de uma lista.

6.4 Fatiamento de Listas (Slicing): Obtendo Sublistas


Além de acessar um único elemento de uma lista por vez usando seu índice, Python
nos oferece uma técnica muito poderosa e flexível chamada fatiamento (ou slicing, em
inglês). O fatiamento permite que você extraia uma porção da lista, ou seja, uma sublista
contendo um conjunto contíguo (ou espaçado) de elementos da lista original.
O resultado de uma operação de fatiamento é sempre uma nova lista. Isso significa
que modificar a fatia não altera a lista original, e vice-versa (a menos que os itens da lista
sejam eles mesmos objetos mutáveis, como outras listas, mas não vamos nos preocupar
com isso por enquanto).

Sintaxe Básica do Fatiamento: [inicio:fim]


A forma mais comum de fatiar uma lista utiliza dois índices separados por dois pontos
(:), dentro dos colchetes:
nova_lista = minha_lista[inicio:fim]

• inicio: É o índice do primeiro elemento que será incluído na fatia. A contagem


começa a partir deste índice. Se o inicio for omitido, o fatiamento começa do
primeiro elemento da lista (índice 0).

• fim: É o índice do primeiro elemento que NÃO será incluído na fatia. Ou seja, a
fatia vai até o elemento anterior ao índice fim. Se o fim for omitido, o fatiamento
vai até o último elemento da lista (inclusive).

Vamos ver com exemplos:


1 # Lista de exemplo para fatiamento
2 letras_alfabeto = [ ’a ’ , ’b ’ , ’c ’ , ’d ’ , ’e ’ , ’f ’ , ’g ’ , ’h ’ , ’i ’ , ’j ’]
3 # Í ndices : 0 1 2 3 4 5 6 7 8 9
4
5 # Pegando os primeiros 4 elementos ( í ndices 0 , 1 , 2 , 3)
6 primeiras_quatro = letras_alfabeto [0:4] # ou simplesmente
letras_alfabeto [:4]
7 print ( f " Primeiras quatro letras : { primeiras_quatro } " )
8
9 # Omitindo o ’ inicio ’
128 Capítulo 6. Listas (Lists)

10 p r i m e i r a s _ q u a t r o _ a lt = letras_alfabeto [:4]
11 print ( f " Primeiras quatro ( alternativo ) : { p r i m e i r a s _ q u a t r o_ a l t } " )
12
13 # Pegando elementos do meio ( do í ndice 2 ao í ndice 5 , inclusive )
14 e lementos _do_meio = letras_alfabeto [2:6]
15 print ( f " Elementos do meio ( í ndices 2 a 5) : { elemen tos_do_m eio } " )
16
17 # Pegando elementos de um í ndice espec í fico at é o final da lista
18 d o _ i n d i c e _ 5 _ e m _ d i a n t e = letras_alfabeto [5:]
19 print ( f " Do í ndice 5 em diante : { d o _ i n d i c e _ 5 _ e m _ d i a n t e } " )
20
21 # Criando uma c ó pia completa da lista
22 c o p i a _ c o m p l e t a _ l e t r a s = letras_alfabeto [:]
23 print ( f " C ó pia completa da lista : { c o p i a _ c o m p l e t a _ l e t r a s } " )
24
25 # Importante : a c ó pia é uma NOVA lista
26 print ( f " A c ó pia é o mesmo objeto que a original ?
{ c o p i a _ c o m p l e t a _ l e t r a s is letras_alfabeto } " )

Código 6.13: Exemplos básicos de fatiamento de listas

Fatiamento com Passo (Stride): [inicio:fim:passo]


A sintaxe completa do fatiamento permite um terceiro argumento, o passo (ou stride),
que especifica de quantos em quantos elementos queremos pular.

nova_lista = minha_lista[inicio:fim:passo]

• passo: O intervalo entre os elementos a serem selecionados. Se omitido, o padrão é


1.

1 # Lista de n ú meros para exemplificar o passo


2 n u me ros _ se q u en c ia i s = [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10]
3
4 # Pegando todos os elementos da lista com passo 2
5 indices_pares = n u me r os _ s eq u en c ia i s [::2] # inicio e fim omitidos
6 print ( f " Elementos em í ndices pares : { indices_pares } " )
7
8 # Pegando elementos do í ndice 1 ao 8 , com passo 3
9 su bl ist a_ co m_p as so = n um e ro s _ se q ue n c ia i s [1:9:3]
10 print ( f " Sublista de 1 a 8 com passo 3: { s ubl is ta _co m_ pa sso } " )
11
12 # Usando um passo negativo para inverter uma lista
13 l i s t a _ i n v e r t i d a _ c o m p l e t a = nu m er o s _s e qu e n ci a is [:: -1]
14 print ( f " Lista invertida completamente : { l i s t a _ i n v e r t i d a _ c o m p l e t a } " )
15
16 # Invertendo uma subparte da lista
17 letras_alfabeto = [ ’a ’ , ’b ’ , ’c ’ , ’d ’ , ’e ’ , ’f ’ , ’g ’ , ’h ’ , ’i ’ , ’j ’]
18 su bp art e_ in ver ti da = letras_alfabeto [5:1: -1] # Do ’f ’ at é antes do
’b ’, invertido
19 print ( f " Letras de ’f ’ a ’c ’ invertidas : { s ub par te _i nve rt id a } " )

Código 6.14: Fatiamento de listas com passo


6.4. Fatiamento de Listas (Slicing): Obtendo Sublistas 129

Fatiamento com Índices Negativos


Os índices negativos que aprendemos na seção anterior também podem ser usados nos
campos inicio e fim do fatiamento, tornando-o ainda mais flexível para trabalhar com o
final da lista.
1 # Lista de exemplo
2 letras_alfabeto = [ ’a ’ , ’b ’ , ’c ’ , ’d ’ , ’e ’ , ’f ’ , ’g ’ , ’h ’ , ’i ’ , ’j ’]
3
4 # Pegar os ú ltimos 3 elementos da lista
5 ultimos_tres = letras_alfabeto [ -3:]
6 print ( f " Os ú ltimos tr ê s elementos : { ultimos_tres } " )
7
8 # Pegar todos os elementos , exceto os dois ú ltimos
9 e x ce to_ d oi s _ ul t im o s = letras_alfabeto [: -2]
10 print ( f " Todos , exceto os dois ú ltimos : { ex c e to _ do i s _u l ti m os } " )
11

12 # Pegar elementos entre o quarto do fim ( í ndice -4) e o segundo do


fim ( í ndice -2 , exclusivo )
13 entre_negativos = letras_alfabeto [ -4: -2]
14 print ( f " Elementos entre -4 ( inclusivo ) e -2 ( exclusivo ) :
{ entre_negativos } " )

Código 6.15: Fatiamento de listas usando índices negativos

Fatias Criam Novas Listas (Cópias Rasas)


É importante reforçar: quando você faz um fatiamento, o Python cria uma nova lista
contendo os elementos selecionados. A lista original permanece inalterada.
1 numeros_o riginais = [10 , 20 , 30 , 40 , 50]
2 fatia_numeros = numeros_ originai s [1:4] # fatia_numeros ser á [20 ,
30 , 40]
3
4 print ( f " Lista Original : { num eros_ori ginais } " )
5 print ( f " Fatia da Lista : { fatia_numeros } " )
6

7 # Modificando a fatia
8 fatia_numeros [0] = 99 # Modifica o primeiro elemento da fatia ( que
era 20)
9 print ( f " Fatia Modificada : { fatia_numeros } " )
10 print ( f " Lista Original ( inalterada ) : { n umeros_o riginais } " )

Código 6.16: Fatias são novas listas


Essa “nova lista” criada pelo fatiamento é uma cópia rasa (shallow copy). Se os elementos
da sua lista forem tipos simples como números ou strings, isso funciona como uma cópia
independente. No entanto, se os elementos da sua lista forem outros objetos mutáveis
(como outras listas), tanto a lista original quanto a fatia conterão referências para os
mesmos objetos internos.

Atribuição a Fatias: Modificando a Lista Original


Embora obter uma fatia (ler) crie uma nova lista,anotação de fatiamento pode ser
usada no lado esquerdo de uma atribuição para modificar a lista original de maneiras
poderosas:
130 Capítulo 6. Listas (Lists)

• Substituir uma porção da lista:


1 l i s t a _ pa r a _ m o d i f i c a r = [ ’a ’ , ’b ’ , ’c ’ , ’d ’ , ’e ’]
2 print ( f " Original : { l i s t a _ pa r a _ m o d i f i c a r } " )
3 l i s t a _ pa r a _ m o d i f i c a r [1:4] = [ ’X ’ , ’Y ’ , ’Z ’] # Substitui b ,
c, d
4 print ( f " Ap ó s substituir [1:4]: { l i s t a _ p a r a _ mo d i f i c a r } " )
5 # Sa í da : [ ’ a ’, ’X ’, ’Y ’, ’Z ’, ’e ’]

Código 6.17: Substituindo uma fatia da lista

• Deletar uma porção da lista: Atribua uma lista vazia [] à fatia.


1 l i s t a _ pa r a _ m o d i f i c a r = [ ’a ’ , ’X ’ , ’Y ’ , ’Z ’ , ’e ’] #
Continua ç ã o do exemplo anterior
2 l i s t a _ pa r a _ m o d i f i c a r [1:4] = [] # Remove X , Y , Z
3 print ( f " Ap ó s deletar [1:4]: { l i s t a _ p a r a _ m o d i fi c a r } " ) #
Sa í da : [ ’ a ’, ’e ’]

Código 6.18: Deletando uma fatia da lista

• Inserir elementos sem substituir: Atribua à uma fatia “vazia” (onde inicio ==
fim).
1 l i s t a _ pa r a _ m o d i f i c a r = [ ’a ’ , ’e ’] # Continua ç ã o
2 l i s t a _ pa r a _ m o d i f i c a r [1:1] = [ ’b ’ , ’c ’ , ’d ’] # Insere antes
do elemento no í ndice 1 ( ’ e ’)
3 print ( f " Ap ó s inserir em [1:1]: { l i s t a _ p a r a_ m o d i f i c a r } " )
4 # Sa í da : [ ’ a ’, ’b ’, ’c ’, ’d ’, ’e ’]

Código 6.19: Inserindo elementos com fatiamento

• Limpar toda a lista:


1 l i s t a _ pa r a _ m o d i f i c a r = [ ’a ’ , ’b ’ , ’c ’ , ’d ’ , ’e ’] #
Reiniciando
2 l i s t a _ pa r a _ m o d i f i c a r [:] = [] # Atribui uma lista vazia à
fatia de toda a lista
3 print ( f " Lista limpa : { l is t a _ p a r a _ m o d i f i c a r } " ) # Sa í da : []

Código 6.20: Limpando uma lista com fatiamento

A atribuição a fatias é uma técnica avançada e flexível para manipular o conteúdo de listas
diretamente.
O fatiamento é uma das características mais expressivas e úteis do Python para
trabalhar com porções de listas de forma flexível e concisa. Dominá-lo tornará seu código
mais Pythônico e eficiente.

6.5 Modificando Listas (Listas são Mutáveis!)


Uma das características mais importantes e poderosas das listas em Python é a sua
mutabilidade. Como mencionamos na introdução deste capítulo, “mutável” significa
que, após uma lista ser criada, seu conteúdo pode ser alterado: podemos modificar itens
existentes, adicionar novos itens e remover itens que não são mais necessários. Isso
6.5. Modificando Listas (Listas são Mutáveis!) 131

diferencia as listas de outros tipos de sequência em Python, como strings e tuplas (que
veremos no próximo capítulo), que são imutáveis.
A capacidade de modificar uma lista “in-place” (ou seja, na própria variável original,
sem precisar criar uma lista inteiramente nova para cada pequena alteração) torna as listas
extremamente flexíveis e eficientes para muitas tarefas de programação.
Vamos explorar as principais formas de modificar listas:

6.5.1 Alterando um Elemento Existente na Lista


A maneira mais direta de modificar um item que já existe em uma lista é usando seu
índice em uma operação de atribuição, da mesma forma que atribuímos valores a variáveis.
A sintaxe é: minha_lista[indice] = novo_valor
O elemento que estava originalmente na posição indice será substituído pelo novo_valor.
Você pode usar tanto índices positivos quanto negativos.
1 # Lista de cores
2 cores_dis poniveis = [ " vermelho " , " verde " , " azul " , " amarelo " ]
3 print ( f " Lista original de cores : { cores_di sponivei s } " )
4
5 # Alterando o segundo elemento ( í ndice 1) de " verde " para " roxo "
6 cores_dis poniveis [1] = " roxo "
7 print ( f " Ap ó s alterar o í ndice 1: { c ores_dis poniveis } " )
8
9 # Alterando o ú ltimo elemento ( í ndice -1) de " amarelo " para
" laranja "
10 c ores_dis poniveis [ -1] = " laranja "
11 print ( f " Ap ó s alterar o ú ltimo elemento ( í ndice -1) :
{ cores_d isponivei s } " )

Código 6.21: Alterando elementos de uma lista por índice

6.5.2 Adicionando Elementos à Lista


Existem várias maneiras de adicionar novos elementos a uma lista existente:

• Método append(item):
Este é o método mais comum para adicionar um único item ao final da lista. Ele
modifica a lista original (é uma operação “in-place”).
1 numeros_pares = [2 , 4 , 6]
2 print ( f " Lista de pares inicial : { numeros_pares } " )
3

4 numeros_pares . append (8) # Adiciona 8 ao final


5 print ( f " Ap ó s append (8) : { numeros_pares } " )
6
7 numeros_pares . append (10) # Adiciona 10 ao final
8 print ( f " Ap ó s append (10) : { numeros_pares } " )

Código 6.22: Usando o método append() para adicionar ao final

• Método insert(indice, item):


Se você precisa adicionar um item em uma posição específica da lista, pode usar o
método insert(). Ele recebe dois argumentos: o indice onde o novo item será
inserido, e o item em si.
132 Capítulo 6. Listas (Lists)

1 letras = [ ’a ’ , ’c ’ , ’d ’ , ’f ’]
2 print ( f " Letras inicialmente : { letras } " )
3

4 # Inserindo ’b ’ no í ndice 1 ( entre ’a ’ e ’c ’)


5 letras . insert (1 , ’b ’)
6 print ( f " Ap ó s insert (1 , ’b ’) : { letras } " )
7
8 # Inserindo ’Z ’ no in í cio da lista ( í ndice 0)
9 letras . insert (0 , ’Z ’)
10 print ( f " Ap ó s insert (0 , ’Z ’) : { letras } " )
11
12 # Inserindo ’g ’ no final da lista ( usando len () para o í
ndice )
13 letras . insert ( len ( letras ) , ’g ’) # Equivalente a
letras . append ( ’ g ’)
14 print ( f " Ap ó s insert no final : { letras } " )

Código 6.23: Usando o método insert() para adicionar em posição específica

• Concatenando com + ou Estendendo com += (ou extend()):

– O operador + pode ser usado para juntar duas listas, mas ele cria uma nova
lista como resultado.
– Para adicionar todos os itens de uma lista (ou outro iterável) ao final de uma
lista existente, modificando a lista original, use += ou o método extend().
1 lista_numeros1 = [10 , 20]
2 lista_numeros2 = [30 , 40]
3
4 # Usando + para criar uma nova lista
5 lista_combinada = lista_numeros1 + lista_numeros2
6 print ( f " Lista 1: { lista_numeros1 } " )
7 print ( f " Lista 2: { lista_numeros2 } " )
8 print ( f " Lista Combinada (+) : { lista_combinada } " )
9
10 # Usando extend () para modificar a lista original
11 l i s t a _n u m e r o s 1 _ c o p i a = lista_numeros1 . copy ()
12 l i s t a _n u m e r o s 1 _ c o p i a . extend ( lista_numeros2 )
13 print ( f " Lista 1 ( c ó pia ) ap ó s extend ( lista2 ) :
{ lista_numeros1_copia }")
14
15 # Usando += ( similar ao extend para listas )
16 l i s t a _ n u m e r o s _ o u t r a _ c o p i a = [1 , 2]
17 l i s t a _ n u m e r o s _ o u t r a _ c o p i a += [3 , 4 , 5]
18 print ( f " Lista ap ó s += : { l i s t a _ n u m e r o s _ o u t r a _ c o p i a } " )

Código 6.24: Concatenando e estendendo listas

A diferença entre append() e extend() é sutil mas importante: se você usar


[Link](lista2), a lista2 inteira será adicionada como um único ele-
mento aninhado dentro da lista1. O extend() adiciona os elementos individuais
da lista2 à lista1.
1 minha_lista_A = [1 , 2]
2 minha_lista_B = [3 , 4]
3 minha_lista_A . append ( minha_lista_B )
6.5. Modificando Listas (Listas são Mutáveis!) 133

4 print ( f " Resultado de append ( lista ) : { minha_lista_A } " ) #


Sa í da : [1 , 2 , [3 , 4]]
5
6 minha_lista_C = [1 , 2]
7 minha_lista_D = [3 , 4]
8 minha_lista_C . extend ( minha_lista_D )
9 print ( f " Resultado de extend ( lista ) : { minha_lista_C } " ) #
Sa í da : [1 , 2 , 3 , 4]

Código 6.25: append() vs extend() com listas

6.5.3 Removendo Elementos da Lista


Assim como adicionamos, também podemos remover elementos de uma lista de várias
formas:

• Instrução del lista[indice]:


A instrução del (de “delete”) remove o elemento que está no indice especificado. Ela
modifica a lista original. Você também pode usar del para remover fatias inteiras.
1 instrumentos = [ " Viol ã o " , " Piano " , " Bateria " , " Flauta " ,
" Baixo " ]
2 print ( f " Instrumentos ( original ) : { instrumentos } " )
3
4 # Removendo " Piano " ( que est á no í ndice 1)
5 del instrumentos [1]
6 print ( f " Ap ó s ’ del instrumentos [1] ’: { instrumentos } " )
7

8 # Removendo os dois ú ltimos elementos usando fatiamento


com del
9 del instrumentos [ -2:]
10 print ( f " Ap ó s ’ del instrumentos [ -2:] ’: { instrumentos } " )

Código 6.26: Usando del para remover elementos

• Método pop(indice_opcional):
O método pop() remove um elemento da lista e também retorna o elemento que
foi removido.

– Se nenhum indice é fornecido, remove e retorna o último elemento.


– Se um indice é fornecido, remove e retorna o elemento daquela posição.
– Gera IndexError se a lista estiver vazia ou o índice for inválido.

1 pilha_de_livros = [ " Livro A " , " Livro B " , " Livro C " , " Livro
D"]
2 print ( f " Pilha de livros : { pilha_de_livros } " )
3
4 livro_do_topo = pilha_de_livros . pop () # Remove e retorna
" Livro D "
5 print ( f " Livro removido do topo : ’{ livro_do_topo } ’ " )
6 print ( f " Pilha restante : { pilha_de_livros } " )
7
8 p r i m e i r o _ l i v r o _ d a _ p i l h a = pilha_de_livros . pop (0) # Remove
e retorna " Livro A "
134 Capítulo 6. Listas (Lists)

9 print ( f " Primeiro livro removido :


’{ p r i m e i r o _ l i v r o _ d a _ p i l h a } ’ " )
10 print ( f " Pilha restante : { pilha_de_livros } " )

Código 6.27: Usando o método pop()

• Método remove(valor):
Se você não sabe o índice de um item, mas sabe o seu valor e quer removê-lo,
pode usar o método remove(). Ele procura pela primeira ocorrência do valor
especificado na lista e o remove.

– Se o valor não for encontrado, levanta um ValueError.

1 lista_de_compras = [ " Ma ç ã " , " Banana " , " Leite " , " P ã o " ,
" Banana " , " Queijo " ]
2 print ( f " Lista de compras inicial : { lista_de_compras } " )
3
4 # Removendo a primeira ocorr ê ncia de " Banana "
5 lista_de_compras . remove ( " Banana " )
6 print ( f " Ap ó s remover ’ Banana ’: { lista_de_compras } " )
7
8 # Tentando remover um item que n ã o existe ( geraria
ValueError )
9 # lista_de_compras . remove (" Chocolate ")

Código 6.28: Usando o método remove()

A capacidade de alterar, adicionar e remover itens torna as listas estruturas de dados


dinâmicas e adaptáveis, perfeitas para uma ampla gama de aplicações onde os dados
mudam ao longo do tempo. Na próxima seção, exploraremos outros métodos úteis que as
listas oferecem para manipulação e consulta.

6.6 Métodos Comuns de Listas: Ferramentas Poderosas


para Manipulação
Além das operações de modificação (adicionar, remover, alterar) e fatiamento que
vimos na seção anterior, as listas em Python vêm com uma rica coleção de métodos
embutidos que nos permitem realizar uma variedade de tarefas comuns de forma eficiente.
Lembre-se que um método é uma função que “pertence” a um objeto (neste caso, a
uma lista) e é chamado usando a sintaxe minha_lista.nome_do_metodo(argumentos).
Alguns desses métodos modificam a lista original (são chamados de operações “in-place”),
enquanto outros retornam um novo valor ou uma nova lista, deixando a original intacta.
Vamos explorar alguns dos mais utilizados:

6.6.1 Revisitando len(lista): Obtendo o Tamanho da Lista


Embora len() não seja tecnicamente um método de lista (é uma função embutida
do Python que se aplica a vários tipos de sequências e coleções), é tão fundamental
ao trabalhar com listas que vale a pena relembrá-la aqui. A função len(minha_lista)
retorna o número de itens (elementos) presentes na lista.
6.6. Métodos Comuns de Listas: Ferramentas Poderosas para Manipulação 135

1 # Usando len () com listas


2 p l a n e t a s _ s i s t e m a _ s o l a r = [ " Merc ú rio " , " V ê nus " , " Terra " , " Marte " ,
" J ú piter " , " Saturno " , " Urano " , " Netuno " ]
3 nu me ro_ de _p lan et as = len ( p l a n e t a s _ s i s t e m a _ s o l a r )
4 print ( f " Nossa lista cont é m { n um ero _d e_ pla ne ta s } planetas . " )
5
6 lista_vaz ia_teste = []
7 print ( f " Uma lista vazia tem { len ( lista_va zia_test e ) } elementos . " )

Código 6.29: Usando len() com listas

6.6.2 Ordenando Listas


Python oferece duas maneiras principais de ordenar os elementos de uma lista:

• Método minha_lista.sort() (Ordenação “In-Place”)


O método sort() ordena os elementos da lista minha_lista diretamente.

– Por padrão,aordenação é crescente.


– Importante: sort() modifica a lista original e retorna None.
– Os elementos devem ser comparáveis entre si (ex: todos números, ou todas
strings).
– Use reverse=True para ordem decrescente.

1 n um ero s_ al eat or io s = [52 , 11 , 8 , 103 , 34 , 8]


2 print ( f " Lista original de n ú meros : { n ume ro s_ ale at or io s } " )
3
4 n um ero s_ al eat or io s . sort () # Ordena a lista
’ n um er os_ al ea tor io s ’ diretamente
5 print ( f " Lista ap ó s sort () ( crescente ) :
{ nu me ro s_a le at ori os } " )
6
7 nomes_alunos = [ " Carlos " , " Beatriz " , " Daniel " , " Ana " ]
8 print ( f " \ nLista original de nomes : { nomes_alunos } " )
9
10 nomes_alunos . sort ()
11 print ( f " Nomes ap ó s sort () ( alfab é tica ) : { nomes_alunos } " )
12
13 nomes_alunos . sort ( reverse = True ) # Ordena em ordem
alfab é tica decrescente
14 print ( f " Nomes ap ó s sort ( reverse = True ) : { nomes_alunos } " )

Código 6.30: Usando o método sort() para ordenar listas

• Função Embutida sorted(iteravel) (Cria uma Nova Lista Ordenada)


Diferentemente do método sort(),afunção sorted() recebe qualquer sequência
iterável como argumento e retorna uma nova lista contendo os itens ordenados.
A original não é modificada. Também aceita reverse=True.
1 pontuacoes_jogo = [1500 , 800 , 2100 , 1200]
2 print ( f " Pontua ç õ es originais : { pontuacoes_jogo } " )
3
4 p o n t u a c o e s _ o r d e n a d a s _ c r e s c e n t e = sorted ( pontuacoes_jogo )
136 Capítulo 6. Listas (Lists)

5 print ( f " Nova lista ordenada ( crescente ) :


{ pontuacoes_ordenadas_crescente }")
6 print ( f " Pontua ç õ es originais ( inalteradas ) :
{ pontuacoes_jogo } " )
7
8 p o n t u a c o e s _ o r d e n a d a s _ d e c r e s c e n t e = sorted ( pontuacoes_jogo ,
reverse = True )
9 print ( f " Nova lista ordenada ( decrescente ) :
{ pontuacoes_ordenadas_decrescente }")

Código 6.31: Usando a função sorted() para obter uma lista ordenada

Use minha_lista.sort() quando quiser modificar a lista original. Use sorted(minha_lista)


quando precisar de uma nova lista ordenada, preservando a original.

6.6.3 Invertendo a Ordem dos Elementos


• Método minha_lista.reverse() (Inversão “In-Place”)
Este método inverte a ordem atual dos elementos na lista. Ele modifica a lista
original e retorna None. Não é uma ordenação, apenas uma inversão da ordem
existente.
1 s eq uen ci a_ num er ic a = [1 , 2 , 3 , 4 , 5]
2 print ( f " Sequ ê ncia original : { se que nc ia _nu me ri ca } " )
3 s eq uen ci a_ num er ic a . reverse () # Inverte a lista diretamente
4 print ( f " Sequ ê ncia ap ó s reverse () : { s eq uen ci a_ num er ic a } " )
5
6 misturada = [ " a " , 1 , " b " , 2]
7 misturada . reverse ()
8 print ( f " Lista misturada invertida : { misturada } " )

Código 6.32: Usando o método reverse() para inverter uma lista

6.6.4 Contando Ocorrências de um Item


• Método minha_lista.count(item)
Retorna o número de vezes que o item especificado aparece dentro da lista.
1 votos_enquete = [ " sim " , " n ã o " , " sim " , " sim " , " talvez " ,
" n ã o " , " sim " ]
2
3 contagem_sim = votos_enquete . count ( " sim " )
4 print ( f " N ú mero de votos ’ sim ’: { contagem_sim } " )
5
6 contagem_talvez = votos_enquete . count ( " talvez " )
7 print ( f " N ú mero de votos ’ talvez ’: { contagem_talvez } " )
8
9 conta gem_inde ciso = votos_enquete . count ( " indeciso " )
10 print ( f " N ú mero de votos ’ indeciso ’: { contag em_indec iso } " )

Código 6.33: Usando o método count() para contar itens

6.6.5 Encontrando o Índice de um Elemento


• Método minha_lista.index(item, inicio_opcional, fim_opcional)
Retorna o índice da primeira ocorrência do item especificado.
6.6. Métodos Comuns de Listas: Ferramentas Poderosas para Manipulação 137

– Se o item não for encontrado, levanta um erro ValueError.


– inicio_opcional e fim_opcional restringem a busca a uma fatia.

1 materiais = [ " madeira " , " ferro " , " vidro " , " pl á stico " ,
" ferro " , " papel " ]
2
3 item_procurado = " ferro "
4 # É uma boa pr á tica verificar se o item existe antes de
chamar index ()
5 if item_procurado in materiais :
6 indice_ferro = materiais . index ( item_procurado )
7 print ( f " A primeira ocorr ê ncia de ’{ item_procurado } ’
est á no í ndice : { indice_ferro } " )

Código 6.34: Usando o método index() para encontrar um item

6.6.6 Copiando Listas


Quando você faz nova_lista = lista_original, ambas as variáveis apontam para o
mesmo objeto lista. Para criar uma cópia independente (cópia rasa), use:

• Método minha_lista.copy()

• Fatiamento Completo [:] (nova_lista = lista_original[:])

Uma cópia rasa (shallow copy) cria uma nova lista, mas se os elementos internos forem
objetos mutáveis (como outras listas), as referências a esses objetos internos são copiadas.
1 # Copiando listas simples
2 l i s t a _ o r i g i n a l _ s i m p l e s = [10 , 20 , 30]
3 copia_com_metodo = l i s t a _ o r i g i n a l _ s i m p l e s . copy ()
4 print ( f " Original Simples : { l i s t a _ o r i g i n a l _ s i m p l e s } , ID :
{ id ( l i s t a _ o r i g i n a l _ s i m p l e s ) } " )
5 print ( f " C ó pia com . copy () : { copia_com_metodo } , ID :
{ id ( copia_com_metodo ) } " )
6
7 copia_com_metodo . append (40)
8 print ( f " Original Simples ap ó s modificar c ó pia :
{ lista_original_simples }")
9 print ( f " C ó pia com . copy () modificada : { copia_com_metodo } " )
10
11 # Exemplo de c ó pia rasa com listas aninhadas
12 l i s t a _ a n i n h a d a _ o r i g i n a l = [1 , [100 , 200] , 3]
13 copia_rasa = l i s t a _ a n i n h a d a _ o r i g i n a l . copy ()
14 print ( f " \ nOriginal Aninhada : { l i s t a _ a n i n h a d a _ o r i g i n a l } " )
15 print ( f " C ó pia Rasa : { copia_rasa } " )
16
17 copia_rasa [0] = 99 # N ã o afeta o original
18 copia_rasa [1]. append (300) # Modifica a sublista INTERNA , que é
compartilhada !
19
20 print ( f " Original Aninhada ap ó s modifica ç õ es na c ó pia :
{ lista_aninhada_original }")
21 print ( f " C ó pia Rasa com sublista modificada : { copia_rasa } " )

Código 6.35: Copiando listas e o efeito em listas aninhadas (cópia rasa)


138 Capítulo 6. Listas (Lists)

Para cópias totalmente independentes de listas aninhadas (cópia profunda), existe


[Link]() do módulo copy, que será abordado se necessário em contextos mais
avançados.
Esses métodos comuns de listas são ferramentas essenciais que você usará constante-
mente, permitindo que você ordene, conte, encontre e copie seus dados de forma eficiente.

6.7 Compreensão de Listas (List Comprehensions)


Até agora, quando quisemos criar uma nova lista baseada nos elementos de outra
sequência ou através de alguma lógica de repetição, tipicamente usaríamos um laço for
e o método append() para adicionar itens um a um a uma lista inicialmente vazia. Por
exemplo, para criar uma lista com os quadrados dos números de 0 a 4:
1 # Forma tradicional com la ç o for
2 q u a d r a d o s _ t r a d i c i o n a l = [] # Cria uma lista vazia
3 for x in range (5) : # Itera de 0 a 4
4 qu a d r a d o s _ t r a d i c i o n a l . append ( x **2) # Adiciona o quadrado de x à
lista
5 print ( f " Quadrados ( tradicional ) : { q u a d r a d o s _ t r a d i c i o n a l } " )
6 # Sa í da : Quadrados ( tradicional ) : [0 , 1 , 4 , 9 , 16]

Código 6.36: Criando lista de quadrados com laço for tradicional


Isso funciona perfeitamente bem e é claro. No entanto, Python oferece uma sintaxe mais
concisa e, para muitos, mais legível para este tipo de operação: a compreensão de listas
(ou list comprehension). Elas permitem criar novas listas de forma declarativa, muitas
vezes em uma única linha de código.

A Sintaxe da Compreensão de Listas


Uma compreensão de listas é escrita dentro de colchetes [], assim como a definição
literal de uma lista, mas segue uma estrutura específica.
A forma mais básica é:
nova_lista = [expressao for item in iteravel]
E uma forma mais completa, que inclui uma condição para filtrar itens, é:
nova_lista = [expressao for item in iteravel if condicao]
Vamos destrinchar cada parte:
1. expressao: É a operação ou o valor que será calculado para cada item que satisfizer
a condicao (se houver uma). O resultado desta expressao é o que será adicionado
à nova_lista.
2. for item in iteravel: Esta parte é muito similar a um laço for normal. iteravel
é qualquer objeto Python que pode ser percorrido (como uma lista, uma string, uma
tupla, um objeto range(), etc.), e item é a variável que assume o valor de cada
elemento do iteravel a cada passo da iteração.
3. if condicao (Opcional): Esta é uma cláusula de filtro. Para cada item do
iteravel, a condicao (uma expressão booleana) é verificada. Somente se a condicao
for True é que a expressao será avaliada e seu resultado incluído na nova_lista.
Se a cláusula if for omitida, todos os itens do iteravel serão processados pela
expressao.
6.7. Compreensão de Listas (List Comprehensions) 139

Comparando com Laços for Tradicionais (Novamente)


Vamos refazer o exemplo dos quadrados usando uma compreensão de listas:
1 # Usando compreens ã o de listas para os quadrados
2 q u a d r a d o s _ c o m p r e e n s a o = [ x **2 for x in range (5) ] # Para cada x em
range (5) , calcule x **2
3 print ( f " Quadrados ( compreens ã o ) : { q u a d r a d o s _ c o m p r e e n s a o } " )
4 # Sa í da : Quadrados ( compreens ã o ) : [0 , 1 , 4 , 9 , 16]

Código 6.37: Criando lista de quadrados com list comprehension


Veja como ficou mais enxuto!
Agora, um exemplo com filtro: criar uma lista apenas com os números pares de 0 a 9.

• Com laço for tradicional:


1 pares_loop = []
2 for numero in numeros_ate_9 :
3 if numero % 2 == 0: # Condi ç ã o para ser par
4 pares_loop . append ( numero )
5 print ( f " Pares ( loop tradicional ) : { pares_loop } " )

Código 6.38: Filtrando pares com laço for tradicional

• Com compreensão de listas:


1 pares _compree nsao = [ numero for numero in range (10) if
numero % 2 == 0]
2 # Para cada numero em range (10) , SE numero for par , inclua
numero na lista
3 print ( f " Pares ( compreens ã o ) : { pa res_comp reensao } " )

Código 6.39: Filtrando pares com list comprehension

Mais uma vez, a versão com compreensão de listas é mais compacta.

Vantagens das Compreensões de Listas


1. Concisão: Elas geralmente resultam em um código mais curto.

2. Legibilidade: Para muitos casos, a sintaxe da list comprehension pode ser mais
fácil de ler e entender de relance do que um laço for com append(), pois a lógica
de transformação e filtragem está toda em uma única linha expressiva.

3. Eficiência (Potencial): Em muitos cenários, as list comprehensions podem ser um


pouco mais eficientes em termos de velocidade de execução do que o uso de um laço
for explícito com chamadas repetidas ao método append().

Mais Exemplos Práticos


• Exemplo 1: Converter uma lista de palavras para maiúsculas
1 p al avr as _o rig in ai s = [ " python " , " é " , " muito " , " legal " ]
2 p a l a v r a s _ e m _ m a i u s c u l a s = [ palavra . upper () for palavra in
pa la vr as_ or ig ina is ]
3 print ( f " Palavras em mai ú sculas : { p a l a v r a s _ e m _ m a i u s c u l a s } " )
140 Capítulo 6. Listas (Lists)

4 # Sa í da : [ ’ PYTHON ’, ’É ’, ’ MUITO ’, ’ LEGAL ’]

Código 6.40: Convertendo strings para maiúsculas com list comprehension

• Exemplo 2: Obter comprimentos de palavras, mas só das com mais de 3


letras
1 frase_exemplo = " Esta é uma frase com algumas palavras de
tamanhos variados "
2 palav ras_da_f rase = frase_exemplo . split ()
3
4 c o m p r i m e n t o s _ f i l t r a d o s = [ len ( palavra ) for palavra in
palav ras_da_f rase if len ( palavra ) > 3]
5 print ( f " Comprimentos das palavras com mais de 3 letras :
{ comprimentos_filtrados }")

Código 6.41: Filtrando e transformando com list comprehension

• Exemplo 3: Criar uma lista de tuplas (par de número e seu dobro)


(Uma tupla, que veremos no Capítulo 7, é como uma lista imutável. Aqui, (x, x*2)
cria uma tupla.)
1 nu m er o s_ p a ra _ do b r ar = range (1 , 6) # 1 , 2 , 3 , 4 , 5
2 p ar es_ nu me ro_ do br o = [( x , x * 2) for x in
n um e r os _ pa r a_ d o br a r ]
3 print ( f " Pares ( n ú mero , dobro ) : { p ar es _nu me ro _do br o } " )
4 # Sa í da : [(1 , 2) , (2 , 4) , (3 , 6) , (4 , 8) , (5 , 10) ]

Código 6.42: List comprehension criando lista de tuplas

Quando Usar (e Quando Talvez Evitar)


As compreensões de listas são fantásticas para:

• Criar uma nova lista aplicando uma expressão a cada item de uma sequência existente.

• Filtrar itens de uma sequência e criar uma nova lista com os itens que passam no
filtro.

• Combinar transformação e filtragem.

No entanto, se a lógica para a expressao ou para a condicao dentro da compreensão de


lista se tornar muito complexa e longa, a compreensão pode acabar ficando difícil de
ler. Nesses casos, um laço for tradicional pode ser mais claro. Lembre-se, o objetivo é
escrever código que seja não apenas funcional, mas também fácil de entender.
As compreensões de listas são uma ferramenta expressiva e poderosa, muito caracte-
rística do estilo de programação em Python. Dominá-las para os casos apropriados pode
deixar seu código mais elegante, conciso e, muitas vezes, mais eficiente.

6.8 Listas Aninhadas (Listas dentro de Listas)


Até agora, os elementos das nossas listas foram, em sua maioria, tipos de dados simples
como números ou strings. No entanto, uma das grandes flexibilidades das listas em Python
6.8. Listas Aninhadas (Listas dentro de Listas) 141

é que um elemento de uma lista pode ser, ele mesmo, outra lista! Quando isso acontece,
chamamos essa estrutura de lista aninhada (ou nested list, em inglês).
As listas aninhadas são a maneira mais comum em Python puro (sem usar bibliotecas
especializadas) de representar estruturas de dados bidimensionais (ou até de dimensões
maiores), como:

• Matrizes matemáticas

• Tabelas de dados

• Grades (como um mapa ou um tabuleiro de jogo)

• Qualquer coleção onde os itens são, eles mesmos, agrupamentos de outros itens.

Criando Listas Aninhadas


A criação de uma lista aninhada é uma extensão natural da sintaxe de lista que já
conhecemos. Cada sublista interna é definida com seus próprios colchetes [] e elementos,
e essas sublistas são, então, os elementos da lista externa (principal).
1 # Exemplo 1: Uma matriz num é rica 3 x3 (3 linhas , 3 colunas )
2 matriz_numerica = [
3 [1 , 2 , 3] , # Linha 0 ( primeira sublista )
4 [4 , 5 , 6] , # Linha 1 ( segunda sublista )
5 [7 , 8 , 9] # Linha 2 ( terceira sublista )
6 ]
7 print ( f " Matriz num é rica exemplo : { matriz_numerica } " )
8

9 # Exemplo 2: Lista de informa ç õ es de alunos


10 lista_de_alunos = [
11 [ " Ana Silva " , 20 , 8.5] , # Aluno 1: [ nome , idade , media ]
12 [ " Bruno Costa " , 22 , 7.0] , # Aluno 2
13 [ " Carla Dias " , 21 , 9.2] # Aluno 3
14 ]
15 print ( f " Lista de alunos : { lista_de_alunos } " )
16
17 # Exemplo 3: Lista com um item sendo outra lista
18 r e g i s t r o _ d i s c i p l i n a s _ a l u n o = [ " Programa ç ã o em Python " , [ " C á lculo
I " , " F í sica Experimental " ]]
19 print ( f " Registro de disciplinas : { r e g i s t r o _ d i s c i p l i n a s _ a l u n o } " )

Código 6.43: Exemplos de criação de listas aninhadas


A formatação com quebras de linha e indentação para cada sublista, como no exemplo da
matriz_numerica, é comum para melhorar a legibilidade.

Acessando Elementos em Listas Aninhadas


Para acessar um elemento dentro de uma lista aninhada, você precisará usar múl-
tiplos índices. O primeiro índice acessa a sublista desejada dentro da lista prin-
cipal, e o segundo índice acessa o elemento dentro daquela sublista. A sintaxe é:
lista_aninhada[indice_da_sublista][indice_do_elemento_na_sublista]
1 matriz_numerica = [[1 , 2 , 3] , [4 , 5 , 6] , [7 , 8 , 9]]
2 lista_de_alunos = [[ " Ana Silva " , 20 , 8.5] , [ " Bruno Costa " , 22 , 7.0]]
3
142 Capítulo 6. Listas (Lists)

4 # Acessando a primeira sublista ( linha 0)


5 primeira_linha = matriz_numerica [0]
6 print ( f " Primeira linha da matriz : { primeira_linha } " ) # Sa í da : [1 ,
2 , 3]
7
8 # Acessando o segundo elemento ( í ndice 1) da primeira linha ( í ndice
0)
9 elemento_0_1 = matriz_numerica [0][1]
10 print ( f " Elemento na linha 0 , coluna 1: { elemento_0_1 } " ) # Sa í da : 2
11
12 # Acessando o primeiro elemento ( í ndice 0) da ú ltima linha ( í ndice
-1)
13 e l e m e n t o _ u l t i m o _ p r i m e i r o = matriz_numerica [ -1][0]
14 print ( f " Elemento na ú ltima linha , primeira coluna :
{ e l e m e n t o _ u l t i m o _ p r i m e i r o } " ) # Sa í da : 7
15
16 # Acessando o nome do segundo aluno
17 no me _se gu nd o_a lu no = lista_de_alunos [1][0]
18 print ( f " Nome do segundo aluno : { no me _s egu nd o_ alu no } " ) # Sa í da :
Bruno Costa

Código 6.44: Acessando elementos em listas aninhadas

Iterando sobre Listas Aninhadas


A forma mais comum de percorrer todos os elementos de uma lista aninhada é usando
laços for aninhados. O laço externo itera sobre as sublistas (as “linhas”), e o laço interno
itera sobre os elementos de cada sublista (as “colunas”).

• Exemplo 1: Imprimindo todos os elementos de uma matriz


1 m at riz _p ar a_i te ra r = [
2 [10 , 20 , 30] ,
3 [40 , 50 , 60] ,
4 [70 , 80 , 90]
5 ]
6 print ( " Elementos da matriz , um por linha de sublista : " )
7 for sublista_linha in ma tr iz _pa ra _i ter ar :
8 print ( f " Processando linha : { sublista_linha } " )
9 for el e me n t o_ i nd i v id u al in sublista_linha :
10 print ( f " Elemento : { e le m e nt o _i n d iv i du a l } " )
11 print ( " Fim da itera ç ã o na matriz . " )

Código 6.45: Iterando e imprimindo elementos de uma matriz

• Exemplo 2: Imprimindo uma matriz formatada como grade


1 matriz_grade = [
2 [ ’X ’ , ’O ’ , ’X ’] ,
3 [ ’O ’ , ’X ’ , ’O ’] ,
4 [ ’O ’ , ’O ’ , ’X ’]
5 ]
6 print ( " \ nTabuleiro do Jogo : " )
7 for li nh a_ do_ ta bu lei ro in matriz_grade :
8 for celula in li nha _d o_ tab ul ei ro :
9 print ( celula , end = " | " )
6.8. Listas Aninhadas (Listas dentro de Listas) 143

10 print ( " \ b \ b " ) # Remove os dois ú ltimos caracteres ("


|") e o espa ç o

Código 6.46: Imprimindo matriz como grade

• Exemplo 3: Calculando a soma de todos os elementos


1 matri z_de_num eros = [
2 [1 , 2 , 3] ,
3 [4 , 5] , # Sublistas podem ter tamanhos diferentes
4 [6 , 7 , 8 , 9]
5 ]
6 s o m a _ g er a l _ e l e m e n t o s = 0
7 for linha_numerica in matriz_d e_numero s :
8 for valor_na_linha in linha_numerica :
9 s o ma _ g e r a l _ e l e m e n t o s += valor_na_linha
10

11 print ( f " \ nA soma de todos os elementos é :


{ soma_geral_elementos }")

Código 6.47: Somando elementos de uma matriz numérica

Modificando Listas Aninhadas


Como as listas são mutáveis, você pode modificar os elementos internos usando múltiplos
índices: lista_aninhada[idx_sublista][idx_elemento] = novo_valor
1 # Exemplo : Modificando um tabuleiro de Jogo da Velha
2 tabuleiro = [
3 [ " _ " , " _ " , " _ " ] , # Linha 0
4 [ " _ " , " _ " , " _ " ] , # Linha 1
5 ["_", "_", "_"] # Linha 2
6 ]
7
8

9 print ( " --- Tabuleiro Atual ---" )


10 for linha_item in tab_atual :
11 print ( linha_item [0] , linha_item [1] , linha_item [2])
12 print ( " - - - - - - - - - - - - - - - - - - - - - - - " )
13

14
15
16 # Jogador ’X ’ na posi ç ã o central ( linha 1 , coluna 1 -> í ndices 1 ,1)
17 tabuleiro [1][1] = ’X ’
18 print ( " \ nAp ó s jogada de ’X ’: " )
19 for linha_item in tab_atual :
20 print ( linha_item [0] , linha_item [1] , linha_item [2])
21 print ( " - - - - - - - - - - - - - - - - - - - - - - - " )
22
23 # Jogador ’O ’ no canto superior esquerdo ( í ndices 0 ,0)
24 tabuleiro [0][0] = ’O ’
25 print ( " \ nAp ó s jogada de ’O ’: " )
26 for linha_item in tab_atual :
27 print ( linha_item [0] , linha_item [1] , linha_item [2])
28 print ( " - - - - - - - - - - - - - - - - - - - - - - - " )

Código 6.48: Modificando um tabuleiro de Jogo da Velha


144 Capítulo 6. Listas (Lists)

Considerações sobre Listas Aninhadas


• Tamanhos Diferentes: As sublistas não precisam ter o mesmo tamanho.
• Alternativas para Matrizes Numéricas: Para matemática avançada com ma-
trizes, bibliotecas como NumPy (Parte VI) são mais eficientes. No entanto, para
organização de dados bidimensionais simples, listas aninhadas são adequadas.

As listas aninhadas expandem o poder das listas, permitindo-nos modelar estruturas de


dados mais complexas e multidimensionais de forma intuitiva.

6.9 Quando Usar Listas (e Algumas Considerações)


Ao longo deste capítulo, mergulhamos fundo no universo das listas em Python. Apren-
demos como criá-las, acessar seus elementos individualmente e através de fatias (slicing),
modificá-las (adicionando, removendo e alterando itens), utilizamos diversos métodos
úteis para ordenação e busca, exploramosaelegância das compreensões de listas (list
comprehensions) e vimos como criar estruturas mais complexas com listas aninhadas.
Com todo esse conhecimento, surge uma pergunta natural: “Quando, exatamente,
devo usar uma lista em meus programas?” Esta seção visa responder a isso, resumindo
as características chave das listas e destacando seus casos de uso ideais, além de fazer
algumas breves considerações sobre performance e alternativas.

Características Chave das Listas (Resumo Rápido)


Para decidir quando usar uma lista, é bom ter em mente suas propriedades fundamen-
tais:
• Ordenadas: A ordem em que você insere os itens é preservada, e você pode confiar
nessa ordem. Cada item tem uma posição (índice) bem definida.
• Mutáveis: Este é um grande diferencial. Você pode alterar o conteúdo de uma lista
após sua criação – adicionar, remover ou modificar elementos.
• Heterogêneas (Permitem Tipos Mistos): Uma única lista pode conter itens de
diferentes tipos de dados (ex: números, strings, booleanos e até outras listas).
• Dinâmicas: Listas podem crescer ou encolher de tamanho duranteaexecução do
programa.
• Permitem Duplicatas: Uma lista pode conter o mesmo valor múltiplas vezes.

Quando as Listas Brilham (Casos de Uso Ideais)


Considerando suas características, as listas são particularmente adequadas para os
seguintes cenários:
1. Quando a Ordem dos Itens Importa: Se a sequência dos seus dados é significativa
e precisa ser mantida.
• Exemplos: Os passos em uma receita culinária, uma sequência de instruções, o
histórico de ações de um usuário.
6.9. Quando Usar Listas (e Algumas Considerações) 145

2. Quando a Coleção de Dados Precisa ser Modificada Frequentemente: Se


você antecipa que precisará adicionar, remover ou alterar itens na sua coleção.

• Exemplos: Uma lista de tarefas pendentes, um carrinho de compras.

3. Quando Duplicatas São Permitidas e Relevantes: Se sua coleção pode legiti-


mamente conter o mesmo valor várias vezes.

• Exemplos: Uma lista de notas de alunos, uma lista de palavras em um texto.

4. Para Agrupamento Geral e Flexível de Itens: Quando você precisa de uma


coleção simples para agrupar itens, e a flexibilidade de tipos mistos ou de modificação
é vantajosa.

Algumas Considerações Importantes (e Quando Outras Estruturas


Podem Ser Melhores)
Embora as listas sejam incrivelmente versáteis, há algumas situações onde outras
estruturas de dados (que veremos nos próximos capítulos) podem ser mais apropriadas ou
eficientes:

• Performance em Buscas de Pertinência (in): Para listas muito grandes,


verificar se um item está presente (item in minha_lista) pode ser relativamente
lento.

– Alternativa: Para buscas de pertinência muito frequentes em grandes coleções,


conjuntos (set) ou dicionários (dict) são geralmente mais eficientes.

• Necessidade de Imutabilidade: Se você tem uma coleção ordenada de itens


que, uma vez criada, não deve jamais ser alterada, então uma tupla (tuple)
(Capítulo 7) é uma escolha mais segura.

• Grandes Volumes de Dados Numéricos para Computação Científica: Para


cálculos matemáticos intensivos com arrays numéricos, a biblioteca NumPy (Parte
VI) é muito mais performática.

• Legibilidade com Aninhamento Profundo: Listas aninhadas muito profundas


podem tornar o código complexo. Considere refatorar ou usar classes.

Em resumo, a escolha da estrutura de dados depende dos requisitos do seu problema.

Concluindo o Capítulo sobre Listas


Neste capítulo, demos um mergulho profundo em uma das estruturas de dados mais
fundamentais e versáteis do Python: as listas. Vimos como criá-las, como acessar e fatiar
seus elementos usando índices, e como sua natureza mutável nos permite adicionar, remover
e alterar itens dinamicamente. Exploramos também uma variedade de métodos úteis que
as listas oferecem para ordenação, contagem, busca e cópia, além da elegante sintaxe das
compreensões de listas para criar novas listas de forma concisa. Finalmente, entendemos
como as listas aninhadas podem ser usadas para representar dados multidimensionais.
146 Capítulo 6. Listas (Lists)

As listas são verdadeiros “canivetes suíços” na caixa de ferramentas de um programador


Python. Elas aparecem em quase todo tipo de programa, servindo como base para
armazenar e organizar coleções de informações de maneira ordenada e flexível.
Com o conhecimento que você adquiriu aqui, você está muito mais preparado para
manipular conjuntos de dados, o que é uma habilidade essencial na programação. No
próximo capítulo, conheceremos as tuplas, uma estrutura de dados irmã das listas, mas
com uma diferença crucial: elas são imutáveis. Vamos descobrir por que isso é importante
e quando você pode preferir usar uma tupla em vez de uma lista!
Capítulo 7

Tuplas (Tuples)

No capítulo anterior, exploramos as listas em profundidade, aprendendo como elas são


coleções ordenadas e, crucialmente, mutáveis de itens. Elas são incrivelmente flexíveis
para armazenar dados que podem precisar ser alterados, adicionados ou removidos ao
longo do tempo.
Agora, vamos conhecer outra estrutura de dados sequencial muito importante em
Python: a tupla. À primeira vista, as tuplas podem parecer muito com as listas,
pois também são coleções ordenadas de itens. No entanto, elas possuem uma diferença
fundamental que define seus casos de uso: as tuplas são imutáveis.

7.1 Introdução às Tuplas: Sequências Ordenadas e Imu-


táveis
Uma tupla (tuple), assim como uma lista, é uma sequência que pode armazenar uma
coleção de itens. Esses itens são mantidos em uma ordem específica e podem ser de tipos
diferentes.
A grande e definidora característica de uma tupla é sua imutabilidade. Isso significa
que, uma vez que uma tupla é criada, ela não pode ser alterada. Você não pode
adicionar novos itens a uma tupla existente, nem remover ou modificar os itens que já
estão nela. Se você precisa de uma versão “modificada” de uma tupla, você na verdade
cria uma nova tupla.

Por que usar Tuplas se já temos Listas?


Essa pergunta é muito natural. Se as listas são tão flexíveis e mutáveis, por que
precisaríamos de uma estrutura similar que é mais restritiva? Existem várias boas razões
para o uso de tuplas:

1. Segurança e Integridade dos Dados: Como as tuplas são imutáveis, elas são
ideais para representar coleções de itens que não devem ser alterados após sua criação.
Isso protege seus dados contra modificações acidentais. Pense em um conjunto de
coordenadas (x, y) que definem um ponto fixo, ou as cores RGB de uma cor específica
– são valores que, uma vez definidos, não devem mudar.

2. Uso como Chaves em Dicionários: Dicionários em Python (que veremos no


Capítulo 8) exigem que suas chaves sejam de tipos imutáveis. Como as tuplas são

147
148 Capítulo 7. Tuplas (Tuples)

imutáveis (desde que seus elementos também sejam imutáveis), elas podem ser usadas
como chaves de dicionários, enquanto as listas não podem.

3. Performance (Pequenas Vantagens): Em alguns casos, devido à sua natureza


imutável e estrutura mais simples, o processamento de tuplas pode ser ligeiramente
mais rápido do que o de listas. No entanto, essa diferença é geralmente pequena e
não costuma ser o principal motivo para escolher tuplas em vez de listas, a menos
que você esteja lidando com otimizações de performance em grande escala.

4. Semântica e Clareza: Usar uma tupla em vez de uma lista pode, às vezes,
comunicar melhor a intenção do seu código. Se você usa uma tupla, está sinalizando
para outros programadores (ou para você mesmo no futuro) que aquela coleção de
itens é fixa e não se destina a ser modificada. Ela representa um “registro” ou um
“agrupamento” estável de valores.

Comparação Inicial com Listas

Característica Lista (list) Tupla (tuple)


Ordenada Sim Sim
Mutabilidade Mutável (pode ser alterada) Imutável (não pode ser alte-
rada)
Sintaxe [item1, item2, ...] (item1, item2, ...) (parên-
teses são frequentemente opcio-
nais)
Uso Típico Coleções que mudam, ordem im- Coleções fixas, registros de da-
porta dos

Tabela 7.1: Comparativo inicial entre Listas e Tuplas.

Neste capítulo, vamos aprender como criar tuplas, acessar seus elementos (o que é
muito similar às listas), entender em profundidade o que significa sua imutabilidade, ver
quais operações são permitidas e, o mais importante, quando e por que você escolheria
usar uma tupla em seus programas Python.

7.2 Criando Tuplas: Definindo Nossas Coleções Imutá-


veis
Agora que entendemos o que são as tuplas e sua principal característica – a imutabilidade
– vamos aprender como efetivamente criá-las em nossos programas Python. A sintaxe
para criar tuplas é simples e tem algumas semelhanças comacriação de listas, mas com
particularidades importantes, especialmente ao criar tuplas com um único elemento.

A Sintaxe Principal: Usando Parênteses () (Frequentemente Opci-


onais)
A forma mais comum de definir uma tupla é listar os itens desejados, separados por
vírgulas, e envolver toda essa sequência com parênteses ().
7.2. Criando Tuplas: Definindo Nossas Coleções Imutáveis 149

Sintaxe geral: nome_da_tupla = (item1, item2, item3, ...)


Curiosamente, em muitos contextos, os parênteses ao redor dos elementos de uma
tupla são opcionais, e o Python consegue inferir que você está criando uma tupla apenas
pela presença das vírgulas separando os itens. No entanto, é uma prática fortemente
recomendada usar os parênteses para maior clareza e para evitar ambiguidades.

• Criando uma Tupla Vazia:


Para criar uma tupla que não contém nenhum item, você pode usar parênteses vazios
ouafunção construtora tuple().
1 # Criando uma tupla vazia
2 d a d o s _ i n i c i a i s _ v a z i o s = ()
3 print ( f " Tupla vazia : { d a d o s _ i n i c i a i s _ v a z i o s } " )
4 print ( f " Tipo da vari á vel : { type ( d a d o s _ i n i c i a i s _ v a z i o s ) } " )
5
6 outra _tupla_v azia = tuple ()
7 print ( f " Outra tupla vazia ( com construtor ) :
{ outra_tu pla_vazi a } " )

Código 7.1: Criando uma tupla vazia

• Criando uma Tupla com um ÚNICO Elemento (Ponto Crucial!):


Para criar uma tupla que contém apenas um único elemento, você obrigatoriamente
precisa incluir uma vírgula (,) após esse elemento, mesmo que ele esteja
dentro de parênteses. Sem a vírgula, (item1) é apenas item1 com parênteses de
agrupamento.
1 # Forma CORRETA de criar uma tupla com um ú nico elemento
2 ponto_no_eixo_x = (25 ,) # A v í rgula final é essencial !
3 print ( f " Tupla com um elemento : { ponto_no_eixo_x } , Tipo :
{ type ( ponto_no_eixo_x ) } " )
4
5 # Outra forma correta , sem os par ê nteses ( mas com a
v í rgula )
6 nome_unico = " Singleton " ,
7 print ( f " Tupla com nome ú nico : { nome_unico } , Tipo :
{ type ( nome_unico ) } " )
8
9 # Forma ERRADA ( isto N Ã O cria uma tupla de um elemento )
10 valor_simples = (100) # Isto é apenas o inteiro 100
11 print ( f " Isto n ã o é uma tupla : { valor_simples } , Tipo :
{ type ( valor_simples ) } " )

Código 7.2: Criando tupla com um único elemento (atenção à vírgula)

• Criando Tuplas com Múltiplos Elementos:


Com múltiplos elementos, a sintaxe é mais direta.
1 # Tupla representando coordenadas ( latitude , longitude )
2 coordenadas_gps = ( -23.5505 , -46.6333) # S ã o Paulo
3 print ( f " Coordenadas : { coordenadas_gps } " )
4
5 # Tupla com informa ç õ es de um produto ( ID , Nome , Pre ç o )
6 produto_info = (101 , " Teclado Gamer XYZ " , 299.90)
7 print ( f " Informa ç õ es do produto : { produto_info } " )
8
150 Capítulo 7. Tuplas (Tuples)

9 # Tupla pode conter tipos de dados mistos


10 r e g i s tr o _ d a t a _ e v e n t o = ( " Confer ê ncia Anual " , 2025 ,
" Novembro " , True )
11 print ( f " Registro do evento : { r e g i s t r o _ d a t a _ ev e n t o } " )

Código 7.3: Criando tuplas com múltiplos elementos

Usando a Função Construtora tuple()


Outra maneira de criar tuplas é usandoafunção embutida tuple(). Ela é particu-
larmente útil para converter outros tipos de sequências (ou objetos iteráveis) em uma
tupla.

• Convertendo uma String em uma Tupla de Caracteres:


1 sigla_curso = " ENGCOMP "
2 t u p l a _ d e _ l e t r a s _ s i g l a = tuple ( sigla_curso )
3 print ( f " A string ’{ sigla_curso } ’ convertida em tupla :
{ tupla_de_letras_sigla }")
4 # Sa í da : ( ’ E ’, ’N ’, ’G ’, ’C ’, ’O ’, ’M ’, ’P ’)

Código 7.4: Convertendo string para tupla de caracteres

• Convertendo uma Lista em Tupla:


1 l i s t a _ d e _ c o m p r a s _ m u t a v e l = [ " ma ç ã " , " banana " , " leite " ]
2 t u p l a _ d e _ c o m p r a s _ i m u t a v e l = tuple ( l i s t a _ d e _ c o m p r a s _ m u t a v e l )
3 print ( f " Lista original : { l i s t a _ d e _ c o m p r a s _ m u t a v e l } " )
4 print ( f " Tupla criada a partir da lista :
{ tupla_de_compras_imutavel }")
5 # Sa í da : ( ’ ma ç ã ’, ’ banana ’, ’ leite ’)

Código 7.5: Convertendo lista para tupla

• Convertendo um Objeto range() em Tupla:


1 n u m e r o s _ s e q u e n c i a i s _ a t e _ 3 = tuple ( range (4) ) # range (4)
gera 0 , 1 , 2 , 3
2 print ( f " Tupla criada com range (4) :
{ numeros_sequenciais_ate_3 }")
3
4 nu m er o s_ p a re s _t u p la = tuple ( range (2 , 11 , 2) ) # De 2 a 10 ,
pulando de 2 em 2
5 print ( f " Tupla de pares at é 10: { nu m er o s _p a re s _t u p la } " )

Código 7.6: Convertendo range() para tupla

Tuplas e Quebras de Linha no Código Fonte


Assim como nas listas, se você estiver definindo uma tupla com muitos elementos, pode
quebrar a definição em múltiplas linhas dentro do seu código para melhorar a legibilidade,
desde que as quebras ocorram dentro dos parênteses () da tupla.
7.3. Acessando Elementos da Tupla: Índices e Fatiamento 151

1 # Tupla representando um registro de dados de um sensor


2 leitura_sensor_detalhada = (
3 " Sensor Temperatura Central " , # Nome do sensor
4 " Sala de Servidores A " , # Localiza ç ã o
5 23.5 , # Valor da temperatura em Celsius
6 "°C", # Unidade
7 " 2025 -05 -23 T10 :35:17 Z " # Timestamp da leitura ( formato ISO
8601)
8 )
9
10 print ( f " Leitura do sensor : { l e i t u r a _ s e n s o r _ d e t a l h a d a } " )
11 print ( f " Temperatura registrada :
{ l e i t u r a _ s e n s o r _ d e t a l h a d a [2]}{ l e i t u r a _ s e n s o r _ d e t a l h a d a [3]} " )

Código 7.7: Definindo uma tupla longa em múltiplas linhas


A indentação dos itens nas linhas subsequentes ajuda na organização visual.
Com estas diferentes formas de criar tuplas, você está pronto para começar a usá-las
para armazenar coleções de dados que não devem ser alteradas. A próxima etapa é
aprender como acessar os elementos guardados dentro dessas tuplas.

7.3 Acessando Elementos da Tupla: Índices e Fatia-


mento
Uma vez que uma tupla é criada e seus elementos estão definidos, frequentemente
precisaremos acessar esses elementos individualmente ou obter subconjuntos deles (sub-
tuplas). A boa notícia é que, apesar da imutabilidade das tuplas (que impede a alteração
de seus elementos apósacriação), a forma de ler ou acessar seus elementos é exatamente
a mesma que usamos para as listas.
Isso significa que os conceitos de indexação (baseada em zero, positiva e negativa) e
fatiamento (slicing) que aprendemos no Capítulo 6 para listas se aplicam diretamente às
tuplas.

7.3.1 Acessando Elementos Individuais com Índices


Para acessar um item específico de uma tupla, utilizamos seu índice numérico. Lembre-
se:

• A indexação em Python é baseada em zero.

• Índices positivos contam a partir do início.

• Índices negativos contam a partir do final (-1 é o último elemento).

A sintaxe para acessar um elemento é: nome_da_tupla[indice]


1 # Tupla de exemplo com os meses do primeiro trimestre
2 me se s_d o_ tr ime st re = ( " Janeiro " , " Fevereiro " , " Mar ç o " )
3 # Í ndices Positivos : 0 1 2
4 # Í ndices Negativos : -3 -2 -1
5
6 # Acessando o primeiro m ê s ( í ndice 0)
7 p r im eir o _m e s _d o _a n o = mes es _d o_ tri me st re [0]
152 Capítulo 7. Tuplas (Tuples)

8 print ( f " O primeiro m ê s do trimestre é : { pr i m ei r o_ m es _ d o_ a no } " )


9
10 # Acessando o ú ltimo m ê s do trimestre usando í ndice negativo ( -1)
11 u l t i m o _ m e s _ d o _ t r i m e s t r e = m es es _do _t ri mes tr e [ -1]
12 print ( f " O ú ltimo m ê s do trimestre é : { u l t i m o _ m e s _ d o _ t r i m e s t r e } " )
13

14 # Acessando o segundo m ê s ( í ndice 1)


15 s e g u n d o _ m e s _ d o _ t r i m e s t r e = m es es _do _t ri mes tr e [1]
16 print ( f " O segundo m ê s do trimestre é : { s e g u n d o _ m e s _ d o _ t r i m e s t r e } " )
17
18 # Tentativa de acessar um í ndice inv á lido resultaria em IndexError
19 # print ( m es es_ do _t rim es tr e [3]) # Geraria um erro !

Código 7.8: Acessando elementos de uma tupla com índices


Assim como nas listas, se você tentar usar um índice que está fora do intervalo válido para
a tupla, o Python levantará um erro do tipo IndexError.

7.3.2 Obtendo Sub-Tuplas com Fatiamento (Slicing)


O fatiamento (slicing) em tuplas funciona da mesma maneira poderosa e flexível que
nas listas, permitindo que você extraia uma porção da tupla, resultando em uma nova
tupla.
A sintaxe geral é: nova_tupla = minha_tupla[inicio:fim:passo]
• inicio: O índice do primeiro elemento a ser incluído (padrão é 0).

• fim: O índice do primeiro elemento a não ser incluído (padrão é o final da tupla).

• passo: O intervalo entre os elementos (padrão é 1).

1 # Tupla de exemplo com pontos cardeais


2 p o n t o s _ c a r d e a i s _ t u p l a = ( ’ Norte ’ , ’ Leste ’ , ’ Sul ’ , ’ Oeste ’ ,
’ Nordeste ’ , ’ Sudeste ’ , ’ Sudoeste ’ , ’ Noroeste ’)
3 # Í ndices : 0 1 2 3
4 5 6 7
4
5 # Pegando os dois primeiros pontos ( í ndices 0 e 1)
6 p r i m e i r o s _ d o i s _ p o n t o s = p o n t o s _ c a r d e a i s _ t u p l a [0:2] # Ou
p o n t o s _ c a r d e a i s _ t u p l a [:2]
7 print ( f " Primeiros dois pontos : { p r i m e i r o s _ d o i s _ p o n t o s } " )
8 print ( f " O tipo da fatia é : { type ( p r i m e i r o s _ d o i s _ p o n t o s ) } " )
9
10 # Pegando do segundo elemento at é o final
11 do_sul_em_diante = p o n t o s _ c a r d e a i s _ t u p l a [2:] # Alterei o exemplo
para pegar do Sul em diante
12 print ( f " Do Sul em diante : { do_sul_em_diante } " )
13

14 # Pegando todos os elementos com passo 2 ( alternados )


15 p ontos_al ternados = p o n t o s _ c a r d e a i s _ t u p l a [::2]
16 print ( f " Pontos alternados ( passo 2) : { po ntos_alt ernados } " )
17
18 # Invertendo uma tupla com fatiamento ( passo -1)
19 tupla_invertida = p o n t o s _ c a r d e a i s _ t u p l a [:: -1]
20 print ( f " Pontos cardeais invertidos : { tupla_invertida } " )
21
22 # Usando í ndices negativos no fatiamento para pegar os tr ê s ú ltimos
7.4. Imutabilidade em Detalhe: O que Significa Não Poder Mudar? 153

23 t re s _ul t im o s _p o nt o s = p o n t o s _ c a r d e a i s _ t u p l a [ -3:]
24 print ( f " Os tr ê s ú ltimos pontos : { t r es _ ul t i mo s _p o nt o s } " )

Código 7.9: Fatiamento de tuplas (slicing)

Fatias de Tuplas são Novas Tuplas


É crucial lembrar que, assim como no fatiamento de listas, quando você fatia uma tupla,
o resultado é uma nova tupla. A tupla original permanece completamente inalterada (o
que já era esperado, dado que tuplas são imutáveis).
1 dados_originais = (10 , 20 , 30 , 40 , 50)
2 fatia_dos_dados = dados_originais [1:4] # fatia_dos_dados ser á (20 ,
30 , 40)
3
4 print ( f " Tupla Original : { dados_originais } , ID :
{ id ( dados_originais ) } " )
5 print ( f " Fatia da Tupla : { fatia_dos_dados } , ID :
{ id ( fatia_dos_dados ) } " ) # ID ser á diferente !
6
7 # Tentar modificar a fatia ( ou a original ) resultaria em erro , pois
tuplas s ã o imut á veis .
8 # fatia_dos_dados [0] = 99 # Isso GERARIA um TypeError !

Código 7.10: Fatias de tuplas criam novos objetos tupla

A capacidade de acessar elementos por índice e de extrair fatias é fundamental para


trabalhar com os dados armazenados em tuplas, mesmo que não possamos modificá-los
diretamente. Essas operações de leitura são eficientes e seguem exatamente as mesmas
regras que você aprendeu para as listas.
Na próxima seção, vamos explorar mais a fundo o conceito de imutabilidade das tuplas
e o que isso realmente implica na prática.

7.4 Imutabilidade em Detalhe: O que Significa Não


Poder Mudar?
A palavra-chave quando falamos de tuplas é imutabilidade. Já mencionamos isso
algumas vezes, mas o que exatamente significa para uma estrutura de dados ser imutável
na prática em Python?
Significa que, uma vez que uma tupla é criada e seus elementos são definidos,atupla
em si não pode mais ser alterada. Isso se refere a três aspectos principais:

1. Você não pode alterar o valor de um elemento existente em uma tupla.

2. Você não pode adicionar novos elementos a uma tupla.

3. Você não pode remover elementos de uma tupla.

Qualquer tentativa de realizar essas operações diretamente em uma tupla resultará em um


erro.
154 Capítulo 7. Tuplas (Tuples)

Tentando Modificar uma Tupla: O Erro TypeError


Vamos ver o que acontece quando tentamos quebrar a regra da imutabilidade:
1 # Uma tupla de exemplo
2 minha_tupla_fixa = (10 , 20 , " trinta " , 40.5)
3 print ( f " Tupla original : { minha_tupla_fixa } " )
4
5 # Tentativa 1: Alterar um elemento existente ( GERA TypeError )
6 # minha_tupla_fixa [0] = 5
7 # print (" Tentando alterar minha_tupla_fixa [0] = 5 ... ( Isso
causaria um TypeError !) ")
8
9 # Tentativa 2: Adicionar um novo elemento ( Tuplas n ã o t ê m append )
10 # minha_tupla_fixa . append (50) # Geraria um erro : AttributeError
11 # print (" Tentando minha_tupla_fixa . append (50) ... ( Isso causaria um
AttributeError !) ")
12

13 # Tentativa 3: Remover um elemento ( del em item gera TypeError )


14 # del minha_tupla_fixa [1]
15 # print (" Tentando del minha_tupla_fixa [1] ... ( Isso causaria um
TypeError !) ")

Código 7.11: Tentativas de modificar uma tupla (causam erro)

Como os exemplos (comentados para não interromperaexecução do texto do livro) demons-


tram, o Python protege a integridade da tupla impedindo essas operações de modificação.
Então, como “modificamos” uma tupla se precisarmos?
Se você precisa de uma tupla que é uma versão alterada de uma tupla existente, você
não modifica a original. Em vez disso, você cria uma nova tupla a partir da original,
incorporando as mudanças desejadas. Isso geralmente é feito usando concatenação (+) ou
fatiamento ([:]).
1 tupla_inicial = (1 , 2 , 3)
2 print ( f " Tupla inicial : { tupla_inicial } " )
3
4 # Para " adicionar " o n ú mero 4 , criamos uma nova tupla
5 nova_tupla_com_4 = tupla_inicial + (4 ,) # Concatenando com uma
tupla de um elemento
6 print ( f " Nova tupla ’ adicionando ’ 4: { nova_tupla_com_4 } " )
7
8 # Para " alterar " o primeiro elemento , criamos uma nova tupla
9 t u p l a _ a l t e r a d a _ p r i m e i r o = (100 ,) + tupla_inicial [1:]
10 print ( f " Nova tupla com primeiro elemento ’ alterado ’:
{ tupla_alterada_primeiro }")
11

12 print ( f " A tupla inicial continua a mesma : { tupla_inicial } " )

Código 7.12: Criando novas tuplas para simular modificação

A Nuance da Imutabilidade: Objetos Mutáveis Dentro de Tuplas


Aqui está um ponto muito importante que às vezes causa confusão: a imutabilidade
da tupla em si significa que as referências aos objetos que ela contém não podem ser
alteradas. Ou seja, você não pode fazer com que a posição 0 da tupla, que aponta para o
objeto A, passe a apontar para um objeto B. A “conexão” da tupla com seus objetos é fixa.
7.4. Imutabilidade em Detalhe: O que Significa Não Poder Mudar? 155

No entanto, se um dos objetos queatupla contém for, ele mesmo, um objeto mutável
(como uma lista), então o conteúdo desse objeto mutável interno PODE ser alterado.
A tupla ainda conterá uma referência à mesma lista de antes, mas o conteúdo dessa lista
pode ter mudado.
Vamos ilustrar com um exemplo:
1 # Tupla contendo uma lista ( que é um objeto mut á vel )
2 r e g i s t r o _ a l u n o _ n o t a s = ( " Carlos " , 21 , [7.5 , 8.0 , 9.2])
3 # Elementos : nome ( str ) , idade ( int ) , notas ( list )
4
5 print ( f " Registro original do aluno : { r e g i s t r o _ a l u n o _ n o ta s } " )
6 i d _ d a _ l i s t a _ i n t e r n a _ a n t e s = id ( r e g i s tr o _ a l u n o _ n o t a s [2])
7 print ( f " ID da lista de notas ( antes ) : { i d _ d a _ l i s t a _ i n t e r n a _ a n t e s } " )
8

9 # N ã o podemos fazer isso ( TypeError ) :


10 # r e gi s t r o _ a l u n o _n o t a s [0] = " Carlos Alberto "
11 # r e gi s t r o _ a l u n o _n o t a s [2] = [10.0 , 10.0]
12
13 # MAS , podemos modificar o conte ú do DA LISTA que est á DENTRO da
tupla :
14 print ( " Adicionando uma nova nota à lista interna ... " )
15 lista_interna = r e g i s t r o _ a l u n o _ n o t a s [2]
16 lista_interna . append (6.5)
17 # Ou diretamente : r e g i s t r o _ a l u n o _ n ot a s [2]. append (6.5)
18

19 print ( f " Registro do aluno ap ó s adicionar nota :


{ registro_aluno_notas }")
20 i d _ d a _ l i s t a _ i n t e r n a _ d e p o i s = id ( r e g is t r o _ a l u n o _ n o t a s [2])
21 print ( f " ID da lista de notas ( depois ) :
{ id_da_lista_interna_depois }")
22 # O ID da lista interna deve ser o MESMO .

Código 7.13: Modificando um objeto mutável dentro de uma tupla

Analogia: Pense em uma caixa de correio de metal fixada na parede com parafusos (a
tupla). Você não pode trocar a caixa de correio por outra, nem arrancar um dos seus
compartimentos. No entanto, se dentro de um dos compartimentos houver um envelope de
papel (um objeto mutável, como uma lista), você pode abrir esse envelope e adicionar ou
remover cartas (os itens da lista). O envelope ainda é o mesmo que estava originalmente
no compartimento, mas seu conteúdo mudou.
Por que essa distinção é importante?

• Efeitos Colaterais: Se múltiplas partes do seu código têm acesso a uma tupla que
contém objetos mutáveis, modificações nesses objetos internos serão visíveis para
todas as partes.

• Uso como Chaves de Dicionário: Tuplas só podem ser usadas como chaves em
dicionários se todos os seus elementos forem imutáveis. Se uma tupla contiver uma
lista, ela não poderá ser uma chave de dicionário.

A imutabilidade das tuplas é sobre a impossibilidade de alterar quais objetos a tupla


referencia diretamente. Se esses objetos referenciados são mutáveis, eles ainda podem ser
alterados. Isso oferece um grau de “mutabilidade contida” que é importante entender.
156 Capítulo 7. Tuplas (Tuples)

7.5 Operações e Métodos Comuns com Tuplas (Menos


que Listas)
Devido à sua natureza imutável, você descobrirá que as tuplas possuem um conjunto
menor de métodos embutidos quando comparadas às listas. A maioria dos métodos de
lista que vimos (como append(), insert(), remove(), sort(), reverse()) não existe
para tuplas, justamente porque esses métodos modificariam a coleção “in-place”, o que
viola o princípio da imutabilidade.
No entanto, as tuplas ainda suportam uma série de operações e alguns métodos que são
comuns a sequências e que não alteram a tupla original, mas sim retornam informações
sobre ela ou criam novas tuplas.

Operações Comuns com Tuplas


As seguintes operações, que já conhecemos de outros contextos como strings ou listas,
também se aplicam a tuplas:

• Concatenação (+):
Você pode juntar (concatenar) duas ou mais tuplas usando o operador +. Esta
operação resulta em uma nova tupla.
1 # Concatena ç ã o de tuplas
2 tupla_nomes1 = ( " Ana " , " Bruno " )
3 tupla_nomes2 = ( " Carlos " , " Daniela " )
4
5 tupla_combinada = tupla_nomes1 + tupla_nomes2
6 print ( f " Tupla 1: { tupla_nomes1 } " )
7 print ( f " Tupla 2: { tupla_nomes2 } " )
8 print ( f " Tupla Combinada : { tupla_combinada } " )
9 # Sa í da : ( ’ Ana ’, ’ Bruno ’, ’ Carlos ’, ’ Daniela ’)

Código 7.14: Concatenação de tuplas

• Repetição (*):
Você pode repetir os elementos de uma tupla um certo número de vezes usando o
operador *. Isso também cria uma nova tupla.
1 # Repeti ç ã o de tuplas
2 padrao_repetido = ( " A " , " B " ) * 3
3 print ( f " Padr ã o repetido 3 vezes : { padrao_repetido } " )
4 # Sa í da : ( ’ A ’, ’B ’, ’A ’, ’B ’, ’A ’, ’B ’)
5
6 simbolo_unico = (0 ,) # Lembre - se da v í rgula para tupla de
um elemento !
7 linha_de_zeros = simbolo_unico * 5
8 print ( f " Linha de zeros : { linha_de_zeros } " )
9 # Sa í da : (0 , 0 , 0 , 0 , 0)

Código 7.15: Repetição de tuplas

• Função len(minha_tupla):
Retorna o número de itens na tupla.
7.5. Operações e Métodos Comuns com Tuplas (Menos que Listas) 157

1 dados_rgb = (255 , 128 , 0) # Vermelho , Verde , Azul


2 q u a n t i da d e _ e l e m e n t o s = len ( dados_rgb )
3 print ( f " A tupla de cores RGB tem { q u an t i d a d e _ e l e m e n t o s }
componentes . " )

Código 7.16: Usando len() com tuplas

• Operadores de Associação (in, not in):


Verificam se um item está presente ou não na tupla.
1 p er mis so es _us ua ri o = ( " ler " , " escrever " , " executar " )
2
3 t e m _ p e r m i s s a o _ l e i t u r a = " ler " in p er mis so es _u sua ri o
4 print ( f " Usu á rio tem permiss ã o de leitura ?
{ tem_permissao_leitura }")
5
6 te m _p e rm i s sa o _a d m in = " admin " in p er mis so es _us ua ri o
7 print ( f " Usu á rio tem permiss ã o de admin ?
{ t e m_ p er m i ss a o_ a d mi n } " )
8
9 n a o _ t e m _ p e r m i s s a o _ a p a g a r = " apagar " not in
pe rm is soe s_ us uar io
10 print ( f " Usu á rio N Ã O tem permiss ã o para apagar ?
{ nao_tem_permissao_apagar }")

Código 7.17: Operadores de associação com tuplas

• Iteração com Laço for:


Tuplas são iteráveis e podem ser percorridas com um laço for.
1 # Iterando sobre uma tupla com um la ç o for
2 coord enadas_p onto = (10.5 , -3.2 , 7.0) # x , y , z
3 print ( " Coordenadas do ponto : " )
4 for coordenada_valor in coordena das_pont o :
5 print ( f " Valor : { coordenada_valor } " )

Código 7.18: Iterando sobre uma tupla com for

Métodos Específicos de Tuplas


Devido à sua imutabilidade, as tuplas têm apenas dois métodos principais que não
envolvem modificação:

• minha_tupla.count(item):
Retorna o número de vezes que o item especificado aparece dentro da tupla.
1 # Usando o m é todo count () em tuplas
2 s eq uen ci a_ num er ic a = (1 , 2 , 5 , 2 , 8 , 2 , 5 , 9)
3
4 ocorrencias_do_2 = s equ en ci a_n um er ica . count (2)
5 print ( f " O n ú mero 2 aparece { ocorrencias_do_2 } vezes na
tupla . " )
6

7 ocorrencias_do_7 = s equ en ci a_n um er ica . count (7)


8 print ( f " O n ú mero 7 aparece { ocorrencias_do_7 } vezes na
tupla . " )
158 Capítulo 7. Tuplas (Tuples)

Código 7.19: Usando o método count() em tuplas

• minha_tupla.index(item, inicio_opcional, fim_opcional):


Retorna o índice da primeira ocorrência do item especificado.

– Se o item não for encontrado, levanta um erro ValueError.


– inicio e fim opcionais restringem a busca.
1 # Usando o m é todo index () em tuplas
2 nomes _dias_se mana = ( " Segunda " , " Ter ç a " , " Quarta " ,
" Quinta " , " Sexta " , " Ter ç a " )
3
4 dia_procurado = " Quarta "
5 if dia_procurado in nomes_d ias_sema na : # Boa pr á tica :
verificar antes com ’ in ’
6 indice_quarta = n omes_dia s_semana . index ( dia_procurado )
7 print ( f " ’{ dia_procurado } ’ est á no í ndice :
{ indice_quarta } " )
8 else :
9 print ( f " ’{ dia_procurado } ’ n ã o encontrado . " )
10

11 # Procurando a primeira ocorr ê ncia de " Ter ç a "


12 i n d i c e _ p r i m e i r a _ t e r c a = nomes_di as_seman a . index ( " Ter ç a " )
13 print ( f " Primeira ’ Ter ç a ’ est á no í ndice :
{ indice_primeira_terca }")
14
15 # Procurando " Ter ç a " a partir do í ndice 2
16 if " Ter ç a " in nomes _dias_se mana [ i n d i c e _ p r i m e i r a _ t e r c a +
1:]:
17 i n d i c e _ s e g u n d a _t e r c a =
nomes _dias_se mana . index ( " Ter ç a " ,
i n d i c e _ p r i m e i r a _ t e r c a + 1)
18 print ( f " Segunda ’ Ter ç a ’ est á no í ndice :
{ indice_segunda_terca }")

Código 7.20: Usando o método index() em tuplas

Por que Tão Poucos Métodos? A Imutabilidade em Ação!


Você deve ter notado que a lista de métodos para tuplas é consideravelmente menor
do que a das listas. Não há append(), extend(), insert(), remove(), pop(), sort(),
reverse(), etc.
Isso ocorre precisamente porque as tuplas são imutáveis. Todos esses métodos ausentes
são aqueles que modificariam a coleção original. Como as tuplas não podem ser alteradas
apósacriação, esses métodos simplesmente não fariam sentido para elas.
Se você se encontrar em uma situação onde tem uma tupla e precisa realizar operações
queamodificariam, geralmente o caminho é:
1. Converter a tupla para uma lista: minha_lista = list(minha_tupla)

2. Realizar as modificações na lista.

3. Se necessário, converter a lista de volta para uma tupla: nova_tupla = tuple(minha_lista).


7.6. Desempacotamento de Tuplas (Tuple Unpacking) 159

As tuplas, portanto, suportam todas as operações essenciais de sequências que não envolvem
mutação, mas sua força reside em sua garantia de imutabilidade.

7.6 Desempacotamento de Tuplas (Tuple Unpacking)


Python oferece uma maneira incrivelmente conveniente e expressiva de atribuir os
elementos de uma tupla (ou de outras sequências como listas e strings) a múltiplas variáveis
individuais, tudo em uma única instrução. Essa técnica é chamada de desempacotamento
de tuplas (ou, de forma mais geral, desempacotamento de sequências).
Essa funcionalidade não apenas torna o código mais conciso, mas também mais legível
em muitas situações, especialmente ao lidar com funções que retornam múltiplos valores.

Como Funciona o Desempacotamento Básico


A sintaxe fundamental do desempacotamento envolve ter um conjunto de variáveis
no lado esquerdo de uma atribuição (=) e uma tupla (ou outra sequência) com o mesmo
número de elementos no lado direito.

variavel1, variavel2, ..., variavelN = tupla_com_N_elementos

O Python, então, “desempacota” a tupla, atribuindo o primeiro elemento da tupla à


primeira variável, o segundo elemento à segunda variável, e assim por diante.
Requisito: O número de variáveis à esquerda deve ser exatamente igual ao número
de elementos na tupla à direita. Se não for, o Python levantará um erro ValueError.
1 # Exemplo 1: Desempacotando coordenadas de um ponto 2 D
2 ponto_cartesiano = (10 , 25) # Uma tupla representando (x , y )
3
4 x_coord , y_coord = ponto_cartesiano # Desempacotamento !
5
6 print ( f " A coordenada x é : { x_coord } " )
7 print ( f " A coordenada y é : { y_coord } " )
8

9 # Exemplo 2: Desempacotando informa ç õ es de um registro de aluno


10 dados_aluno = ( " Beatriz Nogueira " , 20 , " Ci ê ncia da Computa ç ã o " )
11
12 nome_completo , idade_aluna , curs o_matricu lado = dados_aluno
13 print ( f " Aluna : { nome_completo } " )
14 print ( f " Idade : { idade_aluna } anos " )
15 print ( f " Curso : { curso_m atriculad o } " )
16
17 # Exemplo de erro ( descomente para testar ) :
18 # t u pl a _ t r e s _ e l e me n t o s = (1 , 2 , 3)
19 # var_a , var_b = t u p l a _ t r e s _ e l e me n t o s # ValueError

Código 7.21: Desempacotamento básico de tuplas

Vale notar queaatribuição múltipla que vimos antes (ex: a, b = 10, 20) é, na verdade,
um caso de desempacotamento.

Uso em Laços for (Relembrando)


Já vimos o desempacotamento em ação ao iterar sobre listas de tuplas com laços for:
160 Capítulo 7. Tuplas (Tuples)

1 # Lista de tuplas , onde cada tupla é ( produto , preco )


2 lista_de_compras = [
3 ( " Ma ç ã " , 5.50) ,
4 ( " P ã o Integral " , 8.75) ,
5 ( " Leite " , 4.20)
6 ]
7
8 print ( " Itens e Pre ç os : " )
9 for produto , preco_unitario in lista_de_compras : # Desempacotamento
aqui !
10 print ( f " - { produto }: R$ { preco_unitario :.2 f } " )

Código 7.22: Desempacotamento em um laço for

Desempacotamento Estendido com * (Python 3+)


O Python 3 introduziu o desempacotamento estendido usando o operador asterisco (*).
Ele permite que uma variável “pegue” múltiplos itens restantes de uma sequência, que são
agrupados em uma lista.

• Pegando o “resto” dos itens:


1 numer os_seque ncia = (1 , 2 , 3 , 4 , 5 , 6 , 7)
2
3 # Pegando o primeiro , o segundo , e o resto em uma lista
4 primeiro , segundo , * resto_da_lista = numero s_sequen cia
5 print ( f " Primeiro : { primeiro } " )
6 print ( f " Segundo : { segundo } " )
7 print ( f " Resto ( como lista ) : { resto_da_lista } " )
8
9 # Pegando o primeiro , o ú ltimo , e os do meio em uma lista
10 inicio_seq , * elementos_do_meio , fim_seq = numeros_ sequenci a
11 print ( f " In í cio : { inicio_seq } " )
12 print ( f " Meio ( como lista ) : { el ementos_ do_meio } " )
13 print ( f " Fim : { fim_seq } " )
14
15 # Se n ã o houver itens para o " resto " , a lista fica vazia
16 a , b , c , * nenhum_resto = (10 , 20 , 30)
17 print ( f " Nenhum resto : { nenhum_resto } " )

Código 7.23: Desempacotamento estendido com *

• Ignorando Variáveis com _ ou *_:


Para ignorar valores desempacotados, use _ para um único valor ou *_ para múltiplos.
1 # Tupla : ( id_sensor , leitura , timestamp , status_bateria )
2 d a d o s _ s e n s o r _ c o m p l e t o = ( " TEMP001 " , 23.5 ,
" 2025 -05 -23 T12 :00:00 Z " , " OK " )
3
4 id_sensor , leitura_atual , * ignorar_resto =
dados_sensor_completo
5 print ( f " ID do Sensor : { id_sensor } , Leitura :
{ leitura_atual } " )
6
7 primeiro_dado , *_ , ultimo_dado = ( " A " , " B " , " C " , " D " , " E " )
# Ignora B , C , D
7.7. Quando Usar Tuplas em vez de Listas? 161

8 print ( f " Primeiro : { primeiro_dado } , Ú ltimo : { ultimo_dado } " )

Código 7.24: Ignorando valores no desempacotamento

Principal Utilidade Prática: Retorno de Múltiplos Valores de Fun-


ções
Uma das aplicações mais comuns do desempacotamento é ao lidar com funções que
retornam múltiplos valores (elas retornam uma tupla).
1 # Uma fun ç ã o simples que retorna dois valores
2 # N ã o se preocupe , ainda veremos fun ç õ es !
3 def o b t e r _ d i m e n s o e s _ r e t a n g u l o () :
4 base = 10.0
5 altura = 5.5
6 return base , altura # Retorna implicitamente a tupla (10.0 , 5.5)
7
8 # Chamando a fun ç ã o e desempacotando o resultado
9 b , h = o b t e r _ d i m e n s o e s _ r e t a n g u l o ()
10
11 print ( f " Base do ret â ngulo : { b } " )
12 print ( f " Altura do ret â ngulo : { h } " )
13 area = b * h
14 print ( f " Á rea calculada : { area } " )
15
16 # Ou podemos receber a tupla inteira
17 dimensoes = o b t e r _ d i m e n s o e s _ r e t a n g u l o ()
18 print ( f " Tupla de dimens õ es : { dimensoes } " )

Código 7.25: Desempacotando retorno de função (prévia do Cap. 10)

Trocando Valores de Variáveis de Forma Pythônica (Swapping)


O desempacotamento permite uma forma concisa de trocar os valores entre duas
variáveis:
1 # Trocando valores de vari á veis
2 var_x = 100
3 var_y = " Python "
4 print ( f " Antes da troca -> var_x : { var_x } , var_y : ’{ var_y } ’ " )
5
6 # A m á gica do desempacotamento para trocar ( swap )
7 var_x , var_y = var_y , var_x
8
9 print ( f " Depois da troca -> var_x : ’{ var_x } ’ , var_y : { var_y } " )

Código 7.26: Trocando valores de variáveis (swapping)


O desempacotamento é uma característica expressiva do Python que, quando usada
apropriadamente, pode tornar seu código mais limpo, mais legível e mais direto ao ponto.

7.7 Quando Usar Tuplas em vez de Listas?


Ao longo deste capítulo e do anterior, exploramos duas das estruturas de dados
sequenciais mais importantes do Python: listas e tuplas. Ambas são capazes de armazenar
162 Capítulo 7. Tuplas (Tuples)

coleções ordenadas de itens, e muitas operações (como acesso por índice, fatiamento,
iteração) são idênticas entre elas. A grande diferença, como já enfatizamos, reside na
mutabilidade: listas são mutáveis, enquanto tuplas são imutáveis.
Então, uma pergunta natural surge: “Quando devo escolher usar uma tupla em vez de
uma lista, ou vice-versa?” Não existe uma resposta única que sirva para todos os casos,
mas podemos seguir algumas diretrizes baseadas nas características de cada uma e no que
você deseja comunicar com seu código.

Situações Onde Tuplas São Preferíveis (ou Necessárias)


1. Para Dados que Não Devem Mudar (Integridade e Constantes):
Se você tem uma coleção de itens que representa algo fixo, que não deve ser alterado
acidentalmente apósacriação, a imutabilidade da tupla oferece essa proteção.
• Exemplos: Coordenadas geográficas (40.7128, -74.0060), cores RGB (0, 0,
255).
2. Uso como Chaves em Dicionários:
Dicionários (Capítulo 8) exigem que suas chaves sejam de tipos imutáveis. Tuplas
(contendo apenas elementos imutáveis) podem ser chaves; listas não podem.
1 # Exemplo conceitual ( dicion á rios ser ã o vistos no Cap . 8)
2 capitais = {} # Um dicion á rio vazio
3 chave_es = ( " Esp í rito Santo " , " ES " )
4 capitais [ chave_es ] = " Vit ó ria " # Usando uma tupla como
chave - FUNCIONA !
5
6 # chave_lista_mg = [" Minas Gerais " , " MG "] # Uma lista
7 # capitais [ chave_lista_mg ] = " Belo Horizonte " # ISSO
GERARIA UM TypeError !
8

9 print ( f " A capital de { chave_es } é { capitais [ chave_es ]}. " )

Código 7.27: Tupla como chave de dicionário (conceitual)

3. Retorno de Múltiplos Valores de Funções (Boa Prática):


Quando uma função precisa retornar múltiplos valores, ela geralmente os retorna
como uma tupla.
1 def obter_min_max ( numeros ) :
2 # Supondo que ’ numeros ’ seja uma lista n ã o vazia
3 minimo = min ( numeros )
4 maximo = max ( numeros )
5 return minimo , maximo # Retorna implicitamente a tupla
( minimo , maximo )
6
7 valores = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6]
8 val_min , val_max = obter_min_max ( valores ) # Desempacotando
9 print ( f " Valor m í nimo : { val_min } , Valor m á ximo : { val_max } " )

Código 7.28: Função retornando múltiplos valores (tupla)

4. Semântica e Intenção Clara no Código:


Usar uma tupla pode comunicar que uma coleção é um “registro” fixo, enquanto uma
lista sugere uma coleção que pode mudar.
7.7. Quando Usar Tuplas em vez de Listas? 163

5. Performance (Consideração Menor, mas Existente):


Tuplas podem ser marginalmente mais rápidas para criar e iterar em alguns casos.
No entanto, a clareza e a corretude da lógica (mutável vs. imutável) são geralmente
considerações mais importantes.

Quando Listas Ainda São a Melhor Escolha


• Quando você precisa de uma coleção que será modificada apósacriação.

• Quando você precisa dos métodos específicos de listas que alteramacoleção


in-place, como append(), sort(), remove(), etc.

• Quando a flexibilidade de uma coleção que pode crescer e encolher dinamicamente é


o mais importante.

Concluindo o Capítulo sobre Tuplas


Neste capítulo, desvendamos as tuplas, as “primas” imutáveis das listas. Vimos como
criá-las (com atenção especial à sintaxe para tuplas de um único elemento), como acessar
seus elementos e obter fatias (de forma idêntica às listas), e exploramos em profundidade
o significado e as implicações da sua imutabilidade – incluindo a nuance de conter objetos
mutáveis. Também cobrimos as operações e os poucos métodos disponíveis para tuplas, o
poderoso recurso de desempacotamento e, finalmente, discutimos quando as tuplas são a
escolha mais adequada em detrimento das listas.
As tuplas oferecem uma forma de garantiraintegridade de coleções de dados que não
devem ser alteradas e desempenham um papel importante em certos contextos da linguagem,
como chaves de dicionários e o retorno de múltiplos valores por funções. Entender suas
características e quando usá-las enriquecerá seu repertório como programador Python.
No próximo capítulo, vamos explorar outra estrutura de dados fundamental e extre-
mamente poderosa: os dicionários, que nos permitem armazenar e acessar dados não por
uma posição numérica, mas através de chaves únicas.
Capítulo 8

Dicionários (Dictionaries)

Nos capítulos anteriores, exploramos as listas e tuplas, que são estruturas de dados do
tipo sequência. Nelas, os elementos são armazenados em uma ordem específica e acessados
através de um índice numérico (0, 1, 2,...). Isso é muito útil quando a ordem dos itens é
importante ou quando queremos simplesmente uma coleção de valores.
No entanto, há muitas situações em que acessar dados por um índice numérico não
éaforma mais intuitiva ou eficiente. Imagine que você quer guardar informações sobre
uma pessoa, como nome, idade e cidade. Seria mais natural acessaraidade da pessoa
usandoa“etiqueta” “idade” do que lembrando que ela está, digamos, no índice 1 de uma
lista. Ou pense em um dicionário de tradução: você procura uma palavra (a chave) para
encontrar seu significado (o valor).
É para esses cenários que o Python nos oferece os dicionários (dict). Eles são uma
estrutura de dados extremamente versátil que armazena coleções de itens não de forma
ordenada por posição, mas sim através de um sistema de pares chave-valor.

8.1 Introdução aos Dicionários: Coleções de Pares Chave-


Valor
Um dicionário em Python é uma coleção de itens onde cada item é um par composto
por uma chave (key) e um valor (value) associado a essa chave. Pense nele como um
dicionário de palavras real:

• A palavra que você procura é a chave.

• A definição dessa palavra é o valor.

Outras analogias úteis incluem:

• Lista telefônica: O nome da pessoa é a chave, e o número de telefone é o valor.

• Catálogo de produtos: O código do produto é a chave, eadescrição (ou preço) do


produto é o valor.

• Ficha de um aluno: O campo (ex: “Nome”, “Matrícula”, “Curso”) é a chave, e a


informação correspondente (ex: “João Silva”, “2024001”, “Engenharia Elétrica”) é o
valor.

164
8.2. Criando Dicionários: Formando Nossos Mapeamentos Chave-Valor 165

Características Principais dos Dicionários


1. Pares Chave-Valor: A unidade fundamental de um dicionário é o par chave:
valor.

2. Chaves Únicas: Dentro de um mesmo dicionário, cada chave deve ser única. Se
você tentar adicionar um par com uma chave que já existe, o valor antigo associado
àquela chave será substituído pelo novo.

3. Chaves Imutáveis: As chaves de um dicionário devem ser de um tipo de dado


imutável. Isso significa que você pode usar strings, números (inteiros, floats), ou
tuplas (desde que as tuplas contenham apenas elementos imutáveis) como chaves.
Você não pode usar listas ou outros dicionários como chaves.

4. Valores de Qualquer Tipo: Os valores associados às chaves em um dicionário po-


dem ser de qualquer tipo de dado Python (números, strings, listas, outros dicionários,
etc.) e podem, inclusive, ser duplicados.

5. Mutáveis: Os dicionários em si são mutáveis. Você pode adicionar novos pares


chave-valor, modificar os valores de chaves existentes e remover pares apósacriação
do dicionário.

6. Acesso por Chave (Não por Índice Numérico): A principal forma de acessar
um valor em um dicionário é através de sua chave correspondente.

7. Ordem de Inserção (Python 3.7+): Em versões do Python a partir da 3.7, os


dicionários mantêm a ordem em que os itens foram inseridos. Embora essa
preservação da ordem seja uma característica conveniente, a forma fundamental de
interagir com dicionários ainda é através de suas chaves.

Os dicionários são uma das estruturas de dados mais poderosas e flexíveis do Python,
usados extensivamente para representar dados estruturados, mapeamentos, contagens de
frequência, e muito mais. Neste capítulo, aprenderemos como criar dicionários, adicionar,
acessar, modificar e remover seus pares chave-valor, iterar sobre eles e utilizar seus métodos
mais comuns.

8.2 Criando Dicionários: Formando Nossos Mapeamen-


tos Chave-Valor
Agora que entendemos o conceito fundamental dos dicionários em Python – coleções de
pares chave-valor onde cada chave é única e imutável, e os valores podem ser de qualquer
tipo – vamos aprender as diversas maneiras de efetivamente criá-los. Python, como de
costume, nos oferece algumas formas flexíveis e intuitivas para essa tarefa.

A Sintaxe Principal: Usando Chaves {} e Pares chave: valor


A forma mais comum e visualmente direta de criar um dicionário é definir seus pares
chave: valor separados por vírgulas, tudo dentro de um par de chaves {}.
A sintaxe geral é:
166 Capítulo 8. Dicionários (Dictionaries)

nome_do_dicionario = {chave1: valor1, chave2: valor2, chave3: valor3,


...}

• Criando um Dicionário Vazio:


Frequentemente, você precisará iniciar um dicionário vazio para depois adicionar
itens a ele dinamicamente. Para isso, use apenas as chaves:
1 # Criando um dicion á rio vazio para um card á pio
2 c a r d a p io _ r e s t a u r a n t e = {}
3 print ( f " Card á pio inicial ( vazio ) : { c a r d a p i o _r e s t a u r a n t e } " )
4 print ( f " O tipo da vari á vel ’ c a r d a p i o _ r e s t au r a n t e ’ é :
{ type ( c a r d a p i o _ r e s t a u r an t e ) } " )
5
6 # Tamb é m é poss í vel usar a fun ç ã o construtora
7 o u t r o _ d i c i o n a r i o _ v a z i o = dict ()
8 print ( f " Outro dicion á rio vazio : { o u t r o _ d i c i o n a r i o _ v a z i o } " )

Código 8.1: Criando um dicionário vazio

Atenção: Um par de chaves {} vazio sempre cria um dicionário vazio. Para criar
um conjunto vazio (outra estrutura de dados que veremos no Capítulo 9), você deve
usar a função set().

• Criando Dicionários com Elementos:


Você pode popular um dicionário com pares chave-valor desde a sua criação. Lembre-
se que as chaves devem ser de tipos imutáveis.
1 # Dicion á rio com informa ç õ es de um aluno
2 aluno_info = {
3 " nome_completo " : " Carlos Alberto N ó brega " ,
4 " matricula_id " : 20240115 ,
5 " curso_atual " : " Engenharia de Computa ç ã o " ,
6 " d i s c i p l i n a s _ c u rs a n d o " : [ " Algoritmos II " , " C á lculo
Vetorial " ] ,
7 " ativo_no_curso " : True
8 }
9 print ( f " Informa ç õ es do aluno :\ n { aluno_info } " )
10
11 # Dicion á rio de c ó digos de erro
12 codigos_erro = {
13 404: " N ã o Encontrado " ,
14 500: " Erro Interno do Servidor " ,
15 200: " OK "
16 }
17 print ( f " \ nAlguns c ó digos HTTP : { codigos_erro } " )
18
19 # Usando tuplas como chaves ( desde que a tupla s ó contenha
imut á veis )
20 estoque_pecas_por_local = {
21 ( " Bloco A " , " Corredor 3 " ) : 150 ,
22 ( " Bloco C " , " Prateleira 7 " ) : 85
23 }
24 print ( f " \ nEstoque em ( ’ Bloco A ’, ’ Corredor 3 ’) :
{ e s t o q u e _ p e c a s _ p o r _ l o c a l [( ’ Bloco A ’, ’ Corredor 3 ’) ]} " )
25
26 # Tentativa de usar uma lista como chave ( GERARIA ERRO !)
27 # dicionario_com_chave_lista_errada = {
8.2. Criando Dicionários: Formando Nossos Mapeamentos Chave-Valor 167

28 # [" chave " , " mutavel "]: " Este valor n ã o seria
alcan ç ado "
29 # }

Código 8.2: Criando dicionários com elementos

Usando a Função Construtora dict()


Além da sintaxe literal com {}, a função embutida dict() oferece outras maneiras
flexíveis de criar dicionários:
1. A partir de argumentos nomeados (keywords):
Os nomes dos argumentos se tornam as chaves (strings), e os valores dos argumentos
se tornam os valores do dicionário.
1 c o n f i g ur a c a o _ s i s t e m a = dict (
2 usuario = " admin " ,
3 porta_servidor =8080 ,
4 modo_debug = True ,
5 idioma_padrao = " pt - br "
6 )
7 print ( f " \ nConfigura ç õ es do sistema ( via dict () ) :
{ configuracao_sistema }")

Código 8.3: Criando dicionário com argumentos nomeados em dict()

2. A partir de um iterável de pares chave-valor:


Você pode passar para dict() um iterável (como uma lista) que contenha outros
iteráveis de dois elementos cada (geralmente tuplas (chave, valor)).
1 lista_de_pares_contatos = [
2 ( " Ana " , " ana@email . com " ) ,
3 ( " Bruno " , " bruno@email . com " ) ,
4 ( " Carla " , " carla@email . com " )
5 ]
6 di c io n ar i o _c o nt a t os = dict ( l i s t a _ d e _ p a r e s _ c o n t a t o s )
7 print ( f " \ nDicion á rio de contatos : { di c io n a ri o _c o n ta t os } " )

Código 8.4: Criando dicionário a partir de lista de tuplas

3. A partir de duas sequências paralelas usando zip():


A função zip() combina elementos de duas (ou mais) sequências em pares, que
podem então ser usados para criar um dicionário.
1 # Criando um dicion á rio a partir de duas listas usando
zip ()
2 l i s t a _ d e _ c h a v e s _ p r o d u t o s = [ " P001 " , " P002 " , " P003 " ]
3 l i s t a _ d e _ n o m e s _ p r o d u t o s = [ " Mouse Gamer " , " Teclado
Mec â nico " , " Monitor UltraWide " ]
4
5 catal ogo_prod utos = dict ( zip ( lista_de_chaves_produtos ,
lista_de_nomes_produtos ))
6 print ( f " \ nCat á logo de produtos ( com zip () ) :
{ catalogo _produto s } " )

Código 8.5: Criando dicionário com zip()


168 Capítulo 8. Dicionários (Dictionaries)

Se as sequências passadas para zip() tiverem tamanhos diferentes, zip() para no


final da sequência mais curta.

Dicionários e Quebras de Linha no Código Fonte


Assim como com listas e tuplas, ao definir um dicionário com muitos pares chave-valor,
é uma boa prática quebrar a definição em múltiplas linhas dentro das chaves {} para
melhorar a legibilidade do seu código.
1 # Dicion á rio representando as propriedades de um material de
engenharia
2 propriedades_aco_1020 = {
3 " nome_comercial " : " A ç o Carbono 1020 " ,
4 " densidade_kg_m3 " : 7850 , # Em kg / m ^3
5 " l i m i t e _ e s c o a m e n t o _ M P a " : 350 , # Em MegaPascals
6 " m o d u l o _ e l a s t i c i d a d e _ G P a " : 200 , # Em GigaPascals
7 " c o n d u t i v i d a d e _ t e r m i c a _ W _ m K " : 52 , # Em W /( m x K )
8 " usos_comuns " : [ " Eixos " , " Engrenagens leves " , " Componentes
estruturais " ]
9 }
10
11 print ( f " \ nPropriedades do
{ p r o p r i e d a d e s _ a c o _ 1 0 2 0 [ ’ nome_comercial ’]}: " )
12 print ( f " Densidade : { p r o p r i e d a d e s _ a c o _ 1 0 2 0 [ ’ densidade_kg_m3 ’]}
kg / m ^3 " )

Código 8.6: Definindo um dicionário longo em múltiplas linhas


A indentação dos pares chave-valor nas linhas subsequentes é uma questão de estilo que
contribui muito para a clareza.
Com essas diferentes abordagens, você tem à disposição várias maneiras de criar
dicionários. A escolha dependerá da situação e de como seus dados estão organizados
inicialmente. O importante é lembrar da estrutura fundamental de pares chave: valor
e das regras para as chaves.

8.3 Acessando Valores no Dicionário: A Chave para a


Informação
Uma vez que temos um dicionário populado com nossos pares chave-valor, a tarefa mais
comum é, sem dúvida, acessar um valor específico utilizando sua chave correspondente.
Esta é a operação fundamental que torna os dicionários tão úteis: eles nos permitem
recuperar informações de forma eficiente e intuitiva, usando um identificador significativo
(a chave) em vez de um índice numérico (como nas listas e tuplas).

Acesso Direto Usando a Chave (Notação de Colchetes [])


A maneira mais direta de acessar o valor associado a uma chave em um dicionário é
usando colchetes [] após o nome do dicionário, com a chave desejada dentro deles.
A sintaxe é: valor_desejado = nome_do_dicionario[chave_especifica]
• A chave_especifica deve ser exatamente igual (mesmo valor e mesmo tipo) a uma
das chaves existentes no dicionário. Lembre-se que as chaves string são sensíveis a
maiúsculas e minúsculas.
8.3. Acessando Valores no Dicionário: A Chave para a Informação 169

• Se a chave existir no dicionário, esta expressão retornará o valor associado a ela.

1 # Dicion á rio de exemplo : tradu ç ã o simples de cores


2 tradutor_cores = {
3 " red " : " vermelho " ,
4 " blue " : " azul " ,
5 " green " : " verde " ,
6 " yellow " : " amarelo "
7 }
8
9 # Acessando a tradu ç ã o de " blue "
10 traducao_azul = tradutor_cores [ " blue " ]
11 print ( f " A tradu ç ã o de ’ blue ’ é : { traducao_azul } " )
12
13 # Acessando a tradu ç ã o de " yellow "
14 print ( f " A tradu ç ã o de ’ yellow ’ é : { tradutor_cores [ ’ yellow ’]} " )

Código 8.7: Acessando valores de um dicionário por chave


O Problema da Chave Inexistente: Cuidado com o KeyError!
Se você tentar acessar um valor usando uma chave que não existe no dicionário através da
notação de colchetes ([]), o Python levantará um erro chamado KeyError, e a execução
do seu programa será interrompida.
1 # Continuamos com o dicion á rio tradutor_cores
2 # tradutor_cores = {" red ": " vermelho " , " blue ": " azul " , ...}
3
4 # Tentativa de acessar uma chave que N Ã O existe no dicion á rio :
5 # traducao_roxo = tradutor_cores [" purple "]
6 # A linha ACIMA , se executada , causaria um erro :
7 # KeyError : ’ purple ’
8

9 # Apenas para fins de impress ã o no livro , o c ó digo acima est á


comentado .
10 print ( " Tentando acessar tradutor_cores [ ’ purple ’]... ( Isso causaria
um KeyError !) " )

Código 8.8: Tentativa de acesso a chave inexistente (gera KeyError)

Acesso Seguro com o Método get(chave, valor_padrao_opcional)


Para evitar o KeyError e ter um controle mais fino sobre o que acontece quando uma
chave não é encontrada, os dicionários Python oferecem o método get().
A sintaxe é:
valor = nome_do_dicionario.get(chave_desejada)
ou
valor = nome_do_dicionario.get(chave_desejada, valor_padrao_se_nao_encontrar)

• Se a chave_desejada existir, get() retorna o valor associado.

• Se a chave_desejada não existir:

– Sem o segundo argumento, retorna None.


– Com o segundo argumento, retorna esse valor padrão.
170 Capítulo 8. Dicionários (Dictionaries)

1 # Dicion á rio de estoque de produtos


2 estoque_loja = {
3 " camiseta_P " : 50 ,
4 " calca_jeans_M " : 30 ,
5 " jaqueta_couro_G " : 15
6 }
7
8 # Usando get () para uma chave existente
9 q u a n t i d a d e _ c a m i s e t a s _ p = estoque_loja . get ( " camiseta_P " )
10 print ( f " Quantidade de Camisetas P : { q u a n t i d a d e _ c a m i s e t a s _ p } " )
11
12 # Usando get () para uma chave inexistente ( sem valor padr ã o )
13 q ua n tid a de _ b er m ud a s = estoque_loja . get ( " bermuda_G " )
14 print ( f " Quantidade de Bermudas G : { q ua n t id a de _ b er m ud a s } " ) # Sa í da :
None
15 if q ua n ti d ad e _ be r mu d a s is None :
16 print ( " N ã o temos bermudas G em estoque ou este item n ã o est á
cadastrado . " )
17
18 # Usando get () para uma chave inexistente ( com um valor padr ã o )
19 quantidade_meias = estoque_loja . get ( " meias_unicas " , 0)
20 print ( f " Quantidade de Meias Ú nicas : { quantidade_meias } " )

Código 8.9: Usando o método get() para acessar valores

Quando Usar Acesso Direto [] vs. Método get()?

• Use nome_do_dicionario[chave] quando você tem a certeza de que a chave deve


existir. O KeyError pode ser útil para sinalizar um problema.

• Use nome_do_dicionario.get(chave, ...) quando é aceitável ou esperado que


a chave possa não existir, e você quer lidar com essa ausência de forma controlada.

Lembre-se: Chaves são Sensíveis a Maiúsculas e Minúsculas


Se suas chaves são strings, é crucial lembrar que Python diferencia letras maiúsculas
de minúsculas.
1 configuracoes_usuario = {
2 " NomeCompleto " : " Jos é da Silva " ,
3 " email " : " jose . silva@exemplo . com "
4 }
5
6 # Acesso correto
7 print ( f " Email do usu á rio : { c o n f i g u r a c o e s _ u s u a r i o [ ’ email ’]} " )
8
9 # Tentativa de acesso com ’ Nomecompleto ’ ( min ú sculo ) - GERARIA
KeyError
10 # print ( c o n f i g u r a c o e s _ u s u a r i o [ ’ nomecompleto ’])

Código 8.10: Sensibilidade a maiúsculas/minúsculas nas chaves

O acesso a valores usando suas chaves é a operação mais fundamental ao trabalhar


com dicionários. A escolha entre a notação de colchetes [] e o método get() dependerá
de como você precisa que seu programa se comporte caso uma chave específica não seja
encontrada.
8.4. Adicionando e Modificando Pares Chave-Valor 171

8.4 Adicionando e Modificando Pares Chave-Valor


Uma das grandes vantagens dos dicionários em Python, assim como das listas, é a
sua mutabilidade. Isso significa que, após um dicionário ser criado, podemos facilmente
adicionar novos pares chave-valor a ele, ou modificar o valor associado a uma chave que já
existe. Essa flexibilidade é crucial para representar dados que evoluem durante a execução
de um programa.

Adicionando Novos Pares Chave-Valor


A forma mais comum e direta de adicionar um novo par chave-valor a um dicionário
existente é utilizando a sintaxe de atribuição com colchetes, onde a nova chave é especificada
dentro dos colchetes:

nome_do_dicionario[nova_chave] = novo_valor

Se a nova_chave não existir previamente no dicionário, este comando criará um novo par.
Lembre-se que a nova_chave deve ser de um tipo imutável.
1 # Come ç ando com um dicion á rio de informa ç õ es de um produto
2 produto_info = {
3 " nome " : " Caneta Esferogr á fica Azul " ,
4 " preco_unitario " : 1.50
5 }
6 print ( f " Informa ç õ es iniciais do produto : { produto_info } " )
7
8 # Adicionando uma nova informa ç ã o : o c ó digo do produto
9 produto_info [ " codigo " ] = " CAN - AZ -001 "
10 print ( f " Ap ó s adicionar o c ó digo : { produto_info } " )
11

12 # Adicionando a quantidade em estoque


13 produto_info [ " estoque " ] = 250
14 print ( f " Ap ó s adicionar o estoque : { produto_info } " )

Código 8.11: Adicionando novos pares a um dicionário

Modificando o Valor de uma Chave Existente


Se você usar a mesma sintaxe de atribuição (nome_do_dicionario[chave] =
valor_atualizado), mas a chave já existir no dicionário, o Python não criará um novo
par. Em vez disso, ele atualizará o valor associado àquela chave_existente.
1 # Usando o dicion á rio ’ produto_info ’ do exemplo anterior
2 produto_info = {
3 " nome " : " Caneta Esferogr á fica Azul " ,
4 " preco_unitario " : 1.50 ,
5 " codigo " : " CAN - AZ -001 " ,
6 " estoque " : 250
7 } # Estado ap ó s o exemplo anterior
8
9 print ( f " \ nPre ç o unit á rio antigo : R$
{ produto_info [ ’ preco_unitario ’]:.2 f } " )
10 produto_info [ " preco_unitario " ] = 1.75 # Modificando o valor
11 print ( f " Pre ç o unit á rio novo : R$
{ produto_info [ ’ preco_unitario ’]:.2 f } " )
172 Capítulo 8. Dicionários (Dictionaries)

12
13 print ( f " Estoque antigo : { produto_info [ ’ estoque ’]} unidades " )
14 produto_info [ " estoque " ] -= 20 # Usando atribui ç ã o composta para
atualizar
15 print ( f " Estoque novo : { produto_info [ ’ estoque ’]} unidades " )
16

17 print ( f " \ nInforma ç õ es do produto atualizadas : { produto_info } " )

Código 8.12: Modificando valores em um dicionário

O Método update(): Adicionando ou Atualizando Múltiplos Pares


Para adicionar ou atualizar vários pares chave-valor em um dicionário de uma só vez, o
método update() é muito útil. Ele modifica o dicionário original “in-place”.
O método update() pode receber como argumento:
1. Outro dicionário.
2. Um iterável de pares chave-valor (como uma lista de tuplas).
3. Argumentos nomeados (keyword arguments).
1 # Dicion á rio inicial de configura ç õ es de um aplicativo
2 config_app = {
3 " idioma " : " portugu ê s " ,
4 " tema_visual " : " claro " ,
5 " n o t if i ca c o es _ at i va s " : True
6 }
7 print ( f " Configura ç õ es originais : { config_app } " )
8
9 # 1. Atualizando com outro dicion á rio
10 atualizacoes_parciais = {
11 " tema_visual " : " escuro " , # Chave existente , ser á atualizada
12 " tamanho_fonte " : 12 , # Nova chave , ser á adicionada
13 " backup _automat ico " : False # Nova chave , ser á adicionada
14 }
15 config_app . update ( a t u a l i z a c o e s _ p a r c i a i s )
16 print ( f " Ap ó s update com dicion á rio : { config_app } " )
17
18 # 2. Atualizando com uma lista de tuplas ( chave , valor )
19 m ais_atua lizacoes = [( " idioma " , " pt - br " ) ,
( " f r e q u e n c i a _ b a c k u p _ h o r a s " , 24) ]
20 config_app . update ( mais_at ualizaco es )
21 print ( f " Ap ó s update com lista de tuplas : { config_app } " )
22
23 # 3. Atualizando com argumentos nomeados ( keywords )
24 config_app . update ( usuario_logado = " Alan " , m o d o _ e c o n o m i a _ e n e r g i a = True )
25 print ( f " Ap ó s update com keywords : { config_app } " )

Código 8.13: Usando o método update() para adicionar/atualizar itens


Em todos os casos com update(): se uma chave do argumento já existe no dicionário
que está sendo atualizado, seu valor é substituído; se não existe, o novo par chave-valor é
adicionado.
A capacidade de adicionar e modificar pares chave-valor dinamicamente é o que torna
os dicionários estruturas de dados tão flexíveis e poderosas para representar informações
que podem mudar ou crescer ao longo do tempo.
8.5. Removendo Pares Chave-Valor de um Dicionário 173

8.5 Removendo Pares Chave-Valor de um Dicionário


Assim como precisamos adicionar e atualizar informações em nossos dicionários, também
é comum a necessidade de remover pares chave-valor que não são mais relevantes ou
necessários. Python oferece algumas maneiras distintas de realizar essa tarefa, cada uma
adequada a diferentes situações.
Lembre-se que os dicionários são mutáveis, então essas operações de remoção modificarão
o dicionário original.

Removendo com a Instrução del


A instrução del (abreviação de “delete”) é uma forma geral em Python para remover
objetos, e pode ser usada para remover um par chave-valor específico de um dicionário,
dada a sua chave.
Sintaxe: del nome_do_dicionario[chave_a_remover]
• Se a chave_a_remover existir no dicionário, o par chave-valor correspondente é
removido.

• Se a chave_a_remover não existir, o Python levantará um erro KeyError.

1 # Dicion á rio de estoque de produtos


2 estoque_frutas = {
3 " ma ç ã " : 50 ,
4 " banana " : 75 ,
5 " laranja " : 30 ,
6 " uva " : 60
7 }
8 print ( f " Estoque inicial de frutas : { estoque_frutas } " )
9
10 # Removendo o item " banana " do estoque
11 if " banana " in estoque_frutas : # Boa pr á tica verificar se a chave
existe
12 del estoque_frutas [ " banana " ]
13 print ( f " Estoque ap ó s remover ’ banana ’: { estoque_frutas } " )
14 else :
15 print ( " ’ banana ’ n ã o encontrada no estoque . " )
16
17 # Removendo " uva " ( assumindo que existe )
18 del estoque_frutas [ " uva " ]
19 print ( f " Estoque ap ó s remover ’ uva ’: { estoque_frutas } " )
20
21 # Tentativa de remover uma chave inexistente ( GERARIA KeyError )
22 # del estoque_frutas [" manga "]

Código 8.14: Removendo itens de um dicionário com del

Removendo com o Método pop(chave, valor_padrao_opcional)


O método pop() não apenas remove o par chave-valor, mas também retorna o valor
que foi removido.
Sintaxe: valor_removido = nome_do_dicionario.pop(chave_a_remover, valor_padrao_se_nao
• Remove o par associado à chave_a_remover e retorna seu valor.
174 Capítulo 8. Dicionários (Dictionaries)

• Se a chave não existir e o segundo argumento (valor padrão) não for fornecido,
levanta KeyError.

• Se a chave não existir e o valor padrão for fornecido, retorna o valor padrão.

1 # Dicion á rio de configura ç õ es de um usu á rio


2 configuracoes_perfil = {
3 " tema " : " escuro " ,
4 " idioma " : " pt - BR " ,
5 " n oti fi ca coe s_ em ail " : True ,
6 " newsletter " : False
7 }
8 print ( f " Configura ç õ es iniciais : { c o n fi g u r a c o e s _ p e r f i l } " )
9
10 # Removendo a configura ç ã o de idioma e guardando o valor removido
11 id io ma_ co nf igu ra do = c o n f i g u r a c o e s _ p er f i l . pop ( " idioma " )
12 print ( f " O idioma configurado era : ’{ id iom a_ co nfi gu ra do } ’ " )
13 print ( f " Configura ç õ es restantes : { c on f i g u r a c o e s _ p e r f i l } " )
14

15 # Removendo a configura ç ã o de newsletter


16 valor_newsletter = c o n f i g u r a c o e s _ p e r fi l . pop ( " newsletter " )
17 print ( f " Configura ç ã o de newsletter removida ( era
{ valor_newsletter }) . " )
18
19 # Tentando remover uma chave inexistente , mas fornecendo um valor
padr ã o
20 f u s o _ h o r a r i o _ r e m o v i d o = c o n f i g u r a c o e s_ p e r f i l . pop ( " fuso_horario " ,
None )
21 if f u s o _ h o r a r i o _ r e m o v i d o is None :
22 print ( " Configura ç ã o ’ fuso_horario ’ n ã o encontrada para
remo ç ã o . " )
23 print ( f " Configura ç õ es ap ó s tentativas de pop :
{ configuracoes_perfil }")

Código 8.15: Usando pop() para remover e obter valor

Removendo um Item Arbitrário (ou o Último Inserido) com popitem()


O método popitem() remove e retorna um par (chave, valor) do dicionário.

• Antes do Python 3.7: O par removido era arbitrário.

• A partir do Python 3.7: Remove e retorna o último par inserido (comporta-


mento LIFO).

• Levanta KeyError se o dicionário estiver vazio.

1 # Dicion á rio de tarefas pendentes ( a ordem de inser ç ã o é mantida em


Python 3.7+)
2 tarefas = {}
3 tarefas [ " primeira " ] = " Lavar a lou ç a "
4 tarefas [ " segunda " ] = " Estudar Python "
5 tarefas [ " terceira " ] = " Fazer compras "
6 print ( f " Tarefas pendentes iniciais : { tarefas } " )
7
8 if tarefas : # Verifica se o dicion á rio n ã o est á vazio
8.6. Iterando sobre Dicionários: Percorrendo os Pares Chave-Valor 175

9 chave_tarefa , descricao_tarefa = tarefas . popitem ()


10 print ( f " Ú ltima tarefa removida : Chave = ’{ chave_tarefa } ’ ,
Descri ç ã o = ’{ descricao_tarefa } ’ " )
11 print ( f " Tarefas restantes : { tarefas } " )
12 else :
13 print ( " Nenhuma tarefa para remover com popitem () . " )
14
15 # Removendo a pr ó xima ( que agora é a ú ltima )
16 if tarefas :
17 chave_tarefa , descricao_tarefa = tarefas . popitem ()
18 print ( f " Processando e removendo a tarefa ’{ descricao_tarefa } ’
( chave : { chave_tarefa }) . " )
19 print ( f " Tarefas restantes : { tarefas } " )

Código 8.16: Usando popitem() para remover o último item inserido

Limpando Todos os Itens com clear()


Se você quer remover todos os pares chave-valor de um dicionário, esvaziando-o com-
pletamente, pode usar o método clear(). Sintaxe: nome_do_dicionario.clear()
1 # Dicion á rio com dados tempor á rios
2 cache_dados = {
3 " usuario_123 " : { " nome " : " Ana " , " ultimo_acesso " : " 2025 -05 -23 " } ,
4 " produto_xyz " : { " preco " : 50.00 , " estoque " : 10}
5 }
6 print ( f " Cache antes da limpeza : { cache_dados } " )
7
8 cache_dados . clear () # Remove todos os itens do dicion á rio
9 print ( f " Cache ap ó s a limpeza : { cache_dados } " ) # Sa í da : {}

Código 8.17: Usando clear() para esvaziar um dicionário

A escolha do método de remoção dependerá da sua necessidade específica: del para


remoção simples por chave, pop() para remover por chave e obter o valor, popitem() para
o padrão LIFO, e clear() para esvaziar completamente o dicionário.

8.6 Iterando sobre Dicionários: Percorrendo os Pares


Chave-Valor
Assim como fazemos com listas, tuplas e strings, frequentemente precisamos “visitar”
ou processar cada um dos itens armazenados em um dicionário. Esse processo de percorrer
os elementos de uma coleção é chamado de iteração. Em Python, a ferramenta principal
para isso é o laço for.
No entanto, como os dicionários são coleções de pares chave-valor, temos algumas
maneiras diferentes de iterar sobre eles, dependendo se estamos interessados apenas nas
chaves, apenas nos valores, ou em ambos.

Iterando Diretamente sobre um Dicionário (Itera sobre as Chaves)


Se você usar um laço for diretamente com um dicionário, a variável de iteração do
laço receberá as chaves do dicionário, uma por vez.
176 Capítulo 8. Dicionários (Dictionaries)

Ordem de Iteração: É importante notar que, a partir do Python 3.7, os dicionários


mantêm a ordem em que os itens foram inseridos. Isso significa que, ao iterar, você
os obterá na mesma sequência em que foram adicionados.
1 # Dicion á rio de exemplo : Aluno e sua nota final
2 notas_finais_por_aluno = {
3 " Alice Garcia " : 8.7 ,
4 " Bruno Lima " : 9.1 ,
5 " Carlos Anjos " : 7.5 ,
6 " Daniela Matos " : 6.9
7 }
8

9 print ( " Boletim dos Alunos ( iterando sobre chaves ) : " )


10 for nome_do_aluno in n o t a s _ f i n a i s _ p o r _ a l u n o :
11 # ’ nome_do_aluno ’ recebe cada chave ( o nome do aluno )
12 nota_do_aluno = n o t a s _ f i n a i s _ p o r _ a l u n o [ nome_do_aluno ] #
Acessamos o valor
13 print ( f " - Aluno ( a ) : { nome_do_aluno } , Nota : { nota_do_aluno } " )

Código 8.18: Iterando sobre as chaves de um dicionário

Iterando sobre os Valores com o Método values()


Se você está interessado apenas nos valores e não precisa das chaves, pode usar o
método meu_dicionario.values(). Ele retorna um objeto de “visão” contendo todos os
valores.
1 # Usando o mesmo dicion á rio ’ n o t a s _ f i n a i s _ p o r _ a l u n o ’
2 notas_finais_por_aluno = {
3 " Alice Garcia " : 8.7 , " Bruno Lima " : 9.1 ,
4 " Carlos Anjos " : 7.5 , " Daniela Matos " : 6.9
5 }
6 print ( " \ nApenas as notas obtidas pelos alunos : " )
7 s o m a _ t o t a l _ d a s _ n o t a s = 0.0
8 numero_de_alunos = 0
9
10 for nota_individual in n o t a s _ f i n a i s _ p o r _ a l u n o . values () :
11 print ( f " - Nota : { nota_individual } " )
12 so m a _ to t a l _ d a s _ n o t a s += nota_individual
13 numero_de_alunos += 1
14
15 if numero_de_alunos > 0:
16 me d i a _g e r a l _ d a _ t u r m a = so m a _ t o t a l _ d a s _ n o t a s / numero_de_alunos
17 print ( f " M é dia geral da turma : { me d i a _ g e r a l _ d a _ t u r m a :.2 f } " )

Código 8.19: Iterando sobre os valores de um dicionário com values()

Iterando sobre Pares Chave-Valor com o Método items() (Mais


Comum)
Frequentemente, você precisará tanto da chave quanto do valor. O método
meu_dicionario.items() retorna um objeto de “visão” que se comporta como uma
sequência de tuplas, onde cada tupla é um par (chave, valor). Você pode usar o
desempacotamento de tuplas no laço for:
8.6. Iterando sobre Dicionários: Percorrendo os Pares Chave-Valor 177

for variavel_chave, variavel_valor in meu_dicionario.items():


# ...

1 # Usando o dicion á rio ’ n o t a s _ f i n a i s _ p o r _ a l u n o ’


2 notas_finais_por_aluno = {
3 " Alice Garcia " : 8.7 , " Bruno Lima " : 9.1 ,
4 " Carlos Anjos " : 7.5 , " Daniela Matos " : 6.9
5 }
6 print ( " \ nRelat ó rio Detalhado dos Alunos : " )
7 for nome_aluno_item , nota_aluno_item in
n o t a s _ f i n a i s _ p o r _ a l u n o . items () :
8 status_aluno = " Aprovado ( a ) " if nota_aluno_item >= 7.0 else
" Reprovado ( a ) "
9 print ( f " Aluno ( a ) : { nome_aluno_item : <20} | Nota :
{ nota_aluno_item : >4.1 f } | Status : { status_aluno } " )

Código 8.20: Iterando sobre chaves e valores com items()

Cuidado ao Modificar um Dicionário Durante a Iteração


Você não deve adicionar ou remover chaves de um dicionário enquanto está
iterando diretamente sobre ele. Fazer isso pode levar a comportamentos imprevisíveis
ou a um erro RuntimeError.
A abordagem segura é:

1. Coletar as chaves a serem modificadas/removidas em uma lista separada durante a


primeira iteração.

2. Realizar as modificações em um segundo passo, fora do laço original, usando a lista


auxiliar.

1 # Queremos remover todos os alunos com nota menor que 7.0


2 no ta s_p ar a_ fil tr ar = {
3 " Alice " : 8.5 , " Bruno " : 6.0 , " Carlos " : 9.2 ,
4 " Daniela " : 5.5 , " Eduardo " : 7.0
5 }
6 print ( f " \ nNotas originais para filtrar : { not as _p ara _f il tra r } " )
7
8 # Passo 1: Coletar as chaves dos alunos a serem removidos
9 a l u n o s _ r e p r o v a d o s _ c h a v e s = []
10 for aluno , nota_al in n ot as _pa ra _f ilt ra r . items () :
11 if nota_al < 7.0:
12 a l u n o s _ r e p r o v a d o s _ c h a v e s . append ( aluno )
13
14 print ( f " Alunos a serem removidos ( reprovados ) :
{ alunos_reprovados_chaves }")
15
16 # Passo 2: Remover os alunos do dicion á rio original
17 for aluno_a_remover in a l u n o s _ r e p r o v a d o s _ c h a v e s :
18 del no ta s_ par a_ fi ltr ar [ aluno_a_remover ]
19
20 print ( f " Notas ap ó s remover os reprovados : { n ot as_ pa ra _f ilt ra r } " )

Código 8.21: Forma segura de remover itens de um dicionário durante “iteração”


178 Capítulo 8. Dicionários (Dictionaries)

Outra forma de criar um novo dicionário com base em um filtro é usar a compreensão
de dicionários, que veremos na Seção 8.8.
A iteração sobre dicionários é uma tarefa muito comum, e o Python oferece maneiras
claras e eficientes de acessar suas chaves, valores ou ambos.

8.7 Métodos Úteis de Dicionários (e Funções Relacio-


nadas)
Além das operações básicas de criação, acesso, modificação, remoção e iteração que já
exploramos, os dicionários Python possuem outros métodos e algumas funções embutidas
úteis que facilitam diversas tarefas comuns, tornando o trabalho com mapeamentos chave-
valor ainda mais eficiente e expressivo.

Revisitando len(dicionario): Obtendo o Número de Pares Chave-


Valor
Assim como para listas e tuplas, a função embutida len() pode ser usada com
dicionários. Quando aplicada a um dicionário, len() retorna o número de pares chave-
valor que ele contém.
1 # Usando len () com dicion á rios
2 estoque_livraria = {
3 " Python Essencial " : 50 ,
4 " Algoritmos Descomplicados " : 35 ,
5 " Introdu ç ã o à IA " : 20
6 }
7 numero_de _titulos = len ( estoque_livraria )
8 print ( f " A livraria tem { nume ro_de_ti tulos } t í tulos diferentes em
estoque . " )
9
10 d ic i ona r io _ v az i o_ e x = {}
11 print ( f " Um dicion á rio vazio tem { len ( d ic i o na r io _ va z i o_ e x ) } itens . " )

Código 8.22: Usando len() com dicionários

Obtendo “Visões” (Views) das Chaves, Valores e Itens


Os métodos keys(), values(), e items() não retornam listas diretamente (em Python
3), mas sim objetos especiais chamados objetos de visão (view objects).

• Dinâmicos: As visões refletem quaisquer mudanças futuras feitas no dicionário.

• Iteráveis: Podem ser usados diretamente em laços for.

• Suportam o operador in de forma eficiente.

• Podem ser convertidos para listas usando list().

• meu_dicionario.keys()
Retorna uma visão contendo todas as chaves.
8.7. Métodos Úteis de Dicionários (e Funções Relacionadas) 179

1 contatos_agenda = { " Ana " : " 9111 -2222 " , " Bruno " :
" 9333 -4444 " , " Carla " : " 9555 -6666 " }
2

3 ch a ve s _d o s _c o nt a t os = contatos_agenda . keys ()
4 print ( f " Vis ã o das chaves : { c ha v e s_ d os _ co n t at o s } " )
5
6 print ( " Nomes na agenda : " )
7 for nome_contato in c h av e s _d o s_ c on t a to s :
8 print ( f " - { nome_contato } " )
9
10 lista_de_nomes = list ( c h av e s _d o s_ c on t a to s )
11 print ( f " Chaves como lista : { lista_de_nomes } " )

Código 8.23: Usando o método keys()

• meu_dicionario.values()
Retorna uma visão contendo todos os valores.
1 # Usando o dicion á rio ’ contatos_agenda ’ do exemplo anterior
2 contatos_agenda = { " Ana " : " 9111 -2222 " , " Bruno " :
" 9333 -4444 " , " Carla " : " 9555 -6666 " }
3 todos_os_valores = contatos_agenda . values ()
4 print ( f " \ nVis ã o dos valores ( telefones ) :
{ todos_os_valores } " )
5
6 print ( " Telefones cadastrados : " )
7 for telefone_contato in todos_os_valores :
8 print ( f " - { telefone_contato } " )

Código 8.24: Usando o método values()

• meu_dicionario.items()
Retorna uma visão contendo tuplas (chave, valor).
1 # Usando o dicion á rio ’ contatos_agenda ’
2 contatos_agenda = { " Ana " : " 9111 -2222 " , " Bruno " :
" 9333 -4444 " , " Carla " : " 9555 -6666 " }
3 todos_os_itens = contatos_agenda . items ()
4 print ( f " \ nVis ã o dos itens ( pares chave - valor ) :
{ todos_os_itens } " )
5
6 print ( " Detalhes dos contatos : " )
7 for nome_item , telefone_item in todos_os_itens :
8 print ( f " Contato : { nome_item } - Telefone :
{ telefone_item } " )

Código 8.25: Usando o método items()

Verificando a Existência de uma Chave com in (Revisão)


Relembrando, chave in meu_dicionario é a forma idiomática e eficiente de verificar
se uma chave existe.
1 estoque_loja = { " caderno " : 100 , " caneta " : 250 , " l á pis " : 180}
2
3 item_busca1 = " caneta "
180 Capítulo 8. Dicionários (Dictionaries)

4 if item_busca1 in estoque_loja :
5 print ( f " Sim , temos ’{ item_busca1 } ’ em estoque
({ estoque_loja [ item_busca1 ]} unidades ) . " )
6
7 item_busca2 = " r é gua "
8 if item_busca2 not in estoque_loja :
9 print ( f " O item ’{ item_busca2 } ’ n ã o foi encontrado no invent á rio . " )

Código 8.26: Verificando se uma chave existe com in

Copiando Dicionários com copy()


A atribuição novo_dict = dict_original apenas cria uma nova referência. Para
uma cópia (rasa), use o método copy(). Uma cópia rasa (shallow copy) cria um novo
dicionário, mas se os valores forem objetos mutáveis (como listas), as referências a esses
objetos internos são copiadas.
1 config_original_servidor = {
2 " host " : " localhost " ,
3 " porta " : 8080 ,
4 " servicos_ativos " : [ " http " , " ftp " ]
5 }
6 config_co pia_rasa = c o n f i g _ o r i g i n a l _ s e r v i d o r . copy ()
7
8 print ( f " Original : { c o n f i g _ o r i g i n a l _ s e r v i d o r } , ID :
{ id ( c o n f i g _ o r i g i n a l _ s e r v i d o r ) } " )
9 print ( f " C ó pia Rasa : { co nfig_cop ia_rasa } , ID :
{ id ( config_co pia_rasa ) } " )
10
11 c onfig_co pia_rasa [ " porta " ] = 9000
12 c onfig_co pia_rasa [ " servicos_ativos " ]. append ( " ssh " ) # Modifica a
lista interna !
13
14 print ( f " \ nAp ó s modificar a c ó pia : " )
15 print ( f " Original : { c o n f i g _ o r i g i n a l _ s e r v i d o r } " )
16 # A lista interna ’ servicos_ativos ’ FOI alterada no original !
17 print ( f " C ó pia Rasa : { co nfig_cop ia_rasa } " )

Código 8.27: Copiando dicionários e o efeito da cópia rasa


Para cópias totalmente independentes de todos os níveis (cópia profunda), usa-se
[Link]() do módulo copy.

Criando Dicionários a Partir de Chaves com [Link]()


Este é um método de classe ([Link](sequencia, valor_opcional)) para
criar um novo dicionário com chaves de uma sequencia e todas com o mesmo
valor_opcional (padrão é None).
1 campos_fo rmulario = [ " nome " , " email " , " telefone " , " cidade " ]
2
3 # Criando um dicion á rio com esses campos e valor padr ã o None
4 f i c h a _ c a d a s t r o _ v a z i a = dict . fromkeys ( campos_ formulari o )
5 print ( f " Ficha de cadastro vazia : { f i c h a _ c a d a s t r o _ v a z i a } " )
6
7 # Criando um dicion á rio com um valor inicial padr ã o para todas as
chaves
8.8. Compreensão de Dicionários (Dictionary Comprehensions) 181

8 p o n t u a c a o _ i n i c i a l _ j o g a d o r e s = dict . fromkeys ([ " JogadorA " ,


" JogadorB " , " JogadorC " ] , 0)
9 print ( f " Pontua ç ã o inicial dos participantes :
{ pontuacao_inicial_jogadores }")

Código 8.28: Usando [Link]() para criar dicionários

Esses métodos e funções adicionais tornam o trabalho com dicionários em Python ainda
mais versátil, permitindo que você os inspecione, copie e crie de maneiras convenientes e
eficientes.

8.8 Compreensão de Dicionários (Dictionary Comprehen-


sions)
Se você gostou das compreensões de listas (list comprehensions) que vimos no Capítulo
6.7 pela sua capacidade de criar listas de forma concisa e expressiva, vai gostar de saber
que Python oferece uma sintaxe muito similar para criar dicionários: as compreensões
de dicionários (dictionary comprehensions).
Assim como suas primas para listas, as compreensões de dicionários nos permitem
construir novos dicionários a partir de outros iteráveis (como listas, tuplas, strings, ou até
mesmo outros dicionários), aplicando uma expressão para gerar cada par chave:valor e,
opcionalmente, filtrando quais itens serão incluídos. Tudo isso, frequentemente, em uma
única linha de código legível.

A Sintaxe da Compreensão de Dicionários


Uma compreensão de dicionários é escrita dentro de chaves {}, assim como a definição
literal de um dicionário, mas com uma estrutura que lembra um laço for.
A forma mais básica é:
novo_dicionario =
{expressao_chave: expressao_valor for item in iteravel}

E uma forma mais completa, que inclui uma condição para filtrar itens, é:
novo_dicionario =
{expressao_chave: expressao_valor for item in iteravel if condicao}

Vamos destrinchar cada componente:

1. expressao_chave: expressao_valor: Este é o par que será adicionado ao novo


dicionário para cada item processado (que satisfaça a condicao, se houver).

2. for item in iteravel: Esta parte é muito similar a um laço for normal. iteravel
é qualquer objeto Python que pode ser percorrido, e item é a variável que assume o
valor de cada elemento do iteravel.

3. if condicao (Opcional): Uma cláusula de filtro. Somente seacondicao for True


é que o par expressao_chave: expressao_valor será incluído.
182 Capítulo 8. Dicionários (Dictionaries)

Comparando com Laços for Tradicionais


• Exemplo: Dicionário de números e seus quadrados (de 0 a 4)

– Com laço for tradicional:


1 q ua d ra d o s_ d ic t _ lo o p = {}
2 for x in range (5) :
3 q u ad r a do s _d i c t_ l oo p [ x ] = x **2
4 print ( f " Quadrados ( loop tradicional ) :
{ q u ad r ad o s _d i ct _ l oo p } " )

Código 8.29: Dicionário de quadrados com laço for

– Com compreensão de dicionário:


1 q ua d ra d o s_ d ic t _ co m p = { x : x **2 for x in range (5) }
2 print ( f " Quadrados ( compreens ã o ) :
{ q u ad r ad o s _d i ct _ c om p } " )

Código 8.30: Dicionário de quadrados com compreensão

• Exemplo: Criar um dicionário de alunos adultos

– Com laço for tradicional:


1 l is t a_ a l un o s_ i d ad e s = [( " Ana " , 30) , ( " Bruno " , 22) ,
( " Carla " , 35) , ( " Daniel " , 19) ]
2 d i c i o n a r i o _ a d u l t o s _ l o o p = {}
3 for nome_aluno , idade_aluno in l i st a _a l un o s _i d ad e s :
4 if idade_aluno >= 18:
5 d i c i o n a r i o _ a d u l t o s _ l o o p [ nome_aluno ] =
idade_aluno
6 print ( f " Adultos ( loop tradicional ) :
{ dicionario_adultos_loop }")

Código 8.31: Filtrando adultos com laço for

– Com compreensão de dicionário:


1 l is t a_ a l un o s_ i d ad e s = [( " Ana " , 30) , ( " Bruno " , 22) ,
( " Carla " , 35) , ( " Daniel " , 19) ]
2 d i c i o n a r i o _ a d u l t o s _ c o m p = { nome : idade for nome ,
idade in li s ta _ a lu n os _ i da d es if idade >= 18}
3 print ( f " Adultos ( compreens ã o ) :
{ dicionario_adultos_comp }")

Código 8.32: Filtrando adultos com compreensão de dicionário

Vantagens das Compreensões de Dicionários


1. Concisão: Geralmente resultam em um código mais curto.

2. Legibilidade: Para muitos casos, podem ser mais fáceis de ler e entender.

3. Expressividade: São uma forma muito “Pythônica” de criar dicionários.


8.8. Compreensão de Dicionários (Dictionary Comprehensions) 183

Mais Exemplos Práticos


• Exemplo 1: Criar um dicionário a partir de duas listas usando zip()
1 chaves = [ " a " , " b " , " c " ]
2 valores = [1 , 2 , 3]
3 d i c i o n ar i o _ c o m b i n a d o = { k : v for k , v in zip ( chaves ,
valores ) }
4 print ( d i c i o n a r i o _ c o m b i n a d o ) # Sa í da : { ’ a ’: 1 , ’b ’: 2 , ’c ’:
3}

Código 8.33: Dicionário a partir de duas listas com zip e compreensão

• Exemplo 2: Invertendo um dicionário (trocando chaves por valores)


(Cuidado: Só funciona bem se os valores originais forem únicos e imutáveis).
1 c o d i g o _ p a r a _ n o m e _ p r o d u t o = { " P01 " : " Caneta " , " P02 " :
" L á pis " , " P03 " : " Borracha " }
2
3 n o m e _ p a r a _ c o d i g o _ p r o d u t o = { valor_nome : chave_codigo
4 for chave_codigo , valor_nome in
c o d i g o _ p a r a _ n o m e _ p r o d u t o . items () }
5 print ( f " Dicion á rio original : { c o d i g o _ p a r a _ n o m e _ p r o d u t o } " )
6 print ( f " Dicion á rio invertido : { n o m e _ p a r a _ c o d i g o _ p r o d u t o } " )

Código 8.34: Invertendo um dicionário com compreensão

• Exemplo 3: Dicionário com filtro e transformação de valor


1 precos_originais = { " p ã o " : 5.0 , " leite " : 4.0 , " caf é " :
15.0 , " manteiga " : 8.0}
2 # Aumentar o pre ç o em 10% apenas dos itens que custam mais
de R$4 .50
3 precos_ajustados = {
4 item : round ( preco * 1.10 , 2) # Arredondando para 2
casas decimais
5 for item , preco in precos_originais . items ()
6 if preco > 4.50
7 }
8 print ( f " Pre ç os originais : { precos_originais } " )
9 print ( f " Pre ç os ajustados para itens caros :
{ precos_ajustados } " )

Código 8.35: Dicionário com filtro e transformação usando compreensão

Quando Usar (e Quando Talvez Evitar)


• Use compreensões de dicionários para:

– Criar dicionários de forma concisa a partir de outros iteráveis.


– Aplicar transformações às futuras chaves e/ou valores.
– Filtrar quais pares chave-valor devem ser incluídos.

• Evite (ou use com cautela) se:


184 Capítulo 8. Dicionários (Dictionaries)

– A lógica para gerar a expressao_chave, expressao_valor ou a condicao se


tornar muito complexa e difícil de ler em uma única linha. Nesses casos, um
laço for tradicional pode ser mais claro.

As compreensões de dicionários são mais uma ferramenta elegante do Python que, quando
bem utilizada, pode tornar seu código mais limpo, expressivo e eficiente para a tarefa de
construir dicionários dinamicamente.

8.9 Dicionários Aninhados (Dicionários dentro de Dici-


onários)
Assim como vimos que as listas podem conter outras listas (listas aninhadas), os
dicionários em Python também possuem essa capacidade de aninhamento. Um dicionário
aninhado é simplesmente um dicionário onde um ou mais de seus valores são, eles mesmos,
outros dicionários.
Essa característica nos permite criar estruturas de dados hierárquicas e mais complexas,
que são extremamente úteis para representar informações do mundo real que naturalmente
possuem múltiplos níveis de detalhe ou categorização. Pense, por exemplo, em um cadastro
de clientes onde cada cliente (a chave principal) tem um conjunto de informações associadas
(como nome, endereço, telefone), e o endereço em si pode ser outro dicionário com rua,
número, cidade, CEP. Estruturas de dados como JSON (JavaScript Object Notation),
muito comuns na web e em APIs, são frequentemente mapeadas para dicionários aninhados
em Python.

Criando Dicionários Aninhados


A sintaxe para criar dicionários aninhados é uma extensão natural da criação de
dicionários simples. Você define o dicionário externo e, para as chaves cujos valores
serão outros dicionários, você simplesmente define esses dicionários internos como faria
normalmente.

• Exemplo 1: Cadastro de Alunos


1 # Dicion á rio aninhado para armazenar dados de alunos
2 c ad ast ro _d e_a lu no s = {
3 " mat2024001 " : { # Chave do dicion á rio externo
4 " nome " : " Ana Clara Sousa " ,
5 " curso " : " Engenharia de Produ ç ã o " ,
6 " periodo " : 3 ,
7 " email " : " ana . sousa@email . com "
8 },
9 " mat2023015 " : {
10 " nome " : " Ricardo Oliveira " ,
11 " curso " : " Sistemas de Informa ç ã o " ,
12 " periodo " : 5 ,
13 " email " : " ricardo . oli@email . com " ,
14 " monitor_de " : " Algoritmos I "
15 },
16 " mat2025007 " : {
17 " nome " : " Sofia Pereira " ,
18 " curso " : " Engenharia El é trica " ,
8.9. Dicionários Aninhados (Dicionários dentro de Dicionários) 185

19 " periodo " : 1


20 }
21 }
22 # print ( ca das tr o_ de_ al un os )

Código 8.36: Exemplo de dicionário aninhado para cadastro de alunos

• Exemplo 2: Cardápio de uma Lanchonete


1 cardapio_da_lanchonete = {
2 " Lanches " : {
3 "X - Burger Simples " : 18.00 ,
4 "X - Salada Especial " : 22.50 ,
5 " Misto Quente na Chapa " : 10.00
6 },
7 " Bebidas " : {
8 " Refrigerante ( lata ) " : 6.00 ,
9 " Suco de Laranja Natural (300 ml ) " : 8.00 ,
10 " Á gua Mineral ( sem g á s ) " : 3.50
11 },
12 " Sobremesas " : {
13 " Pudim de Leite " : 7.00 ,
14 " Mousse de Chocolate " : 9.50
15 }
16 }
17 # print ( c a r d a p i o _ d a _ l a n c h o n e t e )

Código 8.37: Exemplo de dicionário aninhado para cardápio

A formatação com indentação é crucial para a legibilidade de dicionários aninhados.

Acessando Elementos em Dicionários Aninhados


Para acessar um valor em um dicionário aninhado, você usará uma sequência de chaves,
cada uma entre colchetes [], para “navegar” através dos níveis:
dict_principal[chave_externa][chave_interna].
1 # Usando ’ c ada st ro _de _a lu nos ’ do exemplo anterior
2 ca da str o_ de _al un os = {
3 " mat2024001 " : { " nome " : " Ana Clara Sousa " , " curso " : " Engenharia
de Produ ç ã o " , " periodo " : 3} ,
4 " mat2023015 " : { " nome " : " Ricardo Oliveira " , " curso " : " Sistemas
de Informa ç ã o " , " periodo " : 5 , " monitor_de " : " Algoritmos I " } ,
5 " mat2025007 " : { " nome " : " Sofia Pereira " , " curso " : " Engenharia
El é trica " , " periodo " : 1}
6 }
7

8 # Acessando o nome da Ana ( mat2024001 )


9 nome_da_ana = ca da st ro_ de _a lun os [ " mat2024001 " ][ " nome " ]
10 print ( f " Nome da aluna mat2024001 : { nome_da_ana } " )
11
12 # Acessando o curso do Ricardo ( mat2023015 )
13 curso_do_ricardo = ca das tr o_ de_ al un os [ " mat2023015 " ][ " curso " ]
14 print ( f " Curso do Ricardo : { curso_do_ricardo } " )
15
16 # Usando get () para acesso seguro ( email da Sofia pode n ã o existir )
186 Capítulo 8. Dicionários (Dictionaries)

17 email_da_sofia = ca da str o_ de _al un os . get ( " mat2025007 " ,


{}) . get ( " email " , " Email n ã o cadastrado " )
18 print ( f " Email da Sofia : { email_da_sofia } " )

Código 8.38: Acessando dados em dicionários aninhados

Modificando Dicionários Aninhados


Como os dicionários são mutáveis, você pode alterar os valores nos dicionários internos
ou adicionar novos pares.

• Modificar valor: dict_principal[chave_ext][chave_int] = novo_valor

• Adicionar par a dicionário interno: dict_principal[chave_ext][nova_chave_int]


= valor

• Adicionar novo dicionário interno: dict_principal[nova_chave_ext] = {...}

1 # Usando ’ c ada st ro _de _a lu nos ’


2 ca da str o_ de _al un os = {
3 " mat2024001 " : { " nome " : " Ana Clara Sousa " , " curso " : " Engenharia
de Produ ç ã o " , " periodo " : 3} ,
4 " mat2023015 " : { " nome " : " Ricardo Oliveira " , " curso " : " Sistemas
de Informa ç ã o " , " periodo " : 5}
5 }
6
7 # Modificar o per í odo da Ana
8 ca da str o_ de _al un os [ " mat2024001 " ][ " periodo " ] = 4
9 print ( f " Novo per í odo da Ana :
{ ca da st ro_ de _a lun os [ ’ mat2024001 ’][ ’ periodo ’]} " )
10
11 # Adicionar email para o Ricardo
12 ca da str o_ de _al un os [ " mat2023015 " ][ " email " ] = " ricardo . oli@email . com "
13 print ( f " Dados atualizados do Ricardo :
{ ca da st ro_ de _a lun os [ ’ mat2023015 ’]} " )
14
15 # Adicionar um novo aluno
16 ca da str o_ de _al un os [ " mat2024030 " ] = {
17 " nome " : " Lucas Mendes " , " curso " : " Engenharia Mecatr ô nica " ,
18 " periodo " : 2 , " email " : " lucas . mendes@email . com "
19 }
20 print ( f " Novo aluno Pedro : { c ad ast ro _d e_a lu no s [ ’ mat2024030 ’]} " )

Código 8.39: Modificando dicionários aninhados

Iterando sobre Dicionários Aninhados


Para percorrer todos os elementos, use laços for aninhados. O laço externo itera sobre
o dicionário principal, e o interno sobre cada dicionário interno.
1 # Usando ’ c ada st ro _de _a lu nos ’ dos exemplos anteriores ( j á com Lucas )
2 ca da str o_ de _al un os = {
3 " mat2024001 " : { " nome " : " Ana Clara Sousa " , " curso " : " Engenharia
de Produ ç ã o " , " periodo " : 4} ,
8.10. Quando Usar Dicionários? A Escolha Certa para Mapeamentos 187

4 " mat2023015 " : { " nome " : " Ricardo Oliveira " , " curso " : " Sistemas
de Informa ç ã o " , " periodo " : 5 , " email " :
" ricardo . oli@email . com " } ,
5 " mat2024030 " : { " nome " : " Lucas Mendes " , " curso " : " Engenharia
Mecatr ô nica " , " periodo " : 2 , " email " :
" lucas . mendes@email . com " }
6 }
7 print ( " \n - - - Listagem Completa de Alunos ---" )
8 for matricula , info_aluno in c ada st ro _de _a lu nos . items () :
9 print ( f " \ nMatr í cula : { matricula } " )
10 for chave_dado , valor_dado in info_aluno . items () :
11 print ( f " { str ( chave_dado ) . capitalize () }: { valor_dado } " )
12 print ( " --- Fim da Listagem ---" )

Código 8.40: Iterando sobre dicionários aninhados

Considerações sobre Dicionários Aninhados


• Profundidade do Aninhamento: Níveis muito profundos podem tornar o código
difícil de ler.

• Alternativas para Estruturas Muito Complexas: Para dados muito complexos,


o uso de classes (Programação Orientada a Objetos, Parte IV) pode oferecer uma
modelagem mais robusta.

Os dicionários aninhados são uma forma poderosa de criar estruturas de dados mais ricas
e representativas em Python, permitindo modelar relações hierárquicas de forma intuitiva.

8.10 Quando Usar Dicionários? A Escolha Certa para


Mapeamentos
Ao longo deste capítulo, exploramos em detalhes os dicionários em Python. Com
todo esse conhecimento, surge uma pergunta natural: “Quando, exatamente, devo usar
um dicionário em meus programas?” Esta seção visa responder a isso, resumindo as
características chave dos dicionários e destacando seus casos de uso ideais.

Características Chave dos Dicionários (Relembrando)


• Pares Chave-Valor: A informação é armazenada como um mapeamento.

• Chaves Únicas e Imutáveis: Garantem acesso direto.

• Valores Flexíveis: Podem ser de qualquer tipo.

• Mutáveis: Podem ser alterados após a criação.

• Acesso Principal por Chave.

• Ordem de Inserção Preservada (Python 3.7+).


188 Capítulo 8. Dicionários (Dictionaries)

Quando os Dicionários Brilham (Casos de Uso Ideais)


1. Mapeamentos e Associações Diretas:
Sempre que você tem um conjunto de dados onde cada item pode ser unicamente
identificado por uma “etiqueta” (a chave).
• Exemplos: Tradutor de palavras, catálogo de produtos, configurações de aplica-
tivo, propriedades de um objeto.
2. Buscas Rápidas por um Identificador Único (Chave):
Dicionários são muito mais eficientes do que percorrer uma lista para encontrar um
item por um identificador. A busca por chave é, em média, uma operação de tempo
constante.
3. Contagem de Frequência de Itens:
Excelentes para contar quantas vezes cada item aparece em uma coleção.
1 # Contando a frequ ê ncia de palavras em uma frase
2 frase = " o sol brilha e o c é u est á azul o sol é vida "
3 palav ras_na_f rase = frase . lower () . split ()
4

5 f r e q u e n c i a _ d e _ p a l a v r a s = {}
6 for palavra_item in palavra s_na_fra se :
7 f r e q u e n c i a _ d e _ p a l a v r a s [ palavra_item ] =
f r e q u e n c i a _ d e _ p a l a v r a s . get ( palavra_item , 0) + 1
8
9 print ( f " Frequ ê ncia das palavras : { f r e q u e n c i a _ d e _ p a l a v r a s } " )

Código 8.41: Contando frequência de palavras com dicionário

4. Representando Dados Estruturados e Registros:


Para um “registro” com vários campos nomeados, um dicionário é uma forma natural.
Dicionários aninhados são ótimos para estruturas hierárquicas (JSON-like).
• Exemplo: aluno = {"nome": "Ana", "matricula": "123"}
5. Agrupando Dados por Categoria:
Chaves podem representar categorias, e os valores podem ser listas ou outros dicio-
nários.
• Exemplo: produtos_por_categoria =
{"Eletrônicos": ["TV"], "Livros": ["Python"]}

Quando Outras Estruturas Podem Ser Mais Adequadas


• Listas: Quando a ordem é crucial, acesso por índice numérico, permite duplicatas
e precisa de mutabilidade frequente.
• Tuplas: Para coleções ordenadas e imutáveis, garantindo integridade dos dados
ou para uso como chaves de dicionário.
• Conjuntos (set - Capítulo 9): Para coleções de itens únicos onde a ordem não
importa, e para operações de conjunto ou verificações de pertinência rápidas.

A escolha da estrutura de dados certa é uma habilidade importante.


8.10. Quando Usar Dicionários? A Escolha Certa para Mapeamentos 189

Concluindo o Capítulo sobre Dicionários


Neste capítulo, mergulhamos no mundo dos dicionários, uma das estruturas de dados
mais versáteis e poderosas do Python. Vimos como eles funcionam com base em pares
chave-valor, o que permite um acesso rápido e intuitivo aos dados através de chaves únicas e
imutáveis. Aprendemos a criar, acessar, adicionar, modificar e remover itens de dicionários,
além de explorar diversas formas de iterar sobre eles e utilizar seus métodos mais comuns.
Também tocamos nas compreensões de dicionários para criação concisa e nos dicionários
aninhados para estruturas hierárquicas.
Os dicionários são fundamentais para representar dados do mundo real, para mapea-
mentos, para contagens e para qualquer situação onde você precise de uma busca eficiente
por um identificador. Eles são uma peça chave no quebra-cabeça das estruturas de dados
em Python.
No próximo capítulo, vamos conhecer os conjuntos (set), outra estrutura de coleção
importante, mas com um foco diferente: garantir a unicidade dos elementos e realizar
operações de conjunto de forma eficiente. Prepare-se para mais uma ferramenta valiosa!
Capítulo 9

Conjuntos (Sets)

Até agora em nossa jornada pela Parte II, exploramos as listas (coleções ordenadas e
mutáveis), as tuplas (coleções ordenadas e imutáveis) e os dicionários (coleções de pares
chave-valor). Cada uma dessas estruturas tem suas particularidades e casos de uso ideais.
Neste capítulo, vamos conhecer mais uma estrutura de dados embutida fundamental
do Python: o conjunto (ou set, em inglês). Os conjuntos em Python são inspirados
diretamente no conceito de conjuntos da matemática e oferecem uma maneira eficiente
de armazenar e manipular coleções de itens onde duas características são primordiais: os
itens são únicos (não há duplicatas) e a coleção não é ordenada (a ordem em que você
insere os itens não é necessariamente preservada ou relevante para a estrutura em si).

9.1 Introdução aos Conjuntos: Coleções Não Ordenadas


de Itens Únicos
Um conjunto (set) em Python é uma coleção de itens que se destaca por duas
propriedades principais:

1. Itens Únicos: Um conjunto não permite elementos duplicados. Se você tentar


adicionar um item que já existe no conjunto, ele simplesmente não será adicionado
novamente; o conjunto manterá apenas uma ocorrência daquele item. Isso torna os
conjuntos ideais para tarefas como remover duplicatas de outras coleções.

2. Não Ordenado: Diferentemente de listas e tuplas, os conjuntos não mantêm os itens


em uma ordem específica de inserção ou qualquer outra ordem garantida. Quando
você itera sobre um conjunto ou o imprime, a ordem dos elementos pode variar e
não deve ser considerada para a lógica do seu programa. O foco de um conjunto é
na presença ou ausência de um item, não na sua posição.

Analogias para Conjuntos


• Um grupo de amigos únicos para um evento: Se você está montando uma
lista de convidados e não quer que a mesma pessoa seja contada duas vezes.

• Uma coleção de etiquetas (tags) únicas: Para categorizar um artigo ou posta-


gem.

190
9.2. Criando Conjuntos: Definindo Coleções de Itens Únicos 191

• Os elementos distintos em um saco de bolinhas coloridas: Não importa a


ordem, apenas quais cores distintas estão presentes.

Outras Características Importantes dos Conjuntos


• Mutáveis: Os conjuntos padrão (set) em Python são mutáveis; você pode adicionar
ou remover itens deles.
• Itens Imutáveis: Os itens armazenados dentro de um conjunto devem ser de tipos
imutáveis (como números, strings, tuplas que contenham apenas imutáveis). Você
não pode adicionar uma lista a um conjunto.
• Eficiência em Operações de Pertinência: Verificar se um item está presente em
um conjunto (item in meu_conjunto) é uma operação muito rápida.
• Suporte a Operações Matemáticas de Conjuntos: Python oferece suporte
direto a operações como união, interseção, diferença, etc.

Quando os Conjuntos São Úteis?


Os conjuntos brilham em situações como:
• Eliminar duplicatas de uma coleção.
• Realizar testes de pertinência (verificar se um item pertence a um grupo) de forma
eficiente.
• Executar operações da teoria dos conjuntos (união, interseção, etc.).
Neste capítulo, aprenderemos como criar conjuntos, adicionar e remover elementos, realizar
as principais operações de conjunto e entender quando eles são a ferramenta certa para o
seu problema. Veremos também uma variação imutável do conjunto, chamada frozenset.

9.2 Criando Conjuntos: Definindo Coleções de Itens


Únicos
Agora que temos uma ideia do que são os conjuntos em Python – coleções não ordenadas
de itens únicos, onde cada item deve ser imutável – vamos explorar as maneiras de criá-los.
Python oferece duas formas principais: usando a sintaxe literal com chaves {} (com uma
ressalva importante para conjuntos vazios) ou usandoafunção construtora set().

A Sintaxe Literal com Chaves {} (para Conjuntos Não Vazios)


Para criar um conjunto já com alguns elementos, você pode listar os itens separados
por vírgulas, dentro de um par de chaves {}, muito similar à forma como definimos os
elementos de um dicionário, mas sem os pares chave:valor.
Sintaxe: meu_conjunto = {item1, item2, item3, ...}

• Duplicatas são Automaticamente Ignoradas: Se você tentar criar um conjunto


listando o mesmo item várias vezes, o conjunto resultante conterá apenas uma única
ocorrência daquele item.
192 Capítulo 9. Conjuntos (Sets)

1 # Tentando criar um conjunto com duplicatas na defini ç ã o


2 n u m e r o s _ c o m _ r e p e t i c a o = {1 , 2 , 2 , 3 , 4 , 3 , 5 , 1 , 1}
3 print ( f " Conjunto criado a partir de literais com
duplicatas : { n u m e r o s _ c o m _ r e p e t i c a o } " )
4 # Sa í da : {1 , 2 , 3 , 4 , 5} ( a ordem da sa í da pode variar )

Código 9.1: Criação de conjunto com literais (duplicatas ignoradas)

• Itens Devem Ser Imutáveis: Lembre-se que os elementos de um conjunto devem


ser de tipos imutáveis.
1 # Conjunto com diferentes tipos de dados imut á veis
2 c o n j u n t o _ e l e m e n t o s _ m i s t o s = {10 , " Python " , 3.14159 , (2025 ,
" ano " ) } # Tupla é imut á vel
3 print ( f " Conjunto com elementos mistos :
{ conjunto_elementos_mistos }")
4
5 # Tentativa de adicionar um item mut á vel ( uma lista )
GERARIA ERRO
6 # conjunt o_com_er ro = {1 , 2 , [" a " , " b "]}
7 # A linha acima causaria : TypeError : unhashable type :
’ list ’

Código 9.2: Conjunto com diferentes tipos de dados imutáveis

Criando um Conjunto Vazio: Use set() OBRIGATORIAMENTE!


Este é um ponto muito importante: se você usar apenas chaves vazias {} na tentativa
de criar um conjunto vazio, o Python, na verdade, criará um dicionário vazio.
Para criar um conjunto verdadeiramente vazio, você deve usar a função construtora
set() sem nenhum argumento:
1 # Tentativa de criar um conjunto vazio com {} ( INCORRETO para
conjuntos )
2 s u p o s t o _ c o n j u n t o _ v a z i o = {}
3 print ( f " Usando \{\}: { s u p o s t o _ c o n j u n t o _ v a z i o } , Tipo :
{ type ( s u p o s t o _ c o n j u n t o _ v a z i o ) } " )
4 # Sa í da : Usando {}: {} , Tipo : < class ’ dict ’> ( É um DICION Á RIO !)
5
6 # Forma CORRETA de criar um conjunto vazio
7 c o n j u n t o _ r e a l m e n t e _ v a z i o = set ()
8 print ( f " Usando set () : { c o n j u n t o _ r e a l m e n t e _ v a z i o } , Tipo :
{ type ( c o n j u n t o _ r e a l m e n t e _ v a z i o ) } " )
9 # Sa í da : Usando set () : set () , Tipo : < class ’ set ’>

Código 9.3: Criando um conjunto vazio corretamente com set()

Usando a Função Construtora set(iteravel)


A função construtora set() também pode ser usada para criar um conjunto a partir
de qualquer objeto iterável (como uma string, lista, tupla ou outro conjunto). Ao fazer
isso, set() automaticamente extrairá os elementos únicos do iterável para formar o novo
conjunto. A ordem original dos elementos no iterável não é necessariamente preservada.
9.2. Criando Conjuntos: Definindo Coleções de Itens Únicos 193

• A partir de uma String:


1 p a l a v r a _ p a r a _ c o n j u n t o = " abracadabra "
2 c on jun to _d e_l et ra s = set ( p a l a v r a _ p a r a _ c o n j u n t o )
3 print ( f " Conjunto de letras da palavra
’{ p a l a v r a _ p a r a _ c o n j u n t o } ’: { c onj un to _de _l et ras } " )
4 # Sa í da ( a ordem pode variar ) : { ’ a ’, ’r ’, ’b ’, ’c ’, ’d ’}

Código 9.4: Criando conjunto a partir de uma string

• A partir de uma Lista (removendo duplicatas):


1 l i s t a _ n u m e r o s _ c o m _ d u p l i c a t a s = [10 , 20 , 30 , 20 , 10 , 40 ,
50 , 10]
2 c o n j u n t o _ n u m e r o s _ u n i c o s = set ( l i s t a _ n u m e r o s _ c o m _ d u p l i c a t a s )
3 print ( f " Lista original com duplicatas :
{ lista_numeros_com_duplicatas }")
4 print ( f " Conjunto de n ú meros ú nicos ( da lista ) :
{ conjunto_numeros_unicos }")
5 # Sa í da ( a ordem pode variar ) : {40 , 10 , 50 , 20 , 30}

Código 9.5: Criando conjunto a partir de uma lista (remove duplicatas)

• A partir de uma Tupla:


1 t u p l a _ c o r e s _ r e p e t i d a s = ( " vermelho " , " verde " , " azul " ,
" vermelho " , " amarelo " , " verde " )
2 c o n j u n t o _ c o r e s _ u n i c a s = set ( t u p l a _ c o r e s _ r e p e t i d a s )
3 print ( f " Tupla original de cores : { t u p l a _ c o r e s _ r e p e t i d a s } " )
4 print ( f " Conjunto de cores ú nicas ( da tupla ) :
{ conjunto_cores_unicas }")
5 # Sa í da ( a ordem pode variar ) : { ’ amarelo ’, ’ verde ’,
’ azul ’, ’ vermelho ’}

Código 9.6: Criando conjunto a partir de uma tupla

Conjuntos e Quebras de Linha no Código Fonte


Ao definir um conjunto com muitos elementos usando a sintaxe literal {}, você pode
quebrar a definição em múltiplas linhas para melhorar a legibilidade, assim como com
listas e tuplas.
1 # Conjunto de n ú meros primos pequenos , formatado para leitura
2 numeros_primos_iniciais = {
3 2, 3, 5, 7,
4 11 , 13 , 17 , 19 , # Coment á rio em uma linha
5 23 , 29 , 31 , # V í gula final opcional
6 }
7 print ( f " Conjunto de n ú meros primos iniciais :
{ numeros_primos_iniciais }")

Código 9.7: Definindo um conjunto longo em múltiplas linhas

Com essas duas abordagens – a sintaxe literal {} para conjuntos não vazios e a função
construtora set() – você está pronto para criar conjuntos e começar a aproveitar suas
propriedades de unicidade.
194 Capítulo 9. Conjuntos (Sets)

9.3 Operações Básicas com Conjuntos


Uma vez que um conjunto (set) é criado, podemos realizar diversas operações básicas
sobre ele, como verificar seu tamanho, testar se um item específico pertence a ele, ou
percorrer seus elementos. É importante lembrar sempre das duas características principais
dos conjuntos: eles contêm apenas itens únicos e não são ordenados.

Obtendo o Tamanho de um Conjunto com len()


Assim como com listas, tuplas, strings e dicionários, a função embutida len() pode ser
usada para descobrir quantos itens um conjunto contém. Como conjuntos não permitem
duplicatas, len() retornará o número de elementos únicos.
1 # Usando len () com conjuntos
2 c o n j u n t o _ d e _ f r u t a s _ u n i c a s = { " ma ç ã " , " banana " , " laranja " , " ma ç ã " ,
" uva " }
3 print ( f " Conjunto de frutas : { c o n j u n t o _ d e _ f r u t a s _ u n i c a s } " )
4 # Sa í da ( a ordem pode variar ) : { ’ laranja ’, ’ banana ’, ’ ma ç ã ’, ’ uva ’}
5

6 n u m e r o _ d e _ f r u t a s _ d i s t i n t a s = len ( c o n j u n t o _ d e _ f r u t a s _ u n i c a s )
7 print ( f " O conjunto cont é m { n u m e r o _ d e _ f r u t a s _ d i s t i n t a s } frutas
distintas . " )
8
9 c o n j u n t o _ v a z i o _ c r i a d o = set ()
10 print ( f " Um conjunto vazio tem { len ( c o n j u n t o _ v a z i o _ c r i a d o ) }
elementos . " )

Código 9.8: Usando len() com conjuntos

Verificando Pertinência com in e not in (Muito Eficiente!)


Uma das grandes forças dos conjuntos em Python é a extrema eficiência com que eles
realizam testes de pertinência, ou seja, verificar se um determinado item está presente ou
não no conjunto.
• item in meu_conjunto: Retorna True se item estiver no conjunto.
• item not in meu_conjunto: Retorna True se item não estiver no conjunto.
Essas operações são, em geral, significativamente mais rápidas em conjuntos do que em
listas para grandes coleções.
1 i n g r e d i e n t e s _ r e c e i t a _ b o l o = { " farinha de trigo " , " a ç ú car " , " ovos " ,
" leite " , " fermento em p ó " }
2

3 # Verificando se um ingrediente est á presente


4 tem_acucar = " a ç ú car " in i n g r e d i e n t e s _ r e c e i t a _ b o l o
5 print ( f " A receita inclui ’a ç ú car ’? { tem_acucar } " )
6
7 tem_chocolate = " chocolate em p ó " in i n g r e d i e n t e s _ r e c e i t a _ b o l o
8 print ( f " A receita inclui ’ chocolate em p ó ’? { tem_chocolate } " )
9
10 # Usando ’ not in ’ para verificar aus ê ncia
11 nao_leva_sal = " sal " not in i n g r e d i e n t e s _ r e c e i t a _ b o l o
12 print ( f " A receita N Ã O leva ’ sal ’? { nao_leva_sal } " )

Código 9.9: Verificando pertinência em conjuntos com in e not in


9.4. Adicionando e Removendo Elementos de Conjuntos 195

Iterando sobre um Conjunto com Laço for


Os conjuntos são objetos iteráveis, o que significa que você pode usar um laço for para
percorrer cada um dos seus elementos.
Importante: A Ordem da Iteração NÃO é Garantida!
Como os conjuntos são coleções não ordenadas, quando você itera sobre um conjunto, a
ordem em que os elementos são “visitados” não é previsível e não deve ser considerada
para a lógica do seu programa.
1 # Iterando sobre um conjunto de tags de um artigo
2 tags_de_um_post = { " python " , " programa ç ã o " , " conjuntos " , " dados " ,
" tutorial " }
3

4 print ( " \ nTags associadas ao post ( a ordem de impress ã o pode


variar ) : " )
5 for tag_item in tags_de_um_post :
6 print ( f " - { tag_item . capitalize () } " )
7

8 # Outro exemplo
9 n u me ros _ no _ c on j un t o = {30 , 10 , 40 , 10 , 50 , 20 , 30}
10 print ( " \ nIterando sobre o conjunto de n ú meros ( a ordem pode
variar ) : " )
11 for numero_item in n um e ro s _ no _ co n ju n t o :
12 print ( numero_item , end = " | " )
13 print ( " \ nFim da itera ç ã o . " )

Código 9.10: Iterando sobre os elementos de um conjunto

O que NÃO se pode fazer com Conjuntos (devido à ausência de


ordem)
Como os conjuntos não têm uma ordem definida para seus elementos, operações que
dependem de uma posição ou índice numérico não se aplicam a eles:

• Acessar elementos por índice: Tentar meu_conjunto[0] resultará em um TypeError.

• Fatiar (slicing): Tentar meu_conjunto[0:2] também resultará em um TypeError.

Se você precisa de acesso por índice ou de uma ordem garantida, uma lista ou tupla seria
a estrutura de dados mais apropriada.
Estas operações básicas já tornam os conjuntos úteis para muitas tarefas. Na próxima
seção, veremos como podemos modificar conjuntos, adicionando e removendo elementos.

9.4 Adicionando e Removendo Elementos de Conjuntos


Uma das características importantes da estrutura de dados set (conjunto) padrão em
Python é que ela é mutável. Isso significa que, após a criação de um conjunto, podemos
adicionar novos elementos a ele ou remover elementos existentes. Essas operações, é
claro, sempre respeitarão a propriedade fundamental dos conjuntos: a unicidade dos seus
membros.
(Lembre-se que existe uma versão imutável do conjunto, o frozenset, que veremos
mais adiante e que não permite essas modificações).
196 Capítulo 9. Conjuntos (Sets)

9.4.1 Adicionando Elementos a um Conjunto


Python oferece duas maneiras principais de adicionar elementos a um conjunto:

• Método meu_conjunto.add(elemento)
Adiciona um único elemento ao conjunto. Se o elemento já existir, o conjunto não
é alterado. O elemento deve ser imutável.
1 # Adicionando elementos com o m é todo add ()
2 n u m e r o s _ p r i m o s _ c o n h e c i d o s = {2 , 3 , 5 , 7}
3 print ( f " Conjunto inicial de primos :
{ numeros_primos_conhecidos }")
4
5 n u m e r o s _ p r i m o s _ c o n h e c i d o s . add (11) # Adiciona um novo primo
6 print ( f " Ap ó s adicionar o n ú mero 11:
{ numeros_primos_conhecidos }")
7
8 n u m e r o s _ p r i m o s _ c o n h e c i d o s . add (5) # Tenta adicionar um
elemento j á existente
9 print ( f " Ap ó s tentar adicionar o 5 novamente :
{ numeros_primos_conhecidos }")
10
11 # Tentativa de adicionar uma lista ( mut á vel ) - GERARIA ERRO
12 # n u m e r o s _ p r i m o s _ c o n h e c i d o s . add ([13 , 17])

Código 9.11: Adicionando elementos a um conjunto com add()

• Método meu_conjunto.update(iteravel1, iteravel2, ...)


Adiciona todos os elementos de um ou mais iteráveis (lista, tupla, string, outro
conjunto) ao conjunto original. Duplicatas são ignoradas.
1 # Adicionando m ú ltiplos elementos com o m é todo update ()
2 m e u _ c o n j u n t o _ n u m e r i c o = {10 , 20 , 30}
3 print ( f " Meu conjunto num é rico inicial :
{ meu_conjunto_numerico }")
4
5 # Adicionando elementos de uma lista
6 l i s t a _ pa r a _ a d i c i o n a r = [30 , 40 , 50 , 40]
7 m e u _ c o n j u n t o _ n u m e r i c o . update ( l i s t a _ p a r a _ a d i c i o n a r )
8 print ( f " Ap ó s update com a lista { l i s t a _ pa r a _ a d i c i o n a r }:
{ meu_conjunto_numerico }")
9
10 # Adicionando elementos de uma string e de uma tupla
11 m e u _ c o n j u n t o _ n u m e r i c o . update ( " abc " , (50 , 60 , ’c ’) )
12 print ( f " Ap ó s update com ’ abc ’ e (50 , 60 , ’c ’) :
{ meu_conjunto_numerico }")

Código 9.12: Adicionando múltiplos elementos com update()

9.4.2 Removendo Elementos de um Conjunto


Python oferece alguns métodos para remover elementos, com comportamentos diferentes
caso o item não exista.

• Método meu_conjunto.remove(elemento)
Remove o elemento. Se o elemento não estiver presente, levanta um erro KeyError.
9.4. Adicionando e Removendo Elementos de Conjuntos 197

1 # Removendo elementos com o m é todo remove ()


2 conju nto_de_c ores = { " vermelho " , " verde " , " azul " ,
" amarelo " }
3 print ( f " Conjunto de cores inicial : { conju nto_de_co res } " )
4
5 if " verde " in conju nto_de_c ores :
6 co njunto_d e_cores . remove ( " verde " )
7 print ( f " Ap ó s remover ’ verde ’: { con junto_de _cores } " )
8

9 # conjunt o_de_cor es . remove (" roxo ") # GERARIA KeyError

Código 9.13: Removendo com remove() (gera erro se não existe)

• Método meu_conjunto.discard(elemento)
Também remove o elemento. Se o elemento não estiver presente, NÃO levanta
erro.
1 # Removendo elementos com o m é todo discard ()
2 c o n j u n to _ f e r r a m e n t a s = { " martelo " , " chave de fenda " ,
" alicate " , " serra " }
3 print ( f " Conjunto de ferramentas inicial :
{ conjunto_ferramentas }")
4
5 c o n j u n to _ f e r r a m e n t a s . discard ( " alicate " )
6 print ( f " Ap ó s discard ( ’ alicate ’) : { c o n j u n t o _ f e r ra m e n t a s } " )
7

8 c o n j u n to _ f e r r a m e n t a s . discard ( " prego " ) # " prego " n ã o est á


no conjunto , NENHUM ERRO
9 print ( f " Ap ó s discard ( ’ prego ’) : { c o n j u n t o _ f e r r a me n t a s } " )

Código 9.14: Removendo com discard() (não gera erro)

• Método meu_conjunto.pop()
Remove e retorna um elemento arbitrário do conjunto. Levanta KeyError se o
conjunto estiver vazio.
1 # Removendo um elemento arbitr á rio com pop ()
2 c o n j u n t o _ n u m e r o s _ p a r a _ p o p = {7 , 1 , 22 , 58 , 33}
3 print ( f " Conjunto original para pop :
{ conjunto_numeros_para_pop }")
4
5 if c o n j u n t o _ n u m e r o s _ p a r a _ p o p : # Verifica se o conjunto n ã o
est á vazio
6 it em_remov ido_pop = c o n j u n t o _ n u m e r o s _ p a r a _ p o p . pop ()
7 print ( f " Item removido com pop () : { i tem_remo vido_pop } " )
# O item exato pode variar
8 print ( f " Conjunto ap ó s pop () :
{ conjunto_numeros_para_pop }")
9 else :
10 print ( " O conjunto est á vazio , n ã o é poss í vel usar
pop () . " )
11
12 # Esvaziando o conjunto com pop () em um la ç o ( apenas para
demonstra ç ã o )
13 print ( " Esvaziando o conjunto com pop () : " )
14 while c o n j u n t o _ n u m e r o s _ p a r a _ p o p : # Enquanto o conjunto n ã o
for vazio ( Truthy )
198 Capítulo 9. Conjuntos (Sets)

15 print ( f " Removido : { c o n j u n t o _ n u m e r o s _ p a r a _ p o p . pop () } " )


16 print ( f " Conjunto final : { c o n j u n t o _ n u m e r o s _ p a r a _ p o p } " ) #
Sa í da : set ()

Código 9.15: Removendo um item arbitrário com pop()

• Método meu_conjunto.clear()
Remove todos os elementos do conjunto, deixando-o vazio.
1 # Esvaziando um conjunto com clear ()
2 c o n j u n to _ p a r a _ l i m p a r = { " itemA " , " itemB " , " itemC " , 123}
3 print ( f " Conjunto antes de clear () : { c o n j u nt o _ p a r a _ l i m p a r } " )
4
5 c o n j u n to _ p a r a _ l i m p a r . clear ()
6 print ( f " Conjunto ap ó s clear () : { c o n j u n t o _ p a ra _ l i m p a r } " ) #
Sa í da : set ()

Código 9.16: Esvaziando um conjunto com clear()

A escolha do método de remoção dependerá da sua necessidade específica e de como você


quer lidar comaausência de um item.

9.5 Operações de Teoria dos Conjuntos


Uma das grandes utilidades dos conjuntos (set) em Python é a capacidade de realizar
operações matemáticas de teoria dos conjuntos de forma direta e eficiente. Essas operações
permitem combinar ou comparar conjuntos para encontrar elementos comuns, elementos
únicos, ou todos os elementos combinados.
Python oferece tanto operadores especiais quanto métodos equivalentes para realizar
essas operações. Em geral, os métodos que retornam um novo conjunto não modificam
os conjuntos originais, enquanto existem também versões de métodos que atualizam um
conjunto “in-place”.

9.5.1 União de Conjuntos: Juntando Tudo


A união de dois ou mais conjuntos resulta em um novo conjunto que contém todos
os elementos únicos que estão presentes em qualquer um dos conjuntos originais.

• Operador: | (barra vertical) – Ex: conjunto1 | conjunto2

• Método: [Link](conjunto2, ...) – Retorna um novo conjunto.

1 # Exemplo de Uni ã o de Conjuntos


2 set_impares = {1 , 3 , 5 , 7 , 9}
3 s e t_ pri m os _ p eq u en o s = {2 , 3 , 5 , 7 , 11}
4 se t_ out ro s_ num er os = {10 , 20}
5
6 # Uni ã o usando o operador |
7 u n i a o _ i m p a r e s _ p r i m o s = set_impares | s et _ p ri m os _ p eq u en o s
8 print ( f " Í mpares : { set_impares } " )
9 print ( f " Primos pequenos : { se t _ pr i mo s _p e q ue n os } " )
10 print ( f " Uni ã o de Í mpares e Primos ( com |) : { u n ia o _ i m p a r e s _ p r i m o s } " )
11
9.5. Operações de Teoria dos Conjuntos 199

12 # Uni ã o usando o m é todo . union ()


13 uniao_total = set_impares . union ( set_primos_pequenos ,
se t_ ou tro s_ nu mer os )
14 print ( f " Uni ã o de todos os tr ê s conjuntos ( com . union () ) :
{ uniao_total } " )

Código 9.17: União de conjuntos com operador | e método union()

9.5.2 Interseção de Conjuntos: Encontrando os Elementos Comuns


A interseção de dois ou mais conjuntos resulta em um novo conjunto que contém
apenas os elementos que são comuns a todos os conjuntos envolvidos.
• Operador: & (E comercial) – Ex: conjunto1 & conjunto2
• Método: [Link](conjunto2, ...) – Retorna um novo con-
junto.
1 # Usando set_impares e se t _p r i mo s _p e q ue n os definidos anteriormente
2 set_impares = {1 , 3 , 5 , 7 , 9}
3 s e t_ pri m os _ p eq u en o s = {2 , 3 , 5 , 7 , 11}
4 i n t e r s e c a o _ i m p a r e s _ p r i m o s = set_impares & s et _ pr i mo s _ pe q ue n o s
5 print ( f " \ nInterse ç ã o de Í mpares e Primos ( com &) :
{ intersecao_impares_primos }")
6
7 # Exemplo com tr ê s conjuntos
8 habilidades_dev1 = { " Python " , " Java " , " SQL " }
9 habilidades_dev2 = { " Python " , " JavaScript " , " HTML " , " CSS " }
10 habilidades_dev3 = { " Python " , " SQL " , " JavaScript " }
11 habilidades_comuns_todos =
habilidades_dev1 . intersection ( habilidades_dev2 , habilidades_dev3 )
12 print ( f " Habilidades comuns a todos os 3 devs :
{ habilidades_comuns_todos }")

Código 9.18: Interseção de conjuntos com operador & e método intersection()

9.5.3 Diferença de Conjuntos: O que Há em Um, mas Não no


Outro
A diferença entre um conjunto A e um conjunto B (A - B) resulta em um novo
conjunto com os elementos que estão em A, mas não em B. A ordem importa.
• Operador: - (sinal de menos) – Ex: conjunto1 - conjunto2
• Método: [Link](conjunto2, ...) – Retorna um novo conjunto.
1 # Usando set_impares e se t _p r i mo s _p e q ue n os
2 set_impares = {1 , 3 , 5 , 7 , 9}
3 s e t_ pri m os _ p eq u en o s = {2 , 3 , 5 , 7 , 11}
4

5 diferenca_a_b = set_impares - s e t_ p r im o s_ p e qu e no s
6 print ( f " \ nDiferen ç a ( Í mpares - Primos ) : { diferenca_a_b } " )
7
8 diferenca_b_a = s e t_ p ri m o s_ p eq u en o s . difference ( set_impares )
9 print ( f " Diferen ç a ( Primos - Í mpares ) : { diferenca_b_a } " )

Código 9.19: Diferença de conjuntos com operador - e método difference()


200 Capítulo 9. Conjuntos (Sets)

9.5.4 Diferença Simétrica de Conjuntos: Elementos Não Comuns


a Ambos
A diferença simétrica entre dois conjuntos A e B resulta em um novo conjunto com
todos os elementos que estão em A ou em B, mas não nos dois ao mesmo tempo.

• Operador: ˆ (acento circunflexo) – Ex: conjunto1 ˆ conjunto2

• Método: conjunto1.symmetric_difference(conjunto2) – Retorna um novo con-


junto.

1 # Usando set_impares e se t _p r i mo s _p e q ue n os
2 set_impares = {1 , 3 , 5 , 7 , 9}
3 s e t_ pri m os _ p eq u en o s = {2 , 3 , 5 , 7 , 11}
4
5 dif_simetrica_op = set_impares ^ s e t_ p ri m o s_ p eq u e no s
6 print ( f " \ nDiferen ç a Sim é trica ( com ^) : { dif_simetrica_op } " )
7

8 dif_simetrica_metodo =
set_impares . s y m m e t r i c _ d i f f e r en c e ( s et _ p ri m os _ p eq u en o s )
9 print ( f " Diferen ç a Sim é trica ( com m é todo ) : { d i f _ s i m e t r i ca _ m e t o d o } " )

Código 9.20: Diferença simétrica com operador ˆ e método symmetric_difference()

9.5.5 Métodos de Atualização “In-Place”


Para cada uma das operações acima que retornam um novo conjunto, existem métodos
correspondentes que modificam o conjunto original “in-place”:

• União in-place:
meu_conjunto.update(outro) ou meu_conjunto |= outro

• Interseção in-place:
meu_conjunto.intersection_update(outro) ou meu_conjunto &= outro

• Diferença in-place:
meu_conjunto.difference_update(outro) ou meu_conjunto -= outro

• Diferença Simétrica in-place:


meu_conjunto.symmetric_difference_update(outro) ou
meu_conjunto ˆ = outro

1 set_x = {10 , 20 , 30 , 40}


2 set_y = {30 , 40 , 50 , 60}
3 print ( f " Conjunto X original : { set_x } " )
4 print ( f " Conjunto Y original : { set_y } " )
5
6 # Fazendo uma c ó pia de X para i nt e r se c ti o n _u p da t e
7 c o n j u n t o _ x _ c o p i a _ i n t e r = set_x . copy ()
8 c o n j u n t o _ x _ c o p i a _ i n t e r . i nt e r se c ti o n _u p da t e ( set_y )
9 print ( f " X ap ó s i n te r se c t io n _u p d at e ( Y ) : { c o n j u n t o _ x _ c o p i a _ i n t e r } " )
10
11 # Fazendo outra c ó pia para differ ence_upda te
12 c o n j u n t o _ x _ c o p i a _ d i f f = set_x . copy ()
9.6. Métodos de Comparação de Conjuntos: Analisando Relações 201

13 c o n j u n t o _ x _ c o p i a _ d i f f . differenc e_update ( set_y )


14 print ( f " X ap ó s differen ce_updat e ( Y ) : { c o n j u n t o _ x _ c o p i a _ d i f f } " )

Código 9.21: Exemplos de métodos de atualização de conjuntos in-place


Use os métodos de atualização in-place quando não precisar preservar o conjunto original.
As operações de teoria dos conjuntos são uma das características mais distintas e
poderosas dos sets em Python.

9.6 Métodos de Comparação de Conjuntos: Analisando


Relações
Além das operações de teoria dos conjuntos que criam novos conjuntos (como união,
interseção, etc.), Python também oferece métodos específicos para comparar conjuntos e
verificar as relações entre eles. Essas operações de comparação geralmente retornam um
valor booleano (True ou False).

9.6.1 Verificando se um Conjunto é Subconjunto de Outro


Um conjunto A é considerado um subconjunto de um conjunto B se todos os elementos
de A também estiverem presentes em B.

• Método conjunto_A.issubset(conjunto_B): Retorna True se A é subconjunto


de B.

• Operador <=: conjunto_A <= conjunto_B é equivalente a issubset().

• Subconjunto Próprio (Operador <): conjunto_A < conjunto_B verifica se A é


subconjunto de B E A é diferente de B.

1 # Exemplos de verifica ç ã o de subconjunto


2 conj_a = {1 , 2 , 3}
3 conj_b = {1 , 2 , 3 , 4 , 5}
4 conj_c = {1 , 2 , 6} # O ’6 ’ n ã o est á em B
5 conj_d_igual_a_a = {1 , 2 , 3} # Igual a ’a ’
6
7 # Usando issubset ()
8 print ( f " A ({ conj_a }) é subconjunto de B ({ conj_b }) ?
{ conj_a . issubset ( conj_b ) } " )
9 print ( f " C ({ conj_c }) é subconjunto de B ({ conj_b }) ?
{ conj_c . issubset ( conj_b ) } " )
10
11 # Usando o operador <=
12 print ( f " A ({ conj_a }) <= B ({ conj_b }) ? { conj_a <= conj_b } " )
13 print ( f " A ({ conj_a }) <= D ({ conj_d_igual_a_a }) ? { conj_a <=
conj_d_igual_a_a } " )
14
15 # Usando o operador < ( subconjunto pr ó prio )
16 print ( f " A ({ conj_a }) < B ({ conj_b }) ? { conj_a < conj_b } " )
17 print ( f " A ({ conj_a }) < D ({ conj_d_igual_a_a }) ? { conj_a <
conj_d_igual_a_a } " )

Código 9.22: Verificando se um conjunto é subconjunto de outro


202 Capítulo 9. Conjuntos (Sets)

9.6.2 Verificando se um Conjunto é Superconjunto de Outro


Um conjunto A é considerado um superconjunto de um conjunto B se A contiver todos
os elementos que estão presentes em B.

• Método conjunto_A.issuperset(conjunto_B): Retorna True se A é supercon-


junto de B.

• Operador >=: conjunto_A >= conjunto_B é equivalente a issuperset().

• Superconjunto Próprio (Operador >): conjunto_A > conjunto_B verifica se A


é superconjunto de B E A é diferente de B.

1 # Usando os conjuntos a , b , d definidos anteriormente :


2 conj_a = {1 , 2 , 3}
3 conj_b = {1 , 2 , 3 , 4 , 5}
4 conj_d_igual_a_a = {1 , 2 , 3}
5
6 # Usando issuperset ()
7 print ( f " \ nB ({ conj_b }) é superconjunto de A ({ conj_a }) ?
{ conj_b . issuperset ( conj_a ) } " )
8 print ( f " A ({ conj_a }) é superconjunto de B ({ conj_b }) ?
{ conj_a . issuperset ( conj_b ) } " )
9
10 # Usando o operador >=
11 print ( f " B ({ conj_b }) >= A ({ conj_a }) ? { conj_b >= conj_a } " )
12 print ( f " D ({ conj_d_igual_a_a }) >= A ({ conj_a }) ? { conj_d_igual_a_a
>= conj_a } " )
13
14 # Usando o operador > ( superconjunto pr ó prio )
15 print ( f " B ({ conj_b }) > A ({ conj_a }) ? { conj_b > conj_a } " )
16 print ( f " D ({ conj_d_igual_a_a }) > A ({ conj_a }) ? { conj_d_igual_a_a >
conj_a } " )

Código 9.23: Verificando se um conjunto é superconjunto de outro

9.6.3 Verificando se Dois Conjuntos são Disjuntos


Dois conjuntos são considerados disjuntos se eles não tiverem nenhum elemento em
comum (ou seja, se a interseção entre eles for um conjunto vazio).

• Método conjunto_A.isdisjoint(conjunto_B): Retorna True se A e B não tiverem


elementos em comum.

1 # Exemplos de verifica ç ã o de conjuntos disjuntos


2 conjunto1 = {1 , 2 , 3 , 4}
3 conjunto2 = {5 , 6 , 7 , 8} # Nenhum elemento em comum com conjunto1
4 conjunto3 = {3 , 4 , 9 , 10} # Tem os elementos 3 e 4 em comum com
conjunto1
5
6 # Verificando se conjunto1 e conjunto2 s ã o disjuntos
7 sao_disju ntos_1_2 = conjunto1 . isdisjoint ( conjunto2 )
8 print ( f " \ nConjunto1 ({ conjunto1 }) e Conjunto2 ({ conjunto2 }) s ã o
disjuntos ? { sao_d isjuntos _1_2 } " )
9
9.7. Conjuntos Imutáveis: frozenset (Conjunto Congelado) 203

10 # Verificando se conjunto1 e conjunto3 s ã o disjuntos


11 s ao_disju ntos_1_3 = conjunto1 . isdisjoint ( conjunto3 )
12 print ( f " Conjunto1 ({ conjunto1 }) e Conjunto3 ({ conjunto3 }) s ã o
disjuntos ? { sao_d isjuntos _1_3 } " )

Código 9.24: Verificando se conjuntos são disjuntos com isdisjoint()


Esses métodos de comparação são ferramentas valiosas para analisar as relações entre
diferentes coleções de itens únicos. Eles são frequentemente usados em conjunto com
estruturas condicionais (if) para tomar decisões baseadas em como os conjuntos se
sobrepõem ou se contêm.

9.7 Conjuntos Imutáveis: frozenset (Conjunto Conge-


lado)
Até agora, todos os conjuntos (set) que vimos são mutáveis, o que significa que
podemos adicionar e remover elementos deles após sua criação. No entanto, Python também
oferece um tipo de conjunto chamado frozenset (que podemos traduzir livremente como
“conjunto congelado” ou “conjunto imutável”).

O que é um frozenset?
Um frozenset é, em essência, uma versão imutável de um set. Ele compartilha
muitas das propriedades de um set comum:

• Armazena uma coleção não ordenada de itens.

• Garante que todos os seus itens sejam únicos.

• Os itens dentro de um frozenset também devem ser de tipos imutáveis.

A grande e crucial diferença é: uma vez que um frozenset é criado, ele não pode
mais ser alterado.

Criando um frozenset
Para criar um frozenset, você utiliza a função construtora frozenset(), que pode
receber um objeto iterável (como uma lista, tupla, string ou outro conjunto) como
argumento.
1 # Criando frozensets a partir de diferentes iter á veis
2 lista_numeros = [1 , 2 , 3 , 2 , 1 , 4 , 5 , 4]
3 f s e t _ a _ p a r t i r _ d e _ l i s t a = frozenset ( lista_numeros )
4 print ( f " Frozenset a partir da lista { lista_numeros }:
{ fset_a_partir_de_lista }")
5
6 s t r i n g _ c o m _ r e p e t i c a o = " banana "
7 f s e t _ a _ p a r t i r _ d e _ s t r i n g = frozenset ( st r i n g _ c o m _ r e p e t i c a o )
8 print ( f " Frozenset a partir da string ’{ s t r i n g _ c o m _ r e pe t i c a o } ’:
{ fset_a_partir_de_string }")
9
10 fs et _va zi o_ exe mp lo = frozenset ()
11 print ( f " Um frozenset vazio : { f set _v az io_ ex em plo } " )
204 Capítulo 9. Conjuntos (Sets)

Código 9.25: Criando frozensets


Não existe uma sintaxe literal com chaves {} para criar frozensets.

Operações Permitidas com frozenset


Devido à sua imutabilidade, um frozenset não possui métodos como add(), remove(),
update(), etc.
No entanto, frozensets suportam todas as operações que não modificam o conjunto
e que geralmente retornam um novo conjunto (que também será um frozenset) ou um
valor booleano:

• Operações de teoria dos conjuntos: union() (|), intersection() (&), difference()


(-), symmetric_difference() (ˆ).

• Métodos de comparação: issubset() (<=), issuperset() (>=), isdisjoint().

• Funções como len().

• Operador in para verificação de pertinência.

• Iteração com laço for.

1 fs1 = frozenset ([1 , 2 , 3 , 4])


2 fs2 = frozenset ([3 , 4 , 5 , 6])
3
4 # Uni ã o ( retorna um novo frozenset )
5 fs_uniao = fs1 . union ( fs2 )
6 print ( f " Uni ã o de fs1 e fs2 : { fs_uniao } , Tipo : { type ( fs_uniao ) } " )
7
8 # Interse ç ã o
9 fs_intersecao = fs1 & fs2
10 print ( f " Interse ç ã o de fs1 e fs2 : { fs_intersecao } " )
11
12 print ( f " 3 est á em fs1 ? {3 in fs1 } " ) # Verifica ç ã o de pertin ê ncia
13
14 # Tentativa de modificar ( GERARIA ERRO )
15 # fs1 . add (10) # AttributeError

Código 9.26: Operações com frozensets

Principal Vantagem e Uso de frozenset: Ser “Hasheável” (Hasha-


ble)
A principal razão de existir do frozenset é que ele é hasheável (do inglês hashable).
Um objeto hasheável tem um valor de hash que não muda durante sua vida, o que requer
que o objeto seja imutável. Objetos hasheáveis podem ser usados como chaves de dicionário
ou como elementos de um conjunto.
Como frozensets são imutáveis, eles são hasheáveis. Isso significa que um frozenset
pode ser:

1. Elemento de outro conjunto (set ou frozenset):


9.8. Quando Usar Conjuntos (set e frozenset)? 205

1 conjunto_de_frozensets = {
2 frozenset ({1 , 2}) ,
3 frozenset ({ " a " , " b " }) ,
4 frozenset ({1 , 2}) # Duplicata de frozenset ({1 ,2}) é
ignorada
5 }
6 print ( f " Conjunto de frozensets : { c o n j u n t o _ d e _ f r o z e n s e t s } " )
7
8 # Voc ê N Ã O PODE fazer isso com sets mut á veis :
9 # c o n j u n t o _ d e _ s e t s _ m u t a v e i s = {{1 , 2} , {3 , 4}} # Geraria
TypeError

Código 9.27: frozenset como elemento de outro conjunto

2. Uma chave em um dicionário:


1 # Dicion á rio onde as chaves s ã o frozensets
2 regras_desconto_conjuntos = {
3 frozenset ({ " p ã o " , " leite " }) : 0.05 , # 5% de desconto
4 frozenset ({ " caf é " , " a ç ú car " , " filtro " }) : 0.10 # 10% de
desconto
5 }
6 m e u _ c o n j u n t o _ d e _ c o m p r a = frozenset ({ " leite " , " p ã o " }) #
Ordem n ã o importa
7 if m e u _ c o n j u n t o _ d e _ c o m p r a in r e g r a s _ d e s c o n t o _ c o n j u n t o s :
8 desconto =
regras_desconto_conjuntos [ meu_conjunto_de_compra ]
9 print ( f " Desconto para { m e u _ c o n j u n t o _ d e _ c o m p r a }:
{ desconto *100:.0 f }% " )

Código 9.28: frozenset como chave de dicionário

Esta capacidade de serem hasheáveis é a principal utilidade dos frozensets.

9.8 Quando Usar Conjuntos (set e frozenset)?


Ao longo deste capítulo, exploramos os conjuntos (set) e sua variação imutável, os
frozensets. Vimos suas características principais – coleções não ordenadas de itens únicos
– e como realizar diversas operações. Agora, a questão é: “Em quais situações práticas
essas estruturas de dados realmente brilham?”

Quando Usar set (Conjuntos Mutáveis)


Os conjuntos mutáveis são ideais para:
1. Remoção Eficiente de Duplicatas de uma Coleção:
Converter uma lista para set é a forma mais Pythônica de obter itens únicos.
1 l i s t a _ c o m _ m u i t a s _ d u p l i c a t a s = [1 , 2 , ’a ’ , 3 , 2 , ’b ’ , 1 ,
’a ’ , 4 , 5 , 2]
2 itens_unicos_como_conjunto =
set ( l i s t a _ c o m _ m u i t a s _ d u p l i c a t a s )
3 print ( f " Lista original : { l i s t a _ c o m _ m u i t a s _ d u p l i c a t a s } " )
4 print ( f " Itens ú nicos ( como conjunto ) :
{ itens_unicos_como_conjunto }")
206 Capítulo 9. Conjuntos (Sets)

5
6 # Se precisar de volta como uma lista ( ordem original n ã o é
garantida ) :
7 l i s t a _ d e _ i t e n s _ u n i c o s = list ( i t e n s _ u n i c o s _ c o m o _ c o n j u n t o )
8 print ( f " Itens ú nicos ( como lista ) :
{ lista_de_itens_unicos }")

Código 9.29: Removendo duplicatas de uma lista usando set

2. Testes de Pertinência (in, not in) Muito Rápidos:


Se você precisa verificar frequentemente se um item existe em uma grande coleção e
a ordem não importa, sets são muito mais rápidos que listas.
1 # Suponha um grande conjunto de palavras v á lidas
2 p a l a v r a s _ v a l i d a s _ d i c i o n a r i o = { " casa " , " mesa " , " cadeira " ,
" python " }
3 # ... imagine milhares de palavras ...
4
5 if " python " in p a l a v r a s _ v a l i d a s _ d i c i o n a r i o :
6 print ( " ’ python ’ é uma palavra v á lida . " )
7 if " pyhton " not in p a l a v r a s _ v a l i d a s _ d i c i o n a r i o : # Erro de
digita ç ã o
8 print ( " ’ pyhton ’ N Ã O é uma palavra v á lida . " )

Código 9.30: Teste de pertinência eficiente com set

3. Realizar Operações de Teoria dos Conjuntos:


Quando você precisa de uniões, interseções, diferenças, etc., entre coleções.

• Exemplo: Encontrar elementos comuns entre duas listas de tags.

1 tags_artigo1 = { " python " , " dados " , " ia " }


2 tags_artigo2 = { " python " , " web " , " django " }
3 tags_comuns = tags_artigo1 . intersection ( tags_artigo2 )
4 print ( f " Tags comuns : { tags_comuns } " ) # Sa í da : { ’ python ’}
5 t o d a s _ as _ t a g s _ u n i c a s = tags_artigo1 . union ( tags_artigo2 )
6 print ( f " Todas as tags ú nicas : { t o d a s _ a s _ t ag s _ u n i c a s } " )

Código 9.31: Operações de teoria dos conjuntos

4. Quando a Ordem Não Importa e a Unicidade é Chave:


Se a coleção representa um grupo de itens onde a ordem é irrelevante, e você só se
importa com os itens distintos.

Quando Usar frozenset (Conjuntos Imutáveis)


O frozenset compartilha as propriedades de unicidade e as operações de teoria dos
conjuntos com o set, mas sua imutabilidade o torna adequado para:

1. Quando Você Precisa de um Conjunto Imutável: Para garantir a integridade


dos dados.

2. Como Elementos de Outro Conjunto: sets são mutáveis e não podem ser
elementos de outros sets. frozensets podem.
9.8. Quando Usar Conjuntos (set e frozenset)? 207

1 conjunto_de_combinacoes = {
2 frozenset ({ " vermelho " , " azul " }) ,
3 frozenset ({ " amarelo " , " azul " })
4 }
5 print ( f " Combina ç õ es de cores : { c o n j u n t o _ d e _ c o m b i n a c o e s } " )

Código 9.32: frozenset como elemento de outro conjunto

3. Como Chaves de Dicionário: Se você precisa usar uma coleção de itens únicos
(onde a ordem não importa) como chave, um frozenset é a solução.
1 menu_combinado = {
2 frozenset ({ " p ã o " , " queijo " , " presunto " }) : " Misto
Quente " ,
3 frozenset ({ " massa " , " molho de tomate " , " queijo " }) :
" Macarronada "
4 }
5 in g re d ie n t es _ at u a is = frozenset ({ " queijo " , " p ã o " ,
" presunto " })
6 if i n gr e d ie n te s _a t u ai s in menu_combinado :
7 print ( f " Com { i n gr e di e n te s _a t u ai s } , voc ê pode fazer :
{ menu_combinado [ in g re d ie n t es _ at u a is ]} " )

Código 9.33: frozenset como chave de dicionário

Comparativo Rápido: Listas, Tuplas, Dicionários e Conjuntos


A tabela a seguir resume as principais características das estruturas de dados que
vimos até agora: *(Lembre-se que a “ordem” em dicionários refere-se à ordem de inserção

Tabela 9.1: Comparativo das Principais Estruturas de Dados em Python.


Estrutura Ordenada? Mutável? Duplicatas? Acesso Principal
Lista (list) Sim Sim Sim Índice Numérico
Tupla (tuple) Sim Não Sim Índice Numérico
Dicionário (dict) Sim (3.7+) Sim Chaves Únicas Chave
Conjunto (set) Não Sim Não Pertinência
frozenset Não Não Não Pertinência

em Python 3.7+, mas o acesso primário não é por essa ordem.)*

Concluindo o Capítulo sobre Conjuntos


Neste capítulo, exploramos os conjuntos (set) e sua contraparte imutável, os frozensets.
Vimos que sua principal força reside na capacidade de armazenar coleções de itens únicos
de forma eficiente e na facilidade com que podemos realizar operações da teoria dos
conjuntos, como união, interseção e diferença. Também aprendemos a criar, adicionar,
remover e comparar conjuntos, e entendemos quando cada um (mutável ou imutável) é
mais apropriado.
Os conjuntos são ferramentas poderosas para tarefas como remover duplicatas, realizar
testes de pertinência rápidos e manipular grupos de dados com base em suas relações
matemáticas.
208 Capítulo 9. Conjuntos (Sets)

Com o conhecimento sobre listas, tuplas, dicionários e agora conjuntos, você tem um
arsenal robusto das principais estruturas de dados embutidas do Python! Isso conclui a
Parte II do nosso livro sobre Estruturas de Dados Fundamentais.
Na Parte III: Funções e Modularização, começaremos a organizar nosso código de
forma mais estruturada e reutilizável, aprendendo a definir e usar nossas próprias funções.
Prepare-se para dar um grande passo em direção à escrita de programas mais complexos e
bem organizados!
Parte III

Funções e Modularização

209
Capítulo 10

Definindo e Usando Funções

Até este ponto em nossa jornada com Python, aprendemos a escrever sequências de
instruções, tomar decisões com estruturas condicionais e repetir tarefas com laços. Nossos
programas, embora funcionais, podem começar a se tornar longos e, em alguns casos,
repetitivos se precisarmos realizar a mesma sequência de passos em diferentes partes do
código.
Imagine que você tem um cálculo específico – como calcular a área de um círculo dado
o seu raio – que precisa ser feito em vários pontos do seu programa. Você poderia copiar e
colar as linhas de código desse cálculo toda vez que precisar dele? Sim, mas isso não seria
nem um pouco eficiente ou elegante. Se você precisasse corrigir ou alterar a forma como a
área é calculada, teria que mudar em todos os lugares onde copiou o código!
É para resolver esse tipo de situação, e para nos ajudar a construir programas maiores
e mais organizados, que existem as funções.

10.1 Introdução às Funções: Blocos de Código Reutili-


záveis
Em sua essência, uma função é um bloco de código nomeado que realiza uma
tarefa específica. Pense nela como uma “mini-receita” ou um “subprograma” dentro do
seu programa principal. Você define essa sequência de instruções uma vez, dá um nome a
ela e, a partir daí, pode “chamar” ou “invocar” essa função pelo nome sempre que precisar
executar aquela tarefa específica.

Por que Usar Funções? Os Superpoderes da Modularização


O uso de funções é uma das pedras angulares da programação estruturada e traz
inúmeros benefícios:
1. Reutilização de Código (DRY - Don’t Repeat Yourself - Não se Repita):
Este é talvez o benefício mais óbvio. Se você tem um conjunto de operações que
precisa realizar múltiplas vezes, você o define em uma função e chama essa função
quantas vezes for necessário. Isso evita a duplicação de código, tornando seus
programas mais curtos, mais fáceis de escrever e, crucialmente, mais fáceis de dar
manutenção.
2. Modularização e Organização:
Funções permitem que você quebre um programa grande e complexo em partes

210
10.2. Definindo uma Função: A Instrução def 211

menores, mais gerenciáveis e mais compreensíveis. Cada função pode ser responsável
por uma pequena parte da lógica geral do programa.

3. Abstração:
Funções nos permitem usar o princípio da abstração. Elas “escondem” os detalhes
de como uma tarefa é realizada e expõem apenas uma interface simples para usá-la
(o nome da função e os dados que ela precisa, chamados de parâmetros). Você não
precisa saber como a função print() funciona internamente para usá-la; você apenas
sabe seu nome e o que ela faz.

4. Legibilidade:
Programas bem divididos em funções tendem a ser mais fáceis de ler e seguir. O
fluxo principal do programa pode ser composto por uma série de chamadas a funções
com nomes descritivos, tornando a lógica geral mais clara.

Já Usamos Funções!
Na verdade, você já tem usado funções em Python desde o início:

• print(): Para exibir dados.

• len(): Para obter o comprimento de sequências.

• input(): Para ler dados do usuário.

• type(): Para verificar o tipo de um dado.

• int(), float(), str(), list(), set(), dict(): Funções construtoras.

• range(): Para gerar sequências numéricas.

Estas são funções embutidas (built-in functions). Além delas, também vimos os
métodos (como minha_lista.append() ou minha_string.upper()), que são, na prática,
funções associadas a objetos específicos.
A grande novidade neste capítulo é que agora aprenderemos a criar e usar nossas
próprias funções, personalizadas para as necessidades específicas dos nossos programas.
Isso nos dará um poder imenso para construir aplicações mais sofisticadas, organizadas e
eficientes de forma “descomplicada”.

10.2 Definindo uma Função: A Instrução def


Para que possamos usar nossas próprias funções em um programa Python, o primeiro
passo é defini-las. A definição de uma função envolve dar um nome a ela e especificar o
conjunto de instruções (o “corpo” da função) que serão executadas quando essa função for
chamada. Em Python, a palavra-chave utilizada para iniciar a definição de uma função é
def.
212 Capítulo 10. Definindo e Usando Funções

A Sintaxe da Definição de Função com def


A forma mais básica de definir uma função em Python, inicialmente sem que ela receba
informações externas (parâmetros), é a seguinte:
1 def nome_da_funcao () :
2 # Linha 1 do corpo da fun ç ã o ( indentada )
3 # Linha 2 do corpo da fun ç ã o ( indentada )
4 # ... mais instru ç õ es que comp õ em a fun ç ã o ...

Código 10.1: Sintaxe básica para definir uma função


Vamos analisar cada componente desta sintaxe:

1. def: Esta é a palavra-chave que informa ao interpretador Python que você está
prestes a definir uma função. Ela deve ser a primeira coisa na linha de definição da
função.

2. nome_da_funcao: Este é o nome que você escolhe para a sua função.

• Ele deve seguir as mesmas regras de nomenclatura que usamos para variáveis
(pode conter letras, números e o caractere underscore _, mas não pode começar
com um número e nem ser uma palavra reservada do Python).
• A convenção em Python para nomes de funções é usar snake_case (letras mi-
núsculas com palavras separadas por underscores), por exemplo: calcular_media,
imprimir_relatorio.
• O nome deve ser descritivo, indicando o que a função faz.

3. Parênteses (): Imediatamente após o nome da função, você deve colocar um par
de parênteses.

• Por enquanto, estarão vazios, indicando que a função não recebe parâmetros.
Aprenderemos sobre parâmetros na Seção 10.4. Mesmo sem parâmetros, os
parênteses são obrigatórios.

4. Dois Pontos (:): A primeira linha da definição da função (a linha def) deve
terminar com dois pontos. Eles sinalizam o início do bloco de código que constitui o
corpo da função.

5. Corpo da Função (Bloco Indentado):

• Uma ou mais instruções Python que formam o corpo da função.


• Crucial: Todas as instruções dentro do corpo da função devem estar indenta-
das em relação à linha def (convenção: 4 espaços). A indentação define o que
faz parte da função.

O que Acontece Quando uma Função é Definida?


É importante entender que, quando o Python executa uma linha com def nome_da_funcao():,
ele não executa o código que está dentro do corpo da função imediatamente.
O que acontece é:

1. Python cria um objeto função.


10.3. Chamando (Invocando) uma Função: Colocando o Código em Ação 213

2. Ele associa o nome_da_funcao que você escolheu a esse objeto função.


3. O código dentro do corpo da função é armazenado como parte desse objeto função,
pronto para ser executado.
O corpo da função só será efetivamente executado quando a função for chamada (ou
invocada) em outra parte do seu programa.

Exemplo: Definindo uma Função Simples de Saudação


Vamos criar nossa primeira função. Ela será bem simples: apenas imprimirá uma
saudação na tela.
1 # Definindo nossa primeira fun ç ã o chamada ’ saudacao_inicial ’
2 def saudacao_inicial () :
3 # Este é o corpo da fun ç ã o ( indentado )
4 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " )
5 print ( " Bem - vindo ( a ) ao Programa Exemplo ! " )
6 print ( " Esta fun ç ã o imprime uma sauda ç ã o . " )
7 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " )
8
9 # Neste ponto do programa , a fun ç ã o ’ saudacao_inicial ’ foi
DEFINIDA .
10 # O Python sabe que ela existe e o que ela faz , mas as mensagens
11 # acima AINDA N Ã O foram impressas no console .
12 # Elas s ó aparecer ã o quando chamarmos a fun ç ã o .

Código 10.2: Definindo uma função simples de saudação


Definir funções com def é o primeiro passo fundamental para criar um código mais
modular, organizado e reutilizável. Na próxima seção, aprenderemos como efetivamente
colocar nossas funções para trabalhar, ou seja, como chamá-las.

10.3 Chamando (Invocando) uma Função: Colocando o


Código em Ação
Na seção anterior, aprendemos a definir uma função usandoapalavra-chave def. Definir
uma função é como escrever uma receita ou criar uma planta baixa para uma casa: você
detalha os passos ou a estrutura, mas nada acontece de fato até que você decida seguir a
receita ou construir a casa.
Da mesma forma, o código dentro de uma função só é executado quando a função é
chamada (ou invocada). Chamar uma função significa dar a ordem para que o Python
execute o bloco de código que foi definido sob o nome daquela função.

A Sintaxe para Chamar uma Função


Chamar uma função em Python é muito simples: basta escrever o nome da função
seguido por parênteses ().
Sintaxe: nome_da_funcao()
Se a função tivesse sido definida para aceitar informações externas (parâmetros, que
veremos na Seção 10.4), nós colocaríamos os valores correspondentes (argumentos) dentro
desses parênteses. Por enquanto, vamos nos concentrar em chamar funções que foram
definidas sem parâmetros, como a nossa saudacao_inicial() do exemplo anterior.
214 Capítulo 10. Definindo e Usando Funções

O Fluxo de Execução Durante uma Chamada de Função


Quando o interpretador Python encontra uma chamada de função no seu código, o
fluxo de execução do programa é alterado da seguinte maneira:

1. A execução do código no ponto onde a função foi chamada é temporariamente


pausada.

2. O controle do programa “pula” para a primeira linha de código dentro do corpo


da função que foi definida anteriormente.

3. Todas as instruções dentro do corpo da função são executadas em sequência.

4. Apósaúltima instrução do corpo da função ser executada (ou se uma instrução return
for encontrada, como veremos na Seção 10.5), o controle do programa “retorna”
para o ponto exato logo após onde a função foi originalmente chamada.

5. O programa então continua sua execução a partir daquele ponto.

Exemplo Prático: Chamando a Nossa Função saudacao_inicial


Vamos relembrar a definição da função saudacao_inicial que criamos na Seção 10.2:
1 # Defini ç ã o da fun ç ã o ( relembrando da Se ç ã o 10.2)
2 def saudacao_inicial () :
3 # Este é o corpo da fun ç ã o
4 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " )
5 print ( " Bem - vindo ( a ) ao Programa Exemplo ! " )
6 print ( " Esta fun ç ã o imprime uma sauda ç ã o . " )
7 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " )

Código 10.3: Definição da função saudacao_inicial (relembrando)


Até aqui, apenas definimos a função. Para que as mensagens sejam impressas, precisamos
chamá-la:
1 # Agora vamos CHAMAR ( invocar ) a fun ç ã o que definimos
2
3 print ( " Mensagem ANTES de chamar a fun ç ã o pela primeira vez ... " )
4
5 saudacao_inicial () # Primeira chamada à fun ç ã o
6
7 print ( " Mensagem AP Ó S a primeira chamada da fun ç ã o ter terminado . " )
8

9 print ( " \ nPodemos chamar a mesma fun ç ã o quantas vezes quisermos : " )
10 saudacao_inicial () # Segunda chamada
11 saudacao_inicial () # Terceira chamada
12
13 print ( " \ nFim do programa , ap ó s todas as chamadas de fun ç ã o . " )

Código 10.4: Chamando a função saudacao_inicial várias vezes


Saída Esperada do Código Acima:

Mensagem ANTES de chamar a função pela primeira vez...


-------------------------------------
Bem-vindo(a) ao Programa Exemplo!
10.3. Chamando (Invocando) uma Função: Colocando o Código em Ação 215

Esta função imprime uma saudação.


-------------------------------------
Mensagem APÓS a primeira chamada da função ter terminado.

Podemos chamar a mesma função quantas vezes quisermos:


-------------------------------------
Bem-vindo(a) ao Programa Exemplo!
Esta função imprime uma saudação.
-------------------------------------
-------------------------------------
Bem-vindo(a) ao Programa Exemplo!
Esta função imprime uma saudação.
-------------------------------------

Fim do programa, após todas as chamadas de função.

Observe como o bloco de código dentro de saudacao_inicial() foi executado a cada vez
que saudacao_inicial() foi escrita no código.

Benefício da Reutilização em Ação


O exemplo acima ilustra claramente um dos maiores benefícios das funções: a reuti-
lização de código. Em vez de escrevermos as quatro instruções print() da saudação
três vezes, nós as definimos uma única vez dentro da função e depois apenas chamamos a
função saudacao_inicial() três vezes.

Funções Devem Ser Definidas Antes de Serem Chamadas


Em Python, quando o interpretador lê seu código de cima para baixo, uma função
geralmente precisa ser definida antes do ponto no código onde ela é chamada pela
primeira vez. Se você tentar chamar uma função que o Python ainda não “conhece”, você
receberá um erro chamado NameError.
1 # Tentativa de chamar uma fun ç ã o antes de sua defini ç ã o ( GERARIA
ERRO )
2
3 # m en s ag e m _i m po r t an t e () # NameError : name ’ me n sa g e m_ i mp o r ta n te ’ is
not defined
4
5 def m en s ag e m_ i m po r ta n t e () :
6 print ( " Esta é uma mensagem muito importante ! " )
7
8 # Agora , chamar a fun ç ã o funcionaria :
9 m e ns age m _i m p or t an t e () # Chamada v á lida ap ó s a defini ç ã o

Código 10.5: Erro ao chamar função não definida (exemplo conceitual)

Chamar uma função é o ato de colocar seu bloco de código para trabalhar. É uma
operação fundamental que nos permite estruturar nossos programas de forma modular e
aproveitar o poder da reutilização que as funções oferecem. Na próxima seção, veremos
como tornar nossas funções ainda mais flexíveis e poderosas, permitindo que elas recebam
informações através de parâmetros.
216 Capítulo 10. Definindo e Usando Funções

10.4 Parâmetros e Argumentos: Passando Informações


para Funções
As funções que definimos até agora, como a saudacao_inicial(), são um pouco
limitadas porque executam sempre exatamente a mesma tarefa. Para que as funções se
tornem verdadeiramente ferramentas flexíveis e reutilizáveis, precisamos de uma maneira
de passar informações para dentro delas quando as chamamos. É aqui que entram os
conceitos de parâmetros e argumentos.

• Parâmetros: São como “variáveis especiais” que você declara na definição da


função, dentro dos parênteses. Eles atuam como espaços reservados (placeholders)
para os valores que a função espera receber.

• Argumentos: São os valores reais que você envia para a função quando a chama.
Esses valores são atribuídos aos parâmetros correspondentes.

Pense em uma receita: os parâmetros seriam os nomes genéricos dos ingredientes (ex:
quantidade_de_farinha); os argumentos seriam as quantidades específicas que você
usa (ex: 2 xícaras).

Definindo Funções com Parâmetros


A sintaxe para definir uma função com parâmetros é:

def nome_da_funcao(parametro1, parametro2, parametro3, ...):


# Corpo da função, onde você pode usar parametro1, parametro2, etc.
# ...

Dentro dos parênteses, você lista os nomes dos parâmetros, separados por vírgulas.

Chamando Funções com Argumentos (Argumentos Posicionais)


Quando você chama uma função definida com parâmetros, você fornece os argumentos
dentro dos parênteses da chamada:

nome_da_funcao(argumento1, argumento2, argumento3, ...)

Na forma mais básica, chamada de argumentos posicionais, os argumentos são atribuídos


aos parâmetros com base em sua posição. O primeiro argumento vai para o primeiro
parâmetro, o segundo para o segundo, e assim por diante. O número de argumentos
geralmente deve corresponder ao número de parâmetros.

Exemplos Práticos
• Exemplo 1: Função de saudação personalizada
1 # Definindo uma fun ç ã o com um par â metro chamado
’ nome_da_pessoa ’
2 def c u mp r im e n ta r _p e s so a ( nome_da_pessoa ) :
3 # ’ nome_da_pessoa ’ é o par â metro .
4 print ( f " Ol á , { nome_da_pessoa }! Seja muito
bem - vindo ( a ) . " )
10.4. Parâmetros e Argumentos: Passando Informações para Funções 217

5
6 # Chamando a fun ç ã o com diferentes argumentos
7 cu m pr i me n t ar _ pe s s oa ( " Carlos " ) # " Carlos " é o argumento
8 cu m pr i me n t ar _ pe s s oa ( " Mariana " )
9
10 visitante_atual = " Jos é "
11 c u m pr i me n t ar _ pe s so a ( visitante_atual )

Código 10.6: Função de saudação com parâmetro

• Exemplo 2: Função para somar dois números


1 # Definindo uma fun ç ã o com dois par â metros : n1 e n2
2 def m o s t r a r _ s o m a _ d o i s _ n u m e r o s ( n1 , n2 ) :
3 soma_calculada = n1 + n2
4 print ( f " A soma de { n1 } e { n2 } é igual a
{ soma_calculada }. " )
5
6 # Chamando a fun ç ã o com diferentes pares de argumentos
7 m o s t r a r _ s o m a _ d o i s _ n u m e r o s (15 , 10)
8 m o s t r a r _ s o m a _ d o i s _ n u m e r o s (7.2 , 2.8)
9
10 primeiro_valor = 100
11 segundo_valor = 550
12 m o s t r a r _ s o m a _ d o i s _ n u m e r o s ( primeiro_valor , segundo_valor )

Código 10.7: Função para somar dois números (com parâmetros)

• Exemplo 3: Função para exibir informações de um produto


1 def e x i b i r _ d e t a l h e s _ d o _ p r o d u t o ( nome_item , preco_item ,
categoria_item ) :
2 print ( " --- Detalhes do Produto ---" )
3 print ( f " Nome : { nome_item } " )
4 print ( f " Pre ç o : R$ { preco_item :.2 f } " )
5 print ( f " Categoria : { categoria_item } " )
6 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - " )
7
8 e x i b i r _ d e t a l h e s _ d o _ p r o d u t o ( " Notebook Pro Max " , 7500.00 ,
" Eletr ô nicos " )
9 print ()
10 e x i b i r _ d e t a l h e s _ d o _ p r o d u t o ( " Cafeteira SuperAroma " , 129.90 ,
" Eletrodom é sticos " )

Código 10.8: Função para exibir detalhes de um produto

Parâmetros são Variáveis Locais da Função


Os parâmetros definidos na assinatura de uma função se comportam como variáveis
locais dentro do corpo daquela função. Seus valores são inicializados com os argumentos
passados durante a chamada. Essas variáveis de parâmetro só existem enquanto a função
está sendo executada.

A Importância da Ordem para Argumentos Posicionais


Com argumentos posicionais, a ordem em que você passa os argumentos é crucial,
pois determina a qual parâmetro cada valor será atribuído.
218 Capítulo 10. Definindo e Usando Funções

1 def subtrair_numeros ( minuendo , subtraendo ) :


2 # Esta fun ç ã o calcula minuendo - subtraendo
3 resultado = minuendo - subtraendo
4 print ( f " { minuendo } - { subtraendo } = { resultado } " )
5
6 subtrair_numeros (10 , 3) # minuendo =10 , subtraendo =3. Sa í da : 10 - 3
= 7
7 subtrair_numeros (3 , 10) # minuendo =3 , subtraendo =10. Sa í da : 3 - 10
= -7

Código 10.9: Importância da ordem dos argumentos posicionais


A inversão da ordem dos argumentos levou a um resultado diferente.
Os parâmetros e argumentos são o mecanismo fundamental pelo qual tornamos nossas
funções flexíveis e capazes de operar sobre diferentes dados a cada chamada. Na próxima
seção, veremos como as funções podem “comunicar de volta”, ou seja, como elas podem
retornar resultados para o código que as chamou.

10.5 Retornando Valores de Funções: A Instrução return


Até agora, as funções que criamos, como cumprimentar_pessoa() ou
mostrar_soma_dois_numeros(), realizaram ações – elas imprimiram algo no console. No
entanto, elas não “devolveram” nenhum resultado que pudesse ser armazenado em uma
variável ou usado em outros cálculos no local onde foram chamadas.
Para que as funções se tornem ainda mais úteis e se integrem melhor em expressões e
lógicas mais complexas, elas precisam de uma maneira de enviar um valor de volta para o
código que as invocou. Em Python, isso é feito usando a instrução return.

A Instrução return
A sintaxe da instrução return é simples:
return valor_ou_expressao
Quando o interpretador Python encontra uma instrução return dentro de uma função:
1. A valor_ou_expressao (se houver) é avaliada.
2. A execução da função termina imediatamente. Qualquer código restante na
função após o return não é executado.
3. O valor resultante é enviado de volta (retornado) para o local onde a função foi
chamada.
O valor retornado pode ser de qualquer tipo de dado Python.

Recebendo e Usando o Valor Retornado


O valor retornado por uma função pode ser:
• Atribuído a uma variável: resultado = minha_funcao()
• Usado diretamente em uma expressão: print(minha_funcao() * 2)
• Passado como argumento para outra função: outra_funcao(minha_funcao())
10.5. Retornando Valores de Funções: A Instrução return 219

Exemplos Práticos
• Exemplo 1: Função que calcula e retorna a área de um retângulo
1 def c a l c u l a r _ a r e a _ r e t a n g u l o ( base , altura ) :
2 " " " Calcula a á rea de um ret â ngulo e retorna o
resultado . " " "
3 if base < 0 or altura < 0:
4 print ( " Aviso : Base e altura devem ser positivas . " )
5 return None # Retorna None se a entrada for
inv á lida
6 area = base * altura
7 return area # Retorna o valor calculado
8
9 b1 = 10
10 h1 = 5
11 area1 = c a l c u l a r _ a r e a _ r e t a n g u l o ( b1 , h1 )
12 if area1 is not None :
13 print ( f " A á rea de um ret â ngulo com base { b1 } e altura
{ h1 } é { area1 }. " )
14

15 area_invalida = c a l c u l a r _ a r e a _ r e t a n g u l o ( -5 , 10)
16 if area_invalida is None :
17 print ( " N ã o foi poss í vel calcular a á rea com valores
inv á lidos . " )

Código 10.10: Função retornando a área de um retângulo

• Exemplo 2: Função que verifica se um número é par


1 def eh_par ( numero ) :
2 " " " Verifica se um n ú mero é par . Retorna True se for
par , False caso contr á rio . " " "
3 return numero % 2 == 0 # Retorna diretamente o
resultado da express ã o booleana
4
5 num1 = 10
6 if eh_par ( num1 ) :
7 print ( f " O n ú mero { num1 } é par . " )
8 else :
9 print ( f " O n ú mero { num1 } é í mpar . " )

Código 10.11: Função retornando um valor booleano

• Exemplo 3: Função que retorna uma string formatada


1 def c r i a r _ n o m e _ f o r m a t a d o _ i n v e r s o ( primeiro_nome , sobrenome ) :
2 " " " Retorna o nome completo formatado como ’ Sobrenome ,
PrimeiroNome ’. " " "
3 if not primeiro_nome or not sobrenome :
4 return " Entrada inv á lida : nome ou sobrenome
ausente . "
5 return f " { sobrenome . upper () } ,
{ primeiro_nome . capitalize () } "
6
7 nome_fmt1 = c r i a r _ n o m e _ f o r m a t a d o _ i n v e r s o ( " joana " , " d ’ arc " )
8 print ( nome_fmt1 )

Código 10.12: Função retornando uma string formatada


220 Capítulo 10. Definindo e Usando Funções

Funções sem Instrução return Explícita


Se uma função chega ao final do seu corpo sem encontrar uma instrução return
valor_ou_expressao, ou se ela tem um return sem nenhum valor após ela,afunção
automaticamente retorna o valor especial None.
1 def e x i b i r _ s a u d a c a o _ s e m _ r e t o r n o ( nome ) :
2 print ( f " Ol á , { nome }! Tenha um bom dia . " )
3 # N ã o h á instru ç ã o ’ return ’ aqui
4
5 valor_retornado = e x i b i r _ s a u d a c a o _ s e m _ r e t o r n o ( " Alan " )
6 print ( f " O valor retornado foi : { valor_retornado } " ) # Sa í da : None

Código 10.13: Função que retorna None implicitamente

Múltiplas Instruções return (Apenas Uma é Executada)


Uma função pode conter várias instruções return, geralmente dentro de diferentes
blocos condicionais. No entanto, assim queaprimeira instrução return é executada,afunção
termina imediatamente e retorna aquele valor.
A instrução return é essencial para que funções produzam resultados utilizáveis,
tornando o código mais modular e poderoso.

10.6 Docstrings (Strings de Documentação): Documen-


tando Suas Funções
Já falamos sobre a importância dos comentários (#) para adicionar notas e explicações
em nosso código. No entanto, Python possui um mecanismo mais formal e poderoso
para documentar o que suas funções (e também módulos, classes e métodos) fazem: as
docstrings, ou strings de documentação.
Uma docstring é uma string literal que serve como uma nota explicativa diretamente
associada ao objeto que ela documenta. Diferentemente dos comentários #, as docstrings
são armazenadas em um atributo especial do objeto e podem ser acessadas em tempo de
execução.

O que é uma Docstring?


Uma docstring é definida como uma string que aparece como a primeira instrução
logo após a linha de definição de uma função (ou módulo, classe ou método).
A convenção é usar aspas triplas ("..." ou ”’...”’) para delimitar a docstring,
permitindo que a documentação se estenda por múltiplas linhas.
1 def m i n h a _ f u n c a o _ ex e m p l o ( parametro1 , parametro2 ) :
2 " " " Esta é uma docstring .
3
4 Ela explica o que a fun ç ã o faz , quais s ã o seus par â metros
5 e o que ela retorna . Esta é uma docstring de m ú ltiplas linhas .
6 """
7 # Corpo da fun ç ã o aqui ...
8 resultado = parametro1 + parametro2
9 return resultado
10
10.6. Docstrings (Strings de Documentação): Documentando Suas Funções 221

11 def o u t r a _ f u n c a o _s i m p l e s () :
12 ’ ’ ’ Docstring de linha ú nica tamb é m é poss í vel com aspas
triplas . ’ ’ ’
13 print ( " Fazendo algo simples . " )

Código 10.14: Exemplo de definição de função com docstring

Por que Usar Docstrings?


Escrever boas docstrings é uma prática fundamental por várias razões:
• Documentação para Outros (e para o seu “Eu do Futuro”): Ajudam a
entender o propósito e o uso de uma função sem precisar ler todo o seu código
interno.

• Acesso em Tempo de Execução: Docstrings são armazenadas no atributo especial


__doc__ da função (ex: minha_funcao_exemplo.__doc__).

• Ferramentas de Ajuda Integradas: A função help() do Python usa a docstring


para exibir informações sobre a função.

• Geração Automática de Documentação: Ferramentas como Sphinx podem


extrair docstrings para gerar documentação completa do projeto.

Como Escrever Boas Docstrings (Estilo “Descomplicado”)


Uma boa docstring geralmente inclui:
1. Resumo Conciso (Primeira Linha): Um resumo breve e imperativo do que a
função faz (ex: “Calcula a área...”), começando com maiúscula e terminando com
ponto. Se for multilinha, esta linha fica sozinha, seguida por uma linha em branco.

2. Explicação Mais Detalhada (Opcional): Parágrafos adicionais explicando a


lógica ou considerações importantes.

3. Documentando Parâmetros (Args): Descrever cada parâmetro. Um formato


comum:

Args:
nome_parametro1 (tipo): Descrição do parâmetro.
nome_parametro2 (tipo): Descrição do outro parâmetro.

4. Documentando o Valor de Retorno (Returns): Descrever o que a função


retorna. Um formato comum:

Returns:
tipo_retornado: Descrição do valor que a função retorna.

5. Documentando Erros/Exceções (Raises, Opcional para iniciantes): Se a


função pode levantar exceções específicas.
222 Capítulo 10. Definindo e Usando Funções

Exemplos de Funções com Docstrings:


• Exemplo 1: Função calcular_media_notas
1 def c a l c u l a r _ m e d i a _ n o t a s ( nota1 , nota2 , nota3 ) :
2 " " " Calcula e retorna a m é dia aritm é tica de tr ê s notas .
3
4 Args :
5 nota1 ( float ou int ) : O valor da primeira nota .
6 nota2 ( float ou int ) : O valor da segunda nota .
7 nota3 ( float ou int ) : O valor da terceira nota .
8
9 Returns :
10 float : A m é dia calculada das tr ê s notas .
11 Retorna None se alguma nota for inv á lida ( ex :
negativa ) .
12 """
13 if nota1 < 0 or nota2 < 0 or nota3 < 0:
14 print ( " Aviso : As notas devem ser valores n ã o
negativos . " )
15 return None
16
17 media = ( nota1 + nota2 + nota3 ) / 3
18 return media

Código 10.15: Função para calcular média com docstring

• Exemplo 2: Função saudacao_completa


1 def saudaca o_complet a ( nome , periodo_dia = " dia " ) :
2 " " " Gera uma sauda ç ã o personalizada para uma pessoa .
3
4 Args :
5 nome ( str ) : O nome da pessoa a ser saudada .
6 periodo_dia ( str , opcional ) : " dia " , " tarde " , ou
" noite " .
7 Padr ã o é " dia " .
8
9 Returns :
10 str : Uma string contendo a sauda ç ã o formatada .
11 """
12 if not isinstance ( nome , str ) or not nome . strip () :
13 return " Nome inv á lido fornecido . "
14
15 saudacao_base = f " Ol á , { nome . capitalize () }! "
16
17 if periodo_dia . lower () == " tarde " :
18 return f " { saudacao_base } Tenha uma boa tarde . "
19 elif periodo_dia . lower () == " noite " :
20 return f " { saudacao_base } Tenha uma boa noite . "
21 else :
22 return f " { saudacao_base } Tenha um bom dia . "

Código 10.16: Função de saudação com docstring e parâmetro opcional

Acessando Docstrings
Você pode acessar a docstring de uma função com:
10.7. Escopo de Variáveis: Local vs. Global 223

1. help(nome_da_funcao)

2. print(nome_da_funcao.__doc__) (dois underscores antes e depois de doc)

Concluindo sobre Docstrings


As docstrings são uma parte essencial da escrita de bom código Python. Elas melhoram
a compreensibilidade, facilitam a manutenção e são a base para a documentação automática.
Crie o hábito de escrever docstrings claras para todas as suas funções.

10.7 Escopo de Variáveis: Local vs. Global


À medida que nossos programas crescem e começamos a usar funções para organizar
nosso código, surge uma questão importante: uma variável criada em uma parte do
programa está acessível em todas as outras partes? A resposta é: nem sempre. O local
onde você define uma variável determina onde ela pode ser usada. Esse “alcance” ou “região
de visibilidade” de uma variável é chamado de escopo.
Entender o escopo das variáveis é crucial para evitar erros comuns (como o NameError)
e para escrever um código mais organizado e previsível. Em Python, os dois principais
tipos de escopo com os quais lidaremos inicialmente são o escopo local e o escopo
global.

Escopo Local (Variáveis Locais)


• O que são? Variáveis definidas (recebem um valor pela primeira vez) dentro de
uma função são chamadas de variáveis locais. Isso inclui os parâmetros da função.

• Visibilidade: Só existem e são acessíveis dentro da função onde foram definidas.

• Tempo de Vida: São criadas quando a função é chamada e destruídas quando a


função termina.

1 # Exemplo de escopo local


2 def c a l c u l a r _ i m p o s t o _ p r o d u t o ( preco_base ) :
3 # ’ preco_base ’ é um par â metro , portanto , uma vari á vel local
4 taxa_imposto = 0.05 # ’ taxa_imposto ’ é uma vari á vel local
5 imposto_devido = preco_base * taxa_imposto
6 print ( f " Dentro da fun ç ã o : Imposto = R$ { imposto_devido :.2 f } " )
7 return imposto_devido
8
9 imposto_p roduto_a = c a l c u l a r _ i m p o s t o _ p r o d u t o (100.0)
10 print ( f " Imposto para o produto A : R$ { im posto_pr oduto_a :.2 f } " )
11
12 # Tentativa de acessar ’ taxa_imposto ’ aqui fora GERARIA NameError
13 # print ( taxa_imposto )

Código 10.17: Exemplo de escopo local de variáveis


224 Capítulo 10. Definindo e Usando Funções

Escopo Global (Variáveis Globais)


• O que são? Variáveis definidas fora de todas as funções, no nível principal do script,
são chamadas de variáveis globais.

• Visibilidade: Podem ser lidas de qualquer lugar do script, inclusive de dentro das
funções. Para modificá-las de dentro de uma função, usa-se a palavra-chave global.

1 # Exemplo de escopo global


2 PI_APROXIMADO = 3.14159 # Vari á vel global
3 autor_do_livro = " Alan Afif Helal "
4
5 def c a l c u l a r _ a r e a _ c i r c u l o _ g l o b a l ( raio ) :
6 # A fun ç ã o pode LER as vari á veis globais
7 area = PI_APROXIMADO * ( raio ** 2)
8 print ( f " C í rculo de raio { raio } tem á rea { area :.2 f }. " )
9 print ( f " Autor : { autor_do_livro }. " )
10 return area
11
12 area_calculada = c a l c u l a r _ a r e a _ c i r c u l o _ g l o b a l (10)
13 print ( f " Valor de PI fora da fun ç ã o : { PI_APROXIMADO } " )

Código 10.18: Acessando variáveis globais de dentro de uma função

Sombreamento de Variáveis (Shadowing)


Se você define uma variável local com o mesmo nome de uma variável global, a
variável local “sombra” (esconde) a global dentro do escopo daquela função.
1 mensagem_status = " Sistema operando normalmente . ( Global ) "
2
3 def v e r i f i c a r _ s t a t u s _ i n t e r n o () :
4 # Esta atribui ç ã o CRIA uma NOVA vari á vel LOCAL ’ mensagem_status ’
5 mensagem_status = " Verificando subsistemas ... ( Local ) "
6 print ( f " Dentro da fun ç ão , mensagem_status é :
’{ mensagem_status } ’ " )
7
8 v e r i f i c a r _ s t a t u s _ i n t e r n o ()
9 print ( f " Fora da fun ç ão , mensagem_status ainda é :
’{ mensagem_status } ’ " )

Código 10.19: Exemplo de sombreamento de variáveis

Modificando Variáveis Globais de Dentro de uma Função: A


Palavra-chave global
Para modificar uma variável global de dentro de uma função, você deve declará-la com
a palavra-chave global no início da função. Sintaxe: global nome_da_variavel_global
1 c o nt ado r _d e _ ac e ss o s = 0 # Vari á vel global
2
3 def registrar_acesso () :
4 global c o nt a do r _d e _ ac e ss o s # Avisa que usaremos a global
5 co n ta d or _ d e_ a ce s s os += 1
6 print ( f " Dentro da fun ç ão , acessos : { c o n ta d or _ d e_ a ce s so s } " )
10.8. Argumentos com Valores Padrão (Default Arguments) 225

7
8 print ( f " Acessos antes : { c on t ad o r_ d e _a c es s o s } " )
9 registrar_acesso ()
10 registrar_acesso ()
11 print ( f " Acessos depois ( global alterada ) : { co n t ad o r_ d e_ a c es s os } " )
12

13 # O que acontece SEM ’ global ’:


14 outro_contador = 100
15 def t e n t a r _ a l t e r a r _ s e m _ k e y w o r d _ g l o b a l () :
16 # Python cria uma LOCAL ’ outro_contador ’ aqui
17 outro_contador = 50
18 print ( f " Dentro da fun ç ã o sem ’ global ’, outro_contador :
{ outro_contador } " )
19
20 t e n t a r _ a l t e r a r _ s e m _ k e y w o r d _ g l o b a l ()
21 print ( f " Fora , ’ outro_contador ’ ainda é ( global inalterada ) :
{ outro_contador } " )

Código 10.20: Modificando variável global com global

Uso da Palavra-chave global: Com Moderação! Modificar variáveis globais de


dentro de funções pode tornar o código difícil de rastrear. É preferível que funções recebam
dados via parâmetros e retornem resultados.

Regra LEGB (Breve Menção)


Python procura variáveis na ordem: Local, escopos de Enclosing functions (funções
aninhadas), Global, e Built-in (embutidos). Focar em Local e Global é suficiente para
começar.
Entender o escopo é crucial para evitar erros e gerenciar dados corretamente.

10.8 Argumentos com Valores Padrão (Default Argu-


ments)
Até agora, quando definimos funções com parâmetros, todos eles eram obrigatórios. No
entanto, há situações em que gostaríamos que alguns parâmetros de uma função tivessem
um valor “comum” ou “padrão”, que seria usado caso nenhum argumento específico fosse
fornecido para eles durante a chamada da função. Isso torna a função mais flexível, pois
alguns de seus argumentos se tornam opcionais. Python nos permite fazer isso através
dos argumentos com valores padrão.

Definindo Parâmetros com Valores Padrão


Para definir um valor padrão para um parâmetro, você simplesmente atribui esse
valor ao parâmetro na própria linha de definição da função (def), usando o operador de
atribuição (=). A sintaxe geral é:

def nome_da_funcao(param1, param2="valor_padrao", param3=0):


# Corpo da função
# ...
226 Capítulo 10. Definindo e Usando Funções

Regra Importante de Ordem: Todos os parâmetros com valores padrão devem vir
após quaisquer parâmetros que não tenham valores padrão.
1 # Defini ç ã o CORRETA
2 def e x e m p l o _ o r d e m _ c o r r e t a ( obrig , opcional = " padr ã o " ) :
3 pass
4
5 # DEFINI Ç Ã O INCORRETA ( GERARIA SyntaxError )
6 # def e x e m p l o _ or d e m _ e r r a d a ( opcional =" padr ã o " , o br ig ato ri o_ dep oi s ) :
7 # pass
8 # # SyntaxError : non - default argument follows default argument

Código 10.21: Ordem correta e incorreta de parâmetros padrão

Como Funciona na Chamada da Função


Quando você chama a função:

• Se você fornecer um argumento para um parâmetro com valor padrão, o valor do


argumento fornecido será usado.

• Se você não fornecer um argumento, o valor padrão definido na assinatura da função


será usado.

Exemplos Práticos
• Exemplo 1: Função de saudação com mensagem padrão
1 def saudar_pessoa ( nome_da_pessoa ,
m e n s a g e m _ d e _ sa u d a c a o = " Ol á " ) :
2 " " " Sa ú da uma pessoa com uma mensagem opcional . " " "
3 print ( f " { m e n s a g em _ d e _ s a u d a c a o } ,
{ nome_da_pessoa . capitalize () }! " )
4

5 saudar_pessoa ( " ana " ) # Usa o padr ã o " Ol á "


6 saudar_pessoa ( " bruno " , " Bom dia " ) # Sobrescreve o padr ã o

Código 10.22: Função de saudação com mensagem padrão

• Exemplo 2: Função para calcular potência, com expoente padrão 2


1 def c a l c u l a r _ p o t e n c i a _ c o m _ p a d r a o ( base , expoente =2) :
2 " " " Calcula ’ base ’ elevada ao ’ expoente ’. Padr ã o é ao
quadrado . " " "
3 resultado = base ** expoente
4 print ( f " { base } elevado a { expoente } é igual a
{ resultado }. " )
5 return resultado
6
7 quadrado_de_7 = c a l c u l a r _ p o t e n c i a _ c o m _ p a d r a o (7)
8 cubo_de_4 = c a l c u l a r _ p o t e n c i a _ c o m _ p a d r a o (4 , 3)

Código 10.23: Função de potência com expoente padrão

• Exemplo 3: Função com múltiplos parâmetros padrão


10.8. Argumentos com Valores Padrão (Default Arguments) 227

1 def c o n f i g u r a r _ d i s p o s i t i v o ( nome_dispositivo ,
porta = " / dev / ttyS0 " , baud_rate =9600) :
2 " " " Configura um dispositivo serial ( simula ç ã o ) . " " "
3 print ( f " Configurando : { nome_dispositivo } na porta
{ porta } a { baud_rate } bps . " )
4 return True
5
6 c o n f i g u r a r _ d i s p o s i t i v o ( " SensorTempA " ) # Usa porta e
baud_rate padr ã o
7 c o n f i g u r a r _ d i s p o s i t i v o ( " ModuloGPS_B " ,
porta = " / dev / ttyUSB0 " , baud_rate =115200)

Código 10.24: Função com múltiplos parâmetros padrão

Cuidado Importante: Valores Padrão Mutáveis


Se o valor padrão de um parâmetro é um objeto mutável (como uma lista []), esse
objeto padrão é criado apenas uma vez, quando a função é definida. Modificações nesse
objeto persistirão entre chamadas.
1 # Exemplo do PROBLEMA com valor padr ã o mut á vel
2 def a d i c i o n a r _ a _ l i s t a _ p a d r a o _ p r o b l e m a ( item , minha_lista =[]) : # N Ã O
FA Ç A DESTA FORMA !
3 minha_lista . append ( item )
4 print ( f " Lista agora : { minha_lista } " )
5 return minha_lista
6

7 print ( " Testando fun ç ã o com lista padr ã o problem á tica : " )
8 lista1 = a d i c i o n a r _ a _ l i s t a _ p a d r a o _ p r o b l e m a (1) # Sa í da : [1]
9 lista2 = a d i c i o n a r _ a _ l i s t a _ p a d r a o _ p r o b l e m a (2) # Sa í da : [1 , 2]
( inesperado !)

Código 10.25: Problema com valor padrão mutável (lista)


A Solução Correta: Use None como valor padrão e crie o objeto mutável dentro da
função.
1 # Forma CORRETA de usar valor padr ã o para tipos mut á veis
2 def a d i c i o n a r _ a _ l i s t a _ p a d r a o _ c o r r e t o ( item ,
m in h a _l i st a _c o r re t a = None ) :
3 if m i nh a _ li s ta _ co r r et a is None :
4 m in h a_ l i st a _c o r re t a = [] # Cria uma NOVA lista se nenhuma
for passada
5 mi n ha _ li s t a_ c or r e ta . append ( item )
6 print ( f " Lista agora : { m in h a _l i st a _c o r re t a } " )
7 return m i nh a _l i st a _ co r re t a
8

9 print ( " \ nTestando fun ç ã o com lista padr ã o correta : " )


10 lista_c1 = a d i c i o n a r _ a _ l i s t a _ p a d r a o _ c o r r e t o (10)
11 lista_c2 = a d i c i o n a r _ a _ l i s t a _ p a d r a o _ c o r r e t o (20)
12 print ( f " Lista C1 final : { lista_c1 } " ) # Sa í da : [10]
13 print ( f " Lista C2 final : { lista_c2 } " ) # Sa í da : [20]

Código 10.26: Solução para valor padrão mutável


Os argumentos com valores padrão aumentam a flexibilidade das suas funções. Apenas
lembre-se da regra de ordem e do cuidado com valores padrão mutáveis.
228 Capítulo 10. Definindo e Usando Funções

10.9 Argumentos Nomeados (Keyword Arguments)


Nas seções anteriores, aprendemos a passar informações para funções usando argumentos
posicionais e também vimos como definir valores padrão para tornar alguns parâmetros
opcionais. Python oferece mais uma maneira de passar argumentos para uma função:
usando argumentos nomeados (ou keyword arguments). Com esta técnica, ao chamar
uma função, você especifica explicitamente a qual parâmetro cada argumento se destina,
usando o nome do parâmetro seguido de um sinal de igual (=) e o valor do argumento.

Sintaxe dos Argumentos Nomeados na Chamada da Função


A sintaxe é simples:

nome_da_funcao(nome_parametro1=valor1, nome_parametro2=valor2, ...)

O nome_parametroX deve corresponder exatamente ao nome de um parâmetro definido


na assinatura da função.

Principais Vantagens dos Argumentos Nomeados


1. A Ordem Não Importa (para os Argumentos Nomeados): Uma vez que você
nomeia um argumento, sua posição na chamada não precisa corresponder à posição
do parâmetro na definição.

2. Clareza e Legibilidade Aumentadas: Para funções com muitos parâmetros, ou


com vários parâmetros opcionais, usar argumentos nomeados torna a chamada muito
mais fácil de entender.

3. Flexibilidade ao Usar com Valores Padrão: Permite que você forneça argu-
mentos apenas para os parâmetros opcionais que deseja alterar, nomeando-os.

Exemplos Práticos
• Exemplo 1: Usando com a função configurar_dispositivo
1 def c o n f i g u r a r _ d i s p o s i t i v o ( nome_dispositivo ,
porta_serial = " / dev / ttyS0 " , baud_rate =9600 ,
timeout_segundos =5) :
2 " " " Configura um dispositivo serial ( simula ç ã o ) . " " "
3 print ( f " Configurando : { nome_dispositivo } " )
4 print ( f " Porta : { porta_serial } , Baud Rate :
{ baud_rate } bps , Timeout : { timeout_segundos } s " )
5 return True
6

7 print ( " Usando argumentos nomeados para configurar


dispositivos : " )
8 # A ordem dos argumentos nomeados n ã o precisa seguir a
ordem dos par â metros
9 c o n f i g u r a r _ d i s p o s i t i v o ( nome_dispositivo = " SensorClima " ,
timeout_segundos =15 , porta_serial = " / dev / ttyACM0 " )
10 # ’ baud_rate ’ usar á seu valor padr ã o (9600)
11
12 print ( " -" * 20)
10.9. Argumentos Nomeados (Keyword Arguments) 229

13 c o n f i g u r a r _ d i s p o s i t i v o ( baud_rate =115200 ,
nome_dispositivo = " PlacaDeControle " )
14 # ’ porta_serial ’ e ’ timeout_segundos ’ usar ã o seus valores
padr ã o .

Código 10.27: Usando argumentos nomeados com valores padrão

• Exemplo 2: Uma função para descrever uma pessoa


1 def d e sc r ev e r _i n di v i du o ( nome , idade , cidade = " N ã o
Informada " , profissao = " N ã o Informada " ,
hobby_principal = " N ã o Informado " ) :
2 " " " Descreve uma pessoa com base nas informa ç õ es
fornecidas . " " "
3 print ( f " \n - - - Ficha de { nome . capitalize () } ---" )
4 print ( f " Idade : { idade } anos " )
5 print ( f " Cidade : { cidade } " )
6 print ( f " Profiss ã o : { profissao } " )
7 print ( f " Hobby Principal : { hobby_principal } " )
8

9 de s cr e ve r _ in d iv i d uo ( " Jo ã o " , 30) # Apenas obrigat ó rios


10 d e s cr e ve r _ in d iv i du o ( " Maria " , 25 , profissao = " Engenheira de
Software " , cidade = " S ã o Paulo " )
11 d e s cr e ve r _ in d iv i du o ( hobby_principal = " Fotografia " ,
nome = " Carlos " , idade =42 , cidade = " Curitiba " ) # Ordem dos
nomeados n ã o importa

Código 10.28: Função com muitos parâmetros opcionais e argumentos nomeados

Misturando Argumentos Posicionais e Nomeados


É possível misturar os dois tipos de argumentos. No entanto, há uma regra crucial:
Todos os argumentos posicionais devem vir ANTES de quaisquer argumentos
nomeados.
1 def formata r_mensage m ( texto_base , prefixo = " INFO : " , sufixo = "
FIM . " , a pl ica r_ ma ius cu la s = False ) :
2 " " " Formata uma mensagem . " " "
3 mensagem = prefixo + texto_base + sufixo
4 if a pl ic ar_ ma iu scu la s :
5 mensagem = mensagem . upper ()
6 print ( mensagem )
7
8 # Chamada v á lida misturando tipos :
9 formatar_ mensagem ( " Alerta de sistema " , ap lic ar _m aiu sc ul as = True )
10 f ormatar_ mensagem ( " Nova tarefa " , " TAREFA : " , sufixo = " ( CONCLU Í DA ) " )
11

12 # Chamada INV Á LIDA ( SyntaxError ) :


13 # fo rmatar_m ensagem ( prefixo =" ERRO : " , " Falha cr í tica ")

Código 10.29: Misturando argumentos posicionais e nomeados

Quando Usar Argumentos Nomeados?


• Quando uma função tem muitos parâmetros, especialmente se opcionais.
230 Capítulo 10. Definindo e Usando Funções

• Para melhorar a legibilidade da chamada da função.

• Para passar argumentos para parâmetros opcionais específicos sem se preocupar com
a ordem dos outros.

Os argumentos nomeados são uma ferramenta poderosa em Python que promove a clareza
e a flexibilidade ao chamar funções.

10.10 Argumentos Opcionais (com Valores Padrão) vs.


Argumentos Nomeados
Nas seções anteriores, exploramos os argumentos com valores padrão (Seção 10.8),
que tornam parâmetros opcionais, e os argumentos nomeados (Seção 10.9), que oferecem
clareza e flexibilidade na chamada de funções. Embora relacionados à forma como funções
são chamadas, eles servem a propósitos distintos, mas complementares. Esta seção visa
esclarecer essas diferenças.

Revisão Rápida dos Conceitos-Chave


• Argumentos Opcionais (via Valores Padrão):

– Definidos na assinatura da função (ex: def func(param="default"):).


– Permitem omitir o argumento na chamada; se omitido, o valor padrão é usado.
– Foco: Tornar a passagem de certos valores não obrigatória.

• Argumentos Nomeados (Keyword Arguments):

– Usados na chamada da função (ex: func(param_nome=valor)).


– Especificam explicitamente a qual parâmetro um valor está sendo passado.
– Foco: Melhorar a clareza da chamada e permitir que argumentos nomeados
sejam passados fora de ordem.

Principais Diferenças e Relações


A distinção mais importante é:

• Valores Padrão estão ligados à definição da função e tornam certos parâmetros


opcionais.

• Argumentos Nomeados estão ligados à chamada da função e oferecem uma forma


de passar argumentos especificando o nome do parâmetro.

Eles podem (e frequentemente são) usados juntos, especialmente com funções que possuem
vários parâmetros com valores padrão.
10.10. Argumentos Opcionais (com Valores Padrão) vs. Argumentos Nomeados 231

Cenários e Exemplos para Ilustrar


• Cenário 1: Função com múltiplos parâmetros opcionais.
1 def g e r a r _ d o c u m e n t o _ t e x t o ( conteudo_principal ,
titulo_documento = " Documento " ,
2 autor_documento = " Desconhecido " ,
3 incluir_data = True , formato_data = " % d /% m /% Y " ) :
4 " " " Gera uma representa ç ã o textual de um documento
( simula ç ã o ) . " " "
5 print ( f " --- { titulo_documento . upper () } ---" )
6 print ( f " Autor : { autor_documento } " )
7 if incluir_data :
8 d at a _a t u al _ si m u la d a = " 23/05/2025 "
9 print ( f " Data ( formato { formato_data }) :
{ d a ta _ at u a l_ s im u l ad a } " )
10 print ( " \ nConte ú do : " )
11 print ( c on teu do _p ri nci pa l )
12 print ( " -" * 30)
13
14 print ( " RELAT Ó RIO 1 ( apenas obrigat ó rio ) : " )
15 g e r a r _ d o c u m e n t o _ t e x t o ( " Conte ú do do primeiro relat ó rio . " )
16
17 print ( " \ nRELAT Ó RIO 2 ( modificando alguns opcionais com
nomes ) : " )
18 gerar_documento_texto (
19 c ont eu do _p rin ci pa l = " An á lise dos resultados . " ,
20 titulo_documento = " Resultados Q1 " ,
21 autor_documento = " Equipe Financeira "
22 )
23
24 print ( " \ nRELAT Ó RIO 3 ( modificando outros opcionais com
nomes , fora de ordem ) : " )
25 gerar_documento_texto (
26 " Minhas anota ç õ es . " ,
27 autor_documento = " Meu Di á rio " ,
28 incluir_data = False
29 )

Código 10.30: Usando argumentos nomeados com parâmetros opcionais

Com argumentos nomeados, podemos especificar apenas os opcionais que queremos


mudar, sem nos preocuparmos com a ordem dos outros.

• Cenário 2: Argumento nomeado para um parâmetro obrigatório (para


clareza).
1 def c a l c u l a r _ v o l u m e _ p a r a l e l e p i p e d o ( comprimento , largura ,
altura ) :
2 return comprimento * largura * altura
3
4 # Usando argumentos nomeados para clareza ( ordem n ã o
importa para os nomeados )
5 vol = c a l c u l a r _ v o l u m e _ p a r a l e l e p i p e d o ( altura =2 ,
comprimento =10 , largura =5)
6 print ( f " Volume : { vol } " )

Código 10.31: Argumento nomeado para parâmetro obrigatório


232 Capítulo 10. Definindo e Usando Funções

Regra de Ouro: Posicionais Sempre Antes dos Nomeados


Relembrando: ao misturar argumentos posicionais e nomeados em uma chamada, todos
os argumentos posicionais devem vir primeiro.
1 def dados_impressao ( texto , vezes =1 , modo_urgente = False ) :
2 mensagem = texto * vezes
3 if modo_urgente :
4 mensagem = mensagem . upper () + " !!! "
5 print ( mensagem )
6
7 # V á lido :
8 dados_impressao ( " Alerta " , modo_urgente = True )
9

10 # INV Á LIDO ( SyntaxError ) :


11 # dados_impressao ( modo_urgente = True , " Alerta Errado ")

Código 10.32: Ordem correta ao misturar argumentos

Concluindo a Distinção
• Valores Padrão tornam parâmetros opcionais na definição da função.
• Argumentos Nomeados oferecem clareza e flexibilidade de ordem na chamada da
função.

Eles frequentemente trabalham bem juntos, especialmente para funções com várias opções
de configuração. Entender ambos leva a um uso mais eficaz e a um código Python mais
legível e robusto.

10.11 Funções Lambda (Funções Anônimas): Pequenas


Funções em Uma Linha
Além da maneira tradicional de definir funções usando a palavra-chave def, Python
oferece uma sintaxe mais curta para criar pequenas funções que não precisam de um
nome formal (são “anônimas”). Essas são chamadas de funções lambda ou expressões
lambda.
Elas são particularmente úteis quando você precisa de uma função simples para uma
tarefa rápida, muitas vezes para ser usada como argumento para outra função (como
sorted(), map() ou filter()).

O que é uma Função Lambda?


Uma função lambda é caracterizada por:
• Ser anônima (sem nome formal com def).
• Ser definida com a palavra-chave lambda.
• Poder receber qualquer número de argumentos, mas só pode conter uma única
expressão.
• O resultado da expressão é automaticamente retornado.
10.11. Funções Lambda (Funções Anônimas): Pequenas Funções em Uma Linha 233

Sintaxe da Função Lambda


A sintaxe geral é:
lambda argumentos: expressao

• lambda: A palavra-chave.
• argumentos: Um ou mais argumentos, separados por vírgula (ex: x, ou a, b).
• : (dois pontos): Separa os argumentos da expressão.
• expressao: Uma única expressão Python cujo resultado é retornado.

Como Usar Funções Lambda


• Atribuindo a uma Variável (Menos Comum):
1 # Fun ç ã o lambda para dobrar um n ú mero
2 dobrar_numero = lambda x : x * 2
3 print ( f " O dobro de 7 é : { dobrar_numero (7) } " ) # Sa í da : 14
4
5 # Fun ç ã o lambda para somar dois n ú meros
6 somar_valores = lambda a , b : a + b
7 print ( f " A soma de 10 e 5 é : { somar_valores (10 , 5) } " ) #
Sa í da : 15

Código 10.33: Atribuindo uma função lambda a uma variável

Se a função lambda for complexa ou reutilizada, def é geralmente mais claro.


• Uso Principal: Como Argumento para Funções de Ordem Superior:
Lambdas brilham quando usadas como pequenas funções “descartáveis” passadas
como argumentos.
– Com sorted(iteravel, key=funcao_chave):
1 # Lista de tuplas ( produto , preco )
2 produtos_loja = [( " Camiseta " , 50.00) , ( " Cal ç a " ,
120.00) , ( " Meia " , 15.00) ]
3

4 # Ordenando pela idade ( segundo elemento da tupla )


5 produtos_ordenados_por_preco =
sorted ( produtos_loja , key = lambda item_produto :
item_produto [1])
6 print ( f " Produtos ordenados por pre ç o :
{ produtos_ordenados_por_preco }")

Código 10.34: Usando lambda com sorted()

– Com map(funcao, iteravel): Aplica funcao a cada item.


1 l i s t a _ d e _ n u m e r o s_ m a p = [1 , 2 , 3 , 4 , 5]
2 qu adrados_ via_map = list ( map ( lambda x : x **2 ,
l i s t a _ d e _ n u m er o s _ m a p ) )
3 print ( f " Quadrados usando map e lambda :
{ quadrado s_via_ma p } " )

Código 10.35: Usando lambda com map()


234 Capítulo 10. Definindo e Usando Funções

– Com filter(funcao, iteravel): Filtra itens para os quais funcao retorna


True.
1 l i s t a _ d e _ n u m e r o s _ f i l t e r = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,
9 , 10]
2 n u m e r o s _ p a r e s _ f i l t r a d o s = list ( filter ( lambda x : x
% 2 == 0 , l i s t a _ d e _ n u m e r o s _ f i l t e r ) )
3 print ( f " N ú meros pares usando filter e lambda :
{ numeros_pares_filtrados }")

Código 10.36: Usando lambda com filter()

Limitações das Funções Lambda


• Apenas Uma Expressão: O corpo de uma lambda não pode ter múltiplas instru-
ções.

• Sem Docstrings: Não têm uma forma padrão de incluir docstrings.

• Legibilidade: Se a expressão se tornar muito complexa, uma lambda pode ser difícil
de ler.

Quando Usar Lambda vs. def?


• Use lambda para funções pequenas, simples e de uso único, especialmente como
argumentos para outras funções.

• Use def para qualquer função que precise de múltiplas linhas de lógica, docstrings,
ou que seja reutilizada em vários lugares.

A clareza deve ser sempre a prioridade.

Concluindo o Capítulo sobre Funções


Neste capítulo, demos um passo fundamental na nossa jornada de programação em
Python: aprendemos a criar e usar funções. Começamos entendendo a importância da reu-
tilização e modularização, vimos a sintaxe def para definir nossas próprias funções e como
chamá-las. Exploramos como passar informações para funções através de parâmetros
e argumentos (posicionais, com valores padrão e nomeados) e como as funções podem
“devolver” resultados usandoainstrução return. Discutimosaimportância de documentar
nossas funções com docstrings e entendemos as diferenças entre escopos de variáveis
locais e globais. Finalmente, demos uma olhada nas concisas funções lambda.
As funções são, verdadeiramente, os blocos de construção de programas maiores e mais
complexos. Elas nos permitem:

• Organizar nosso código em partes lógicas e gerenciáveis.

• Reutilizar código, evitando repetição e facilitando a manutenção.

• Abstrair detalhes complexos, tornando nossos programas mais fáceis de entender


em um nível mais alto.
10.11. Funções Lambda (Funções Anônimas): Pequenas Funções em Uma Linha 235

Com o domínio das funções, você está agora equipado para escrever programas Python
muito mais estruturados, eficientes e legíveis.
No próximo capítulo, continuaremos no tema da modularização, explorando como
podemos organizar nossas funções (e outros códigos) em arquivos separados chamados
módulos e como podemos usar módulos criados por outros ou pelo próprio Python,
expandindo ainda mais as capacidades dos nossos programas.
Capítulo 11

Módulos e Pacotes

No Capítulo 10, aprendemos a criar nossas próprias funções, o que nos deu uma
ferramenta poderosa para agrupar blocos de código reutilizáveis e tornar nossos programas
mais modulares e organizados. As funções nos ajudam a evitar a repetição de código
dentro de um mesmo arquivo.
Mas, e se quisermos reutilizar uma função que escrevemos em vários programas dife-
rentes? Ou se nosso programa se tornar tão grande que mantê-lo em um único arquivo
.py começa a ficar confuso e difícil de gerenciar?
É aqui que entram os módulos e, em uma escala maior, os pacotes. Eles são o
próximo nível de organização e modularização em Python, permitindo-nos estruturar nossos
projetos de forma mais eficaz, promover a reutilização de código entre diferentes projetos
e também aproveitar o imenso ecossistema de funcionalidades já prontas desenvolvidas
pela comunidade Python.

11.1 Introdução a Módulos


O que é um Módulo em Python?
Da forma mais simples, um módulo em Python é um arquivo contendo definições
e instruções Python. Esse arquivo tem a extensão .py (assim como os scripts que temos
escrito até agora). Dentro de um módulo, você pode definir:

• Funções

• Classes (que veremos na Parte IV sobre Programação Orientada a Objetos)

• Variáveis

Essas definições e instruções dentro de um módulo podem ser importadas e usadas em


outros módulos ou em scripts Python.

Por que Usar Módulos? As Vantagens da Modularização em


Arquivos
A utilização de módulos traz diversos benefícios significativos para o desenvolvimento
de software:

236
11.2. Criando Nossos Próprios Módulos: Empacotando Nosso Código 237

1. Organização Lógica do Código:


À medida que seus programas crescem, mantê-los em um único arquivo se torna
impraticável. Os módulos permitem que você divida seu programa em múltiplos
arquivos menores, cada um focado em um conjunto específico de funcionalidades
relacionadas.
2. Reutilização de Código em Múltiplos Projetos:
Se você desenvolve um conjunto de funções úteis em um módulo, pode facilmente
importar e usar esse módulo em qualquer outro programa Python que você escrever,
sem precisar copiar e colar o código.
3. Criação de Namespaces (Espaços de Nomes):
Cada módulo em Python tem seu próprio espaço de nomes privado. Isso significa
que os nomes de funções, classes e variáveis definidos dentro de um módulo não
entram em conflito direto com nomes idênticos definidos em outros módulos ou no
seu script principal (desde que você os acesse usando o nome do módulo).
4. Facilita o Trabalho em Equipe:
Quando vários desenvolvedores trabalham em um mesmo projeto, os módulos permi-
tem que cada um trabalhe em partes diferentes do sistema de forma mais indepen-
dente.

A Relação com o que Já Vimos


No Capítulo 10, focamos em como as funções nos ajudam a modularizar o código
dentro de um arquivo. Os módulos levam essa ideia um passo adiante, permitindo a
modularização entre arquivos. Eles são a base para construir bibliotecas e para estruturar
aplicações Python de qualquer tamanho.
Neste capítulo, aprenderemos como criar nossos próprios módulos, como importar e
usar o que está definido neles, como o Python encontra esses módulos, e também daremos
uma olhada em alguns dos módulos incrivelmente úteis que já vêm com a Biblioteca
Padrão do Python. Por fim, introduziremos o conceito de pacotes, que são uma forma de
organizar múltiplos módulos relacionados em uma hierarquia de diretórios.

11.2 Criando Nossos Próprios Módulos: Empacotando


Nosso Código
Uma das grandes belezas do Python é a simplicidade com que podemos criar nossos pró-
prios módulos. Não há necessidade de comandos especiais de compilação ou configurações
complexas para definir um módulo básico.

Passo 1: Escrevendo o Código do Módulo


Criar um módulo em Python é tão simples quanto:
1. Abrir seu editor de código ou IDE.
2. Criar um novo arquivo.
3. Escrever suas definições Python (funções, variáveis, classes, etc.) nesse arquivo.
238 Capítulo 11. Módulos e Pacotes

4. Salvar o arquivo com a extensão .py.


O nome do arquivo (sem a extensão .py) se tornará o nome do seu módulo.
Vamos criar um módulo simples. Crie um arquivo chamado minhas_operacoes.py
com o seguinte conteúdo:
1 # Arquivo : minhas_operacoes . py
2 """
3 Este é o nosso primeiro m ó dulo personalizado !
4 Ele cont é m algumas fun ç õ es matem á ticas b á sicas e a constante PI .
5 """
6
7 PI_APROXIMADO = 3.14159265 # Uma " constante "
8
9 def somar_numeros (a , b ) :
10 " " " Retorna a soma de a e b . " " "
11 print ( " Fun ç ã o somar_numeros do m ó dulo ’ minhas_operacoes ’ foi
chamada ! " )
12 return a + b
13
14 def subtrair_numeros (a , b ) :
15 " " " Retorna a subtra ç ã o de b de a ( a - b ) . " " "
16 return a - b
17

18 def mu l t ip l ic a r _n u me r os (a , b ) :
19 " " " Retorna a multiplica ç ã o de a por b . " " "
20 return a * b
21
22 # Este print ser á executado quando o m ó dulo for importado pela
primeira vez
23 print ( " O m ó dulo ’ minhas_operacoes ’ foi carregado com sucesso ! " )

Código 11.1: Conteúdo do arquivo minhas_operacoes.py (nosso primeiro módulo)


Salve este arquivo. Agora você tem um módulo chamado minhas_operacoes!

Passo 2: Importando e Usando o Módulo em Outro Script


Para usar as funcionalidades do seu módulo, crie outro script Python (ex: script_principal.py)
no mesmo diretório do arquivo minhas_operacoes.py.

A Instrução import nome_do_modulo


A forma mais básica é usar import seguido do nome do módulo (sem .py).
1 # Arquivo : script_principal . py
2 import minhas_operacoes # Importa nosso m ó dulo
3
4 print ( " \ nUsando as funcionalidades do m ó dulo importado : " )
5
6 # Para acessar , usamos : nome_do_modulo . nome_do_item
7 resultado_soma = minhas_operacoes . somar_numeros (15 , 7)
8 print ( f " Resultado da soma (15 + 7) : { resultado_soma } " )
9
10 valor_de_pi = minhas_operacoes . PI_APROXIMADO
11 print ( f " Valor de PI definido no m ó dulo : { valor_de_pi } " )
12
13 r e s u l t a d o _ m u l t i p l i c a c a o = minhas_operacoes . m ul t ip l i ca r _n u m er o s (6 , 7)
11.2. Criando Nossos Próprios Módulos: Empacotando Nosso Código 239

14 print ( f " Resultado da multiplica ç ã o (6 * 7) :


{ resultado_multiplicacao }")

Código 11.2: Conteúdo do arquivo script_principal.py (importando o módulo)


Ao executar script_principal.py, a mensagem “O módulo ’minhas_operacoes’ foi
carregado com sucesso!” aparecerá uma vez. A notação de ponto (nome_do_modulo.item)
cria um espaço de nomes (namespace), evitando conflitos de nomes.

Outras Formas de Importar


• from nome_do_modulo import item_especifico1, ...
Importa nomes específicos diretamente para o namespace atual. Não é preciso usar
o prefixo nome_do_modulo..
1 # Arquivo : scr ip t_ usa nd o_ fro m . py
2 from minhas_operacoes import subtrair_numeros ,
PI_APROXIMADO
3
4 print ( " Usando itens importados diretamente com ’ from ’: " )
5 diferenca = subtrair_numeros (100 , 30)
6 print ( f " Resultado da subtra ç ã o (100 - 30) : { diferenca } " )
7 print ( f " Valor de PI direto : { PI_APROXIMADO } " )
8

9 # somar_numeros n ã o foi importado diretamente , ent ã o :


10 # resultado_soma = somar_numeros (5 ,5) # Geraria NameError

Código 11.3: Usando from ... import ...

• from nome_do_modulo import * (Importando Tudo – Geralmente Desenco-


rajado)
Importa todos os nomes (que não começam com _) do módulo. Pode poluir o
namespace e causar colisões de nomes. É melhor ser explícito.

• import nome_do_modulo as alias_curto (Importando com Apelido)


Útil para nomes de módulos longos ou para evitar conflitos.
1 # Arquivo : script_com_alias . py
2 import minhas_operacoes as mop # ’ mop ’ é o apelido
3
4 print ( " Usando o m ó dulo com alias ’ mop ’: " )
5 produ to_resul tado = mop . m u lt i pl i c ar _ nu m e ro s (8 , 7)
6 print ( f " Produto (8 * 7) usando alias : { produto_ resultad o } " )
7 print ( f " Constante PI ( usando alias ) : { mop . PI_APROXIMADO } " )

Código 11.4: Importando módulo com um apelido (as)

Você também pode usar as com from: from minhas_operacoes import somar_numeros
as adicionar.

Quando o Código de um Módulo é Executado?


O código no nível superior de um módulo é executado na primeira vez que o módulo
é importado em um programa. Importações subsequentes no mesmo programa usam a
versão já carregada.
240 Capítulo 11. Módulos e Pacotes

Criar seus próprios módulos é um passo fundamental para escrever código Python mais
organizado e reutilizável.

11.3 A Busca por Módulos: Como Python Encontra os


Módulos Importados?
Quando você escreve uma instrução como import minhas_operacoes ou import math
no seu script Python, uma pergunta natural surge: “Como o Python sabe onde procurar
pelos arquivos minhas_operacoes.py ou [Link]?”
O Python não procura magicamente em todo o seu computador. Ele segue um
mecanismo de busca bem definido, percorrendo uma lista específica de diretórios (pastas)
em uma ordem determinada. Se ele encontrar um arquivo com o nome correspondente, ele
o carrega. Se não encontrar, ele levanta um erro ImportError.

O Caminho de Busca de Módulos: [Link]


A principal ferramenta que o Python usa para saber onde procurar por módulos é uma
lista de strings armazenada na variável path dentro do módulo sys. Essa lista é conhecida
como [Link]. Cada string nessa lista representa um caminho de diretório.
Você pode inspecionar o [Link] do seu ambiente Python da seguinte forma:
1 # Para ver os caminhos de busca de m ó dulos
2 import sys # Primeiro , precisamos importar o m ó dulo ’ sys ’
3
4 print ( " Caminhos de busca de m ó dulos ( sys . path ) : " )
5 for indice , caminho in enumerate ( sys . path ) :
6 print ( f " { indice + 1}. { caminho } " )

Código 11.5: Inspecionando [Link]

Ao executar este código, você verá uma lista de diretórios. Essa lista é construída
dinamicamente quando o interpretador Python inicia.

A Ordem de Busca
O Python percorre os diretórios listados em [Link] na ordem em que aparecem.
O primeiro local onde ele encontra um módulo com o nome correspondente é o que será
usado. A ordem típica de busca é:

1. O Diretório do Script Atual (ou Diretório de Trabalho Atual):


Se você está executando um script (ex: python meu_programa.py), o diretório onde
meu_programa.py está localizado é o primeiro lugar que o Python verifica. Se você
estiver usando o interpretador interativamente, o primeiro caminho geralmente é
uma string vazia (”), que representa o diretório de trabalho atual.

2. Diretórios Listados na Variável de Ambiente PYTHONPATH (se definida):


PYTHONPATH é uma variável de ambiente opcional. Se definida, os diretórios listados
nela são pesquisados após o diretório do script atual. Para iniciantes, geralmente
não é necessário configurá-la.
11.4. Módulos da Biblioteca Padrão do Python (Python Standard Library) 241

3. Diretórios de Instalação Padrão (Biblioteca Padrão e Pacotes Instalados):


Finalmente, Python procura nos diretórios onde a sua instalação do Python armazena
a Biblioteca Padrão (como os módulos math, random, etc.) e também onde os
pacotes de terceiros (instalados via pip) são colocados. A localização exata desses
diretórios depende da sua instalação e do sistema operacional.

Implicações da Ordem de Busca: Cuidado com o “Sombreamento”


de Módulos
A ordem de busca é importante. Como o Python usa o primeiro módulo que encontra,
você pode acidentalmente “sombrear” (shadow ) um módulo da biblioteca padrão se criar
um arquivo .py no diretório do seu script com o mesmo nome de um módulo padrão.
Por exemplo, se você criar um arquivo chamado [Link] na mesma pasta do seu script
principal e depois fizer import math, o Python encontrará o seu [Link] primeiro. Isso
provavelmente causará erros, pois seu arquivo não terá as funcionalidades esperadas do
módulo math padrão.
Conselho: Evite nomear seus próprios módulos com os mesmos nomes de módulos
importantes da biblioteca padrão do Python, a menos que saiba exatamente o que está
fazendo.
Entender como o Python localiza os módulos é fundamental para organizar seus projetos
e para depurar problemas de ImportError. A lista [Link] e a ordem de busca são os
conceitos chave aqui.

11.4 Módulos da Biblioteca Padrão do Python (Python


Standard Library)
Uma das grandes filosofias do Python é a ideia de vir com “baterias inclusas”. Isso
significa que, ao instalar o Python, você não recebe apenas o interpretador da linguagem,
mas também uma vasta e poderosa coleção de módulos chamada Biblioteca Padrão do
Python (Python Standard Library).
Esses módulos oferecem funcionalidades prontas para uso para uma enorme gama de
tarefas comuns, desde operações matemáticas avançadas e geração de números aleatórios
até manipulação de datas e horas, interação com o sistema operacional, trabalho com
formatos de dados como JSON e CSV, e muito mais. Conhecer e saber usar os módulos
da Biblioteca Padrão pode economizar um tempo imenso de desenvolvimento.
Nesta seção, vamos dar uma olhada em alguns dos módulos mais úteis e frequentemente
utilizados, com exemplos simples de como importá-los e usar algumas de suas capacidades.

Explorando Alguns Módulos Essenciais


Para usar qualquer um desses módulos, o primeiro passo é sempre importá-los para o
seu script.

• Módulo math: Para Operações Matemáticas Avançadas


Fornece acesso a funções matemáticas que vão além dos operadores básicos.
1 import math # Importa o m ó dulo math
2
242 Capítulo 11. Módulos e Pacotes

3 # Raiz quadrada
4 numero = 64
5 raiz_de_64 = math . sqrt ( numero )
6 print ( f " A raiz quadrada de { numero } é : { raiz_de_64 } " )
7
8 print ( f " Valor de Pi : { math . pi } " )
9 print ( f " Seno de 90 graus ( Pi /2 rad ) :
{ math . sin ( math . pi /2) } " )
10
11 fatorial_de_5 = math . factorial (5)
12 print ( f " O fatorial de 5 é : { fatorial_de_5 } " )

Código 11.6: Exemplos com o módulo math

• Módulo random: Para Geração de Números Aleatórios e Escolhas


Usado para jogos, simulações, sorteios, ou para embaralhar dados.
1 import random # Importa o m ó dulo random
2
3 num_a leatorio _int = random . randint (1 , 6) # Como um dado
4 print ( f " N ú mero aleat ó rio inteiro entre 1 e 6:
{ num_alea torio_in t } " )
5
6 opcoes_de_frutas = [ " ma ç ã " , " banana " , " laranja " , " uva " ]
7 fruta_sorteada = random . choice ( opcoes_de_frutas )
8 print ( f " Fruta sorteada para o lanche : { fruta_sorteada } " )
9
10 c a r t a s _ b a r a l h o _ s i m p l e s = [ " Á s " , " Rei " , " Dama " , " Valete " ]
11 random . shuffle ( c a r t a s _ b a r a l h o _ s i m p l e s ) # Embaralha a lista
" in - place "
12 print ( f " Cartas ap ó s embaralhar : { c a r t a s _ b a r a l h o _ s i m p l e s } " )

Código 11.7: Exemplos com o módulo random

• Módulo datetime: Para Trabalhar com Datas e Horas


Oferece classes e funções para manipular datas, horas e intervalos de tempo.
1 import datetime # Importa o m ó dulo datetime
2
3 agora_mesmo = datetime . datetime . now ()
4 print ( f " Data e hora atuais : { agora_mesmo } " )
5 print ( f " Ano atual : { agora_mesmo . year } , M ê s :
{ agora_mesmo . month } , Dia : { agora_mesmo . day } " )
6
7 # Formatar data e hora para uma string leg í vel
8 data_ formatad a_br = agora_mesmo . strftime ( " % d /% m /% Y
% H :% M :% S " )
9 print ( f " Data e hora formatadas ( BR ) : { data_fo rmatada_ br } " )
10
11 daqui_a_15_dias = agora_mesmo + datetime . timedelta ( days =15)
12 print ( f " Daqui a 15 dias ser á :
{ daqui_a_15_dias . strftime ( ’% d /% m /% Y ’) } " )

Código 11.8: Exemplos com o módulo datetime

• Módulo os (e [Link]): Para Interagir com o Sistema Operacional


Permite interagir com o sistema de arquivos (pastas, arquivos, caminhos).
11.4. Módulos da Biblioteca Padrão do Python (Python Standard Library) 243

1 import os
2 import os . path
3

4 d i r e t o r i o _ a t u a l _ s c r i p t = os . getcwd ()
5 print ( f " Diret ó rio de trabalho atual :
{ diretorio_atual_script }")
6
7 # Juntar partes de um caminho de forma port á vel
8 nome_pasta_dados = " meus_dados "
9 n om e_a rq ui vo_ sa id a = " resultado . txt "
10 c a m i n h o _ c o m p l e t o _ s a i d a = os . path . join ( nome_pasta_dados ,
no me _a rqu iv o_ sai da )
11 print ( f " Exemplo de caminho constru í do :
{ caminho_completo_saida }")
12

13 a r q u i v o _ e x e m p l o _ t e s t e = " minhas_operacoes . py " # Supondo


que este arquivo existe
14 if os . path . exists ( a r q u i v o _ e x e m p l o _ t e s t e ) :
15 print ( f " O arquivo / pasta ’{ a r q u i v o _ e x e m p l o _ t e s t e } ’
existe . " )
16 if os . path . isfile ( a r q u i v o _ e x e m p l o _ t e s t e ) :
17 print ( f " E ’{ a r q u i v o _ e x e m p l o _ t e s t e } ’ é um arquivo . " )
18 else :
19 print ( f " O arquivo / pasta ’{ a r q u i v o _ e x e m p l o _ t e s t e } ’
N Ã O existe . " )

Código 11.9: Exemplos com o módulo os e [Link]

• Módulo sys: Acesso a Parâmetros e Funções do Interpretador


Fornece acesso a variáveis e funções mantidas pelo interpretador Python.
1 import sys
2
3 print ( f " \ nVers ã o do Python : { sys . version [:5]} " )
4 print ( f " Plataforma : { sys . platform } " )
5 print ( f " Argumentos da linha de comando : { sys . argv } " )
6
7 # Exemplo de como sair de um programa :
8 # print (" Saindo do programa ...")
9 # sys . exit (0) # 0 indica sucesso

Código 11.10: Exemplos com o módulo sys

• Módulo json: Trabalhando com Dados JSON


JSON (JavaScript Object Notation) é um formato leve para intercâmbio de dados.
1 import json
2
3 dados_py = { " id " : 101 , " nome " : " Teclado " , " ativo " : True }
4 string_json = json . dumps ( dados_py , indent =2 ,
ensure_ascii = False )
5 print ( f " \ nObjeto Python para String JSON :\ n { string_json } " )
6
7 s t r i n g _j s o n _ r e c e b i d a = ’ {" sensor ": " HC - SR04 " ,
" distancia_cm ": 25.7} ’
8 d a d o s _ py _ c o n v e r t i d o s = json . loads ( s t r i n g _ j s o n _ re c e b i d a )
9 print ( f " String JSON para Objeto Python :
{ dados_py_convertidos }")
244 Capítulo 11. Módulos e Pacotes

10 print ( f " Dist â ncia do sensor :


{ d a d o s _ p y _ c o n v e r t i d o s [ ’ distancia_cm ’]} " )

Código 11.11: Exemplos com o módulo json

• Módulo csv: Lendo e Escrevendo Arquivos CSV


CSV (Comma-Separated Values) é usado para dados tabulares em texto.
1 import csv
2 import io # Para simular um arquivo em mem ó ria
3
4 dados_csv_string = " Produto , Preco , Estoque \ nCaneta
Azul ,1.75 ,150\ nLapis HB ,0.90 ,220 "
5 arquivo_simulado = io . StringIO ( dados_csv_string )
6 leitor_csv = csv . reader ( arquivo_simulado )
7
8 print ( " \ nLendo dados CSV simulados : " )
9 cabecalho = next ( leitor_csv )
10 print ( f " Cabe ç alho : { cabecalho } " )
11 for linha in leitor_csv :
12 print ( f " Dados : { linha } ( Produto : { linha [0]}) " )

Código 11.12: Exemplo simples com o módulo csv

A Ponta do Iceberg: Explore a Documentação!


Os módulos que vimos são apenas uma pequena amostra da Biblioteca Padrão do
Python. Existem centenas de outros para as mais diversas finalidades. A melhor maneira
de descobrir e aprender sobre eles é consultando a documentação oficial do Python
([Link]). Aprender a navegar na documentação e a utilizar os módulos da
Biblioteca Padrão é uma habilidade essencial.

11.5 Código Executável em Módulos


Lembre-se da Seção 11.2, quando criamos nosso primeiro módulo e incluímos uma
instrução print() no seu nível superior. Observamos que essa mensagem era impressa na
primeira vez que o módulo era importado. Isso ocorre porque o código no nível superior
de um módulo é executado quando ele é carregado.
Mas, e se quisermos incluir código em nosso arquivo .py que só deve ser executado
quando o arquivo é rodado como o programa principal, e não quando ele é importado
como um módulo? Para isso, usamos o idioma (padrão de codificação) if __name__ ==
"__main__".

A Variável Especial
Todo módulo Python tem uma variável especial embutida chamada __name__ (dois
underscores antes e depois de “name”). O valor desta variável depende de como o arquivo
está sendo usado:

• Se o arquivo é executado diretamente (ex: python meu_arquivo.py), o Python


define __name__ = "__main__".
11.5. Código Executável em Módulos 245

• Se o arquivo é importado como um módulo (ex: import meu_arquivo), o


Python define __name__ como o nome do arquivo do módulo (sem a extensão .py,
ex: "meu_arquivo").
Para ilustrar, crie verificador_de_name.py:
1 # Arquivo : v e r if i ca d or _ d e_ n am e . py
2
3 print ( f " Dentro de ’ v er i f ic a do r _ de _ na m e . py ’, o valor de __name__ é :
’{ __name__ } ’ " )
4
5 def u ma _ fu n ca o _ qu a lq u e r () :
6 print ( " A fun ç ã o ’ um a _f u nc a o _q u al q u er ’ foi chamada . " )

Código 11.13: Arquivo verificador_de_name.py


E um script_importador_exemplo.py no mesmo diretório:
1 # Arquivo : s c r i p t _ i m p o r t a d o r _ e x e m p l o . py
2
3 print ( " --- Iniciando s c r i p t _ i m p o r t a d o r _ e x e m p l o . py ---" )
4 import ve r i fi c ad o r_ d e _n a me # Importa nosso m ó dulo
5 print ( " --- M ó dulo importado ---" )
6

7 v e ri fic a do r _ de _ na m e . u m a_ f un c a o_ q ua l q ue r ()
8 print ( " --- Fim do s c r i p t _ i m p o r t a d o r _ e x e m p l o . py ---" )

Código 11.14: Arquivo script_importador_exemplo.py


Ao executar python verificador_de_name.py, __name__ será "__main__". Ao executar
python script_importador_exemplo.py, dentro de verificador_de_name.py (quando
é importado), __name__ será "verificador_de_name".

O Bloco Guarda de execução principal


Aproveitando essa propriedade, criamos um bloco de código que só executa se o arquivo
for o programa principal:
1 # Defini ç õ es de fun ç õ es , classes , etc . do m ó dulo aqui ...
2

3 if __name__ == " __main__ " :


4 # C ó digo aqui S Ó executa se este arquivo for rodado diretamente .
5 # N ã o executa quando importado como m ó dulo .
6 print ( " Este arquivo est á sendo executado como script
principal ! " )
7 # Coloque aqui testes do m ó dulo , exemplos de uso , ou a l ó gica
principal do script .

Código 11.15: Sintaxe do bloco if __name__ == "__main__"

Casos de Uso Comuns


• Código de Teste para o Módulo: Colocar chamadas às funções definidas no
próprio módulo para testá-las.
• Exemplos de Uso do Módulo: Demonstrar como usar as funcionalidades.
• Lógica Principal de um Script: Se o arquivo .py é tanto uma biblioteca quanto
um programa executável.
246 Capítulo 11. Módulos e Pacotes

Exemplo Prático Detalhado


Vamos modificar nosso módulo minhas_operacoes.py para usar este padrão. Salve
como minhas_operacoes_v2.py:
1 # Arquivo : m i n ha s _o p er a c oe s _v 2 . py
2 """
3 Este m ó dulo cont é m fun ç õ es matem á ticas e c ó digo de exemplo / teste
4 que s ó executa quando o m ó dulo é rodado diretamente .
5 """
6 PI_APROXIMADO = 3.141592 65358979 3
7
8 def somar_numeros (a , b ) :
9 return a + b
10
11 def subtrair_numeros (a , b ) :
12 return a - b
13
14 def mu l t ip l ic a r _n u me r os (a , b ) :
15 return a * b
16
17 print ( f " M ó dulo ’{ __name__ } ’ ( mi n ha s _ op e ra c oe s _ v2 . py ) sendo
carregado . " )
18
19 if __name__ == " __main__ " :
20 print ( " \n - - - Executando ’ m i nh a s_ o p er a co e s_ v 2 . py ’ como script
principal ---" )
21
22 print ( f " 10 + 5 = { somar_numeros (10 , 5) } " )
23 print ( f " 100 - 75 = { subtrair_numeros (100 , 75) } " )
24 print ( f " 12 * 12 = { m u lt i pl i c ar _ nu m e ro s (12 , 12) } " )
25 print ( f " PI : { PI_APROXIMADO } " )
26 print ( " --- Fim da execu ç ã o do script principal do m ó dulo ---" )

Código 11.16: Arquivo minhas_operacoes_v2.py com bloco __main__

E um script que o importa, script_que_importa_v2.py:


1 # Arquivo : s c r i p t _ q u e _ i m p o r t a _ v 2 . py
2 import mi n h as _ op e ra c o es _ v2 as oper_mat
3
4 print ( " \n - - - Executando s c r i p t _ q u e _ i m p o r t a _ v 2 . py ---" )
5 s = oper_mat . somar_numeros (20 , 30)
6 print ( f " Soma via m ó dulo : 20 + 30 = { s } " )
7 print ( f " PI via m ó dulo : { oper_mat . PI_APROXIMADO } " )
8 print ( " --- Fim do s c r i p t _ q u e _ i m p o r t a _ v 2 . py ---" )

Código 11.17: Arquivo script_que_importa_v2.py

Ao executar minhas_operacoes_v2.py diretamente, o bloco if __name__ == "__main__"


será executado. Ao executar script_que_importa_v2.py, esse bloco em
minhas_operacoes_v2.py não será executado, mas suas funções e variáveis estarão dis-
poníveis.
O uso de if __name__ == "__main__" é um padrão essencial em Python para criar
módulos que são tanto reutilizáveis quanto capazes de serem executados de forma indepen-
dente para testes ou como scripts.
11.6. Introdução a Pacotes (Packages): Organizando Módulos em Diretórios 247

11.6 Introdução a Pacotes (Packages): Organizando


Módulos em Diretórios
Já vimos como os módulos nos ajudam a organizar nosso código Python em arquivos
separados. No entanto, à medida que os projetos crescem, podemos acabar com um grande
número de módulos. Para lidar com essa complexidade e permitir uma estruturação ainda
melhor, Python oferece o conceito de pacotes (packages).

O que são Pacotes (Packages)?


Um pacote em Python é, essencialmente, uma coleção de módulos relacionados,
agrupados dentro de um diretório. Pense em um pacote como uma pasta que não
apenas contém arquivos de módulo (.py), mas que também é reconhecida pelo Python
como uma unidade organizacional.
Os pacotes permitem uma estruturação hierárquica do espaço de nomes dos módulos,
usando a notação de ponto para acessar módulos dentro de pacotes.
Principais Vantagens dos Pacotes:
• Organização Hierárquica: Agrupam módulos com funcionalidades relacionadas.
• Evitar Conflitos de Nomes de Módulos: Diferentes pacotes podem ter módulos
com o mesmo nome (ex: [Link] e [Link]).
• Criação de Bibliotecas Estruturadas.

Criando um Pacote Simples


Criar um pacote envolve organizar seus arquivos em uma estrutura de diretórios
específica.
1. Crie um Diretório: Este será o seu pacote (ex: meu_pacote_calculos).
2. O Arquivo __init__.py (Importante!): Dentro do diretório do pacote, crie um
arquivo chamado __init__.py.
• Este arquivo, mesmo que vazio, informa ao Python que o diretório deve ser
tratado como um pacote.
• O código em __init__.py é executado quando o pacote (ou um de seus módulos)
é importado pela primeira vez.
3. Adicione Módulos ao Pacote: Crie seus arquivos de módulo .py dentro do
diretório do pacote.
Conteúdo dos Módulos de Exemplo:
• meu_pacote_calculos/__init__.py:
1 # Arquivo : m eu _ pa c ot e _ ca l cu l o s / __init__ . py
2 print ( " Pacote ’ me u _ pa c ot e _c a l cu l os ’ sendo inicializado . " )
3 # Pode estar vazio ou conter inicializa ç õ es / importa ç õ es do
pacote .

Código 11.18: Conteúdo de __init__.py (exemplo simples)


248 Capítulo 11. Módulos e Pacotes

• meu_pacote_calculos/aritmetica_basica.py:
1 # Arquivo : m eu _ pa c ot e _ ca l cu l o s / ar itmetica _basica . py
2 def somar (a , b ) : return a + b
3 def multiplicar (a , b ) : return a * b
4 print ( " M ó dulo ’ ari tmetica_b asica ’ carregado . " )

Código 11.19: Módulo aritmetica_basica.py dentro do pacote

• meu_pacote_calculos/geometria_simples.py:
1 # Arquivo : m eu _ pa c ot e _ ca l cu l o s / ge ometria_ simples . py
2 PI_CONST = 3.14159
3 def area_circulo ( raio ) : return PI_CONST * ( raio ** 2)
4 print ( " M ó dulo ’ geo metria_si mples ’ carregado . " )

Código 11.20: Módulo geometria_simples.py dentro do pacote

Importando de Pacotes
A partir de um script fora do pacote (ex: script_principal.py no diretório meu_grande_projeto/),
você pode importar de várias formas:

1. import [Link]
1 # Em script_principal . py
2 import m e u_ p ac o te _ c al c ul o s . aritmeti ca_basic a
3 import m e u_ p ac o te _ c al c ul o s . geometri a_simple s
4
5 soma = m eu _ p ac o te _ ca l c ul o s . aritmetic a_basica . somar (10 , 20)
6 area =
m eu _ p ac o te _ ca l c ul o s . geometria _simples . area_circulo (3)
7 print ( f " Soma : { soma } , Á rea : { area :.2 f } " )

Código 11.21: Importando com import [Link]

2. import [Link] as alias


1 import m e u_ p ac o te _ c al c ul o s . aritmeti ca_basic a as arit
2 prod = arit . multiplicar (7 , 6)
3 print ( f " Produto com alias : { prod } " )

Código 11.22: Importando com alias

3. from pacote import modulo


1 from m e u_ p ac o t e_ c al c ul o s import ari tmetica_ basica
2 soma_fm = ar itmetica _basica . somar (100 , 50)
3 print ( f " Soma ( from pacote ) : { soma_fm } " )

Código 11.23: Importando com from pacote import modulo

4. from [Link] import item_especifico


11.7. Instalando Pacotes Externos com pip (Breve Introdução) 249

1 from m e u_ p ac o t e_ c al c ul o s . aritmet ica_basi ca import somar


2 from m e u_ p ac o t e_ c al c ul o s . geometr ia_simpl es import PI_CONST
3

4 soma_direta = somar (25 , 75)


5 print ( f " Soma direta : { soma_direta } " )
6 print ( f " PI : { PI_CONST } " )

Código 11.24: Importando itens específicos de um módulo do pacote

Subpacotes (Breve Menção)


Um pacote pode conter outros pacotes (subdiretórios com seus próprios __init__.py),
criando hierarquias mais profundas (ex: import [Link]).
Os pacotes são o mecanismo padrão do Python para organizar grandes bases de código
e para distribuir bibliotecas.

11.7 Instalando Pacotes Externos com pip (Breve In-


trodução)
Além da Biblioteca Padrão, existe um vasto ecossistema de pacotes e bibliotecas criados
pela comunidade Python global, oferecendo funcionalidades para quase tudo imaginável.
Para acessar esse ecossistema, usamos o repositório PyPI e a ferramenta pip.

PyPI (Python Package Index): O Grande Repositório


O PyPI ([Link]) é o repositório oficial online que hospeda centenas de milhares de
pacotes de terceiros para Python. É de onde o pip baixa os pacotes por padrão.

pip: O Instalador de Pacotes do Python


pip é a ferramenta de linha de comando padrão para instalar e gerenciar pacotes
Python que não fazem parte da biblioteca padrão. Geralmente, já vem instalado com
Python (verifique com pip –version no terminal).
Como Usar o pip (Comandos no Terminal/Prompt):
• Instalando um Pacote: pip install nome_do_pacote
Exemplo: Para instalar um pacote simples chamado camelcase (que converte strings
para CamelCase):

pip install camelcase

Após instalar, você pode usá-lo em Python:


1 # Exemplo de uso do pacote ’ camelcase ’
2 # import camelcase # Voc ê precisaria instalar primeiro via
pip
3 #
4 # c = camelcase . CamelCase ()
5 # texto = " esta frase ser á convertida para camel case "
250 Capítulo 11. Módulos e Pacotes

6 # print ( c . hump ( texto ) )


7 # Sa í da : EstaFraseSer á C o n v e r t i d a P a r a C a m e l C a s e

Código 11.25: Usando o pacote camelcase após instalação via pip

(Nota: O código Python acima é um exemplo de uso; a instalação é feita no terminal.)

• Desinstalando um Pacote: pip uninstall nome_do_pacote

• Listando Pacotes Instalados: pip list

• Especificando Versões:

– pip install nome_do_pacote==versao_exata


– pip install "nome_do_pacote>=versao_minima"

• Gerenciando Dependências com [Link]:

– pip freeze > [Link]: Salva os pacotes do ambiente atual.


– pip install -r [Link]: Instala pacotes listados no arquivo.

Ambientes Virtuais (venv): Uma Prática Essencial (Breve Introdu-


ção)
Para evitar conflitos de versões de pacotes entre diferentes projetos, é altamente
recomendado usar ambientes virtuais. Eles criam instalações Python isoladas para
cada projeto.

• A Biblioteca Padrão inclui o módulo venv para criar ambientes.

• Comando básico para criar: python -m venv nome_do_ambiente

• Após criar, você precisa ativar o ambiente (os comandos variam entre Windows,
macOS e Linux).

• Com o ambiente ativo, pip install instala pacotes apenas nele.

• Use deactivate para sair do ambiente.

Pesquise sobre “Python venv” para aprender os detalhes de ativação para o seu sistema
operacional. Usar ambientes virtuais é crucial para gerenciar dependências de forma
organizada em projetos reais.

Concluindo o Capítulo sobre Módulos e Pacotes


Neste capítulo, exploramos como ir além de um único arquivo de código em Python.
Vimos como:

• Criar nossos próprios módulos para organizar e reutilizar código.

• Utilizar as diferentes formas de importar módulos.

• Entender como o Python localiza os módulos.


11.7. Instalando Pacotes Externos com pip (Breve Introdução) 251

• Aproveitar a vasta Biblioteca Padrão do Python.

• Usar o padrão if __name__ == "__main__" para criar módulos executáveis.

• Organizar módulos em pacotes.

• Dar os primeiros passos na instalação de pacotes externos com pip e a importância


dos ambientes virtuais.

Os módulos e pacotes são a espinha dorsal da organização de código em Python, permitindo


construir aplicações complexas de forma estruturada, reutilizar código eficientemente e
colaborar com outros. O acesso ao PyPI através do pip abre um universo de ferramentas
que podem acelerar drasticamente seu desenvolvimento.
Com este conhecimento, você está pronto para não apenas escrever programas, mas
também para estruturá-los de forma profissional.
Na Parte IV do nosso livro, entraremos no fascinante mundo da Programação
Orientada a Objetos (POO) em Python, aprendendo a criar nossos próprios tipos de
dados e a modelar problemas de uma maneira ainda mais poderosa e intuitiva.
Parte IV

Programação Orientada a Objetos


(POO) em Python

252
Capítulo 12

Conceitos Fundamentais de
Programação Orientada a Objetos

Até este ponto do nosso livro, focamos no que é conhecido como programação pro-
cedural ou estruturada. Aprendemos a escrever nossos programas como uma sequência
de instruções, utilizando variáveis para armazenar dados, operadores para manipulá-los,
estruturas condicionais (if, elif, else) para tomar decisões, laços (for, while) para
repetir tarefas, e funções (def) para agrupar blocos de código reutilizáveis e organizar
nossas ideias. Essa abordagem é poderosa e suficiente para muitos tipos de problemas.
No entanto, à medida que os programas se tornam maiores e mais complexos, ou
quando tentamos modelar situações e entidades do mundo real de forma mais intuitiva,
um outro paradigma de programação se mostra extremamente útil: a Programação
Orientada a Objetos, ou simplesmente POO (OOP, em inglês, de Object-Oriented
Programming).
Python é uma linguagem multiparadigma, o que significa que ela suporta diferentes
estilos de programação, incluindo o procedural (que temos usado) e o orientado a objetos.
Nesta parte do livro, vamos mergulhar nos conceitos e nas ferramentas que Python oferece
para a POO.

12.1 Mudança de Paradigma: O que é Programação


Orientada a Objetos?
A Programação Orientada a Objetos é uma forma de pensar sobre e organizar o código
que se concentra em “coisas” ou “objetos” do mundo real (ou conceituais). Em vez de
pensar primariamente em termos de uma sequência de ações (procedimentos ou funções)
que manipulam dados, na POO, pensamos em criar “objetos” que possuem suas próprias
características (dados) e seus próprios comportamentos (ações que podem realizar).

• O que é um Objeto?
Na POO, um objeto é uma entidade que agrupa:

1. Dados (Atributos): Informações que descrevem o estado ou as características


do objeto.
2. Comportamentos (Métodos): Ações ou operações que o objeto pode realizar,
geralmente para manipular seus próprios dados ou interagir com outros objetos.

253
254 Capítulo 12. Conceitos Fundamentais de Programação Orientada a Objetos

• Analogias do Mundo Real:


Pense em objetos do seu cotidiano:

– Um Carro é um objeto.
∗ Atributos: cor, marca, modelo, ano, velocidade atual.
∗ Métodos (comportamentos): acelerar(), frear(), ligar_farol().
– Uma Pessoa é um objeto.
∗ Atributos: nome, idade, altura, profissão.
∗ Métodos: andar(), falar(), comer().
– Uma Conta Bancária é um objeto.
∗ Atributos: número da conta, titular, saldo.
∗ Métodos: depositar(), sacar(), verificar_saldo().

A POO tenta trazer essa forma intuitiva de ver o mundo para dentro da estrutura
dos nossos programas.

• Benefícios da POO (Uma Visão Inicial):

– Modelagem Intuitiva: Facilita a representação de entidades complexas.


– Reutilização de Código Aprimorada: Através de mecanismos como herança.
– Melhor Organização e Modularidade: Código agrupado em torno de
objetos.
– Manutenção Facilitada: Alterações mais isoladas.
– Encapsulamento: Esconder complexidade interna, proteger dados.

Nosso foco inicial nesta parte será entender os dois pilares da POO em Python: classes
e objetos. As classes são como os “moldes” que definem como os objetos serão, e os
objetos são as “coisas” concretas que criamos a partir desses moldes. Vamos explorar
como definir nossas próprias classes para criar nossos próprios tipos de objetos, com seus
atributos e métodos.

12.2 Classes e Objetos: Os Pilares da POO


Depois de termos uma ideia do que é a Programação Orientada a Objetos (POO) na
seção anterior, é hora de nos aprofundarmos em seus dois componentes mais fundamentais e
interdependentes: classes e objetos. Não se pode falar de POO sem entender claramente
o papel de cada um e como eles se relacionam.

Classes: As Plantas Baixas ou Moldes para Criar Objetos


Uma classe em POO é como uma planta baixa para construir uma casa, um molde
para fazer um bolo, ou uma receita detalhada. Ela é a definição abstrata de um tipo
de “coisa”. A classe em si não é a “coisa” concreta, mas sim o projeto que descreve:

• Quais características (ou propriedades, ou dados) todas as “coisas” desse tipo terão.
Em POO, chamamos essas características de atributos.
12.2. Classes e Objetos: Os Pilares da POO 255

• Quais comportamentos (ou ações, ou funcionalidades) todas as “coisas” desse tipo


poderão realizar. Em POO, chamamos esses comportamentos de métodos.
Por exemplo, uma classe Cachorro definiria que cachorros têm atributos como nome e
raca, e métodos como latir().
Convenção de Nomenclatura: Em Python, a convenção para nomes de classes é
usar CamelCase (ou CapWords), onde cada palavra começa com maiúscula e não há
underscores (ex: Carro, ContaBancaria).
Sintaxe Básica para Definir uma Classe: A forma mais simples é usando a
palavra-chave class seguida do nome da classe e dois pontos. O corpo da classe é
indentado.
1 class NomeDaClasse :
2 # O corpo da classe vir á aqui ( atributos e m é todos ) .
3 # ’ pass ’ é usado se a classe estiver vazia inicialmente .
4 pass

Código 12.1: Sintaxe básica para definir uma classe

• class: Palavra-chave para iniciar a definição.


• NomeDaClasse: O nome da classe (em CamelCase).
• : (dois pontos): Marca o início do corpo indentado.
• Corpo da Classe (Indentado): Onde atributos e métodos são definidos. pass
não faz nada, é um marcador.

Vamos definir nossa classe Cachorro de forma bem simples por enquanto:
1 # Definindo uma classe simples chamada Cachorro
2 class Cachorro :
3 # Esta classe , por enquanto , n ã o define atributos ou m é todos
espec í ficos ,
4 # mas ela j á cria um novo " tipo " no nosso programa : o tipo
Cachorro .
5 pass

Código 12.2: Definindo uma classe Cachorro simples

Objetos: As Instâncias Concretas de uma Classe


Se a classe é o molde, um objeto é a “coisa” real e concreta construída a partir desse
molde. Um objeto é uma instância específica de uma classe.
Características dos Objetos:
• Cada objeto tem seu próprio conjunto de valores para os atributos definidos pela
classe (seu próprio estado).
• Todos os objetos da mesma classe compartilham os mesmos métodos (comporta-
mentos).
Criando um Objeto (Instanciação): Para criar um objeto (ou “instanciar uma
classe”), você usa o nome da classe seguido por parênteses (), como se estivesse chamando
uma função:
256 Capítulo 12. Conceitos Fundamentais de Programação Orientada a Objetos

nome_do_objeto = NomeDaClasse()
Isso cria um novo objeto do tipo NomeDaClasse na memória.
Vamos criar alguns objetos (instâncias) da nossa classe Cachorro:
1 # Relembrando a defini ç ã o da classe
2 class Cachorro :
3 pass
4
5 # Agora , criando objetos ( inst â ncias ) da classe Cachorro
6 rex = Cachorro () # Cria um objeto do tipo Cachorro
7 luna = Cachorro () # Cria OUTRO objeto do tipo Cachorro
8
9 print ( f " O tipo do objeto ’ rex ’ é : { type ( rex ) } " )
10 print ( f " O tipo do objeto ’ luna ’ é : { type ( luna ) } " )
11

12 # Verificando se s ã o objetos diferentes na mem ó ria


13 print ( f " O objeto ’ rex ’ é o mesmo objeto que ’ luna ’? { rex is luna } " )
14 # Sa í da : False ( s ã o inst â ncias diferentes )

Código 12.3: Criando instâncias (objetos) da classe Cachorro

A Relação Fundamental entre Classe e Objeto


• A classe é a definição, o projeto, o molde. Existe apenas uma definição da classe.

• Os objetos são as realizações concretas, as instâncias dessa classe. Podemos criar


quantos objetos quisermos a partir da mesma classe.

As classes nos dão o poder de definir nossos próprios tipos de dados. Na próxima seção,
vamos começar a dar “vida” às nossas classes, aprendendo como definir as características
(atributos) que nossos objetos terão.

12.3 Atributos: As Características dos Objetos


Na Programação Orientada a Objetos, dissemos que os objetos possuem características
(dados que descrevem seu estado) e comportamentos (ações que podem realizar). Nesta
seção, vamos focar nas características, que em POO são chamadas de atributos.
Pense nos atributos como as variáveis que “vivem” dentro de cada objeto. Se temos
um objeto carro, seus atributos poderiam ser cor, marca, modelo, etc.

Atributos de Instância: Características Individuais de Cada Objeto


Os atributos mais comuns são os atributos de instância. Seus valores são específicos
para cada instância (objeto) da classe. Dois objetos da mesma classe Cachorro podem
ter nomes e idades diferentes.

O Método Construtor
Quando criamos um objeto, como definimos os valores iniciais para seus atributos? Para
isso, Python usa um método muito especial: o método __init__ (com dois underscores
antes e dois depois de “init”).
12.3. Atributos: As Características dos Objetos 257

• O que é __init__? É um método especial chamado automaticamente pelo Python


logo após um novo objeto da classe ser criado na memória. Seu propósito principal
é inicializar os atributos da nova instância. É frequentemente chamado de
“construtor”.

• O Parâmetro self (Crucial!)

– O primeiro parâmetro de __init__ (e da maioria dos métodos de instância) é,


por convenção, chamado self.
– self é uma referência ao próprio objeto (instância) que está sendo criado
ou sobre o qual o método está operando.
– Python passa essa referência automaticamente para self. Você não passa um
argumento para self explicitamente na chamada.
– Dentro de __init__, você usa self para criar e acessar os atributos da instância:
self.nome_do_atributo = valor.

• Sintaxe de __init__: Além de self, pode receber outros parâmetros para os


valores iniciais dos atributos.

1 class NomeDaClasse :
2 def __init__ ( self , parametro1 , parametro2 ,
pa ra me tro _o pc ion al = " padr ã o " ) :
3 # ’ self ’ é a refer ê ncia à inst â ncia atual
4 self . atributo1 = parametro1
5 self . atributo2 = parametro2
6 self . outro_atributo = p ar am etr o_ op cio na l
7 self . fixo = " Valor interno "

Código 12.4: Sintaxe do método __init__


Ao criar um objeto meu_obj = NomeDaClasse(arg1, arg2), Python chama __init__(meu_obj,
arg1, arg2).

Exemplo Prático: A Classe Cachorro com Atributos


1 class Cachorro :
2 # O m é todo construtor __init__
3 def __init__ ( self , nome_do_cao , raca_do_cao , idade_do_cao ) :
4 # ’ self ’ se refere à inst â ncia espec í fica de Cachorro que
est á sendo criada .
5 print ( f " Dentro do __init__ : Criando um cachorro chamado
{ nome_do_cao }... " )
6 self . nome = nome_do_cao # Atributo de inst â ncia ’ nome ’
7 self . raca = raca_do_cao # Atributo de inst â ncia ’ raca ’
8 self . idade = idade_do_cao # Atributo de inst â ncia
’ idade ’
9 self . esta_dormindo = False # Atributo com valor inicial
padr ã o
10
11 # Criando inst â ncias ( objetos ) da classe Cachorro :
12 rex = Cachorro ( " Rex " , " Labrador " , 3)
13 luna = Cachorro ( " Luna " , " Poodle Toy " , 5)
14
258 Capítulo 12. Conceitos Fundamentais de Programação Orientada a Objetos

15 # Os atributos s ã o espec í ficos de cada objeto :


16 print ( f " \ nC1 -> Nome : { rex . nome } , Ra ç a : { rex . raca } , Idade :
{ rex . idade } , Dormindo : { rex . esta_dormindo } " )
17 print ( f " C2 -> Nome : { luna . nome } , Ra ç a : { luna . raca } , Idade :
{ luna . idade } , Dormindo : { luna . esta_dormindo } " )

Código 12.5: Definindo e usando a classe Cachorro com __init__

Acessando e Modificando Atributos de Instância


Uma vez que um objeto é criado, você pode acessar e modificar seus atributos de
instância usando a notação de ponto:
• Acesso: valor_atributo = meu_objeto.nome_do_atributo
• Modificação: meu_objeto.nome_do_atributo = novo_valor

1 # Supondo que ’ rex ’ j á foi criado :


2 # rex = Cachorro (" Rex " , " Labrador " , 3)
3 # rex . esta_dormindo era False ( do __init__ )
4
5 print ( f " \ nO nome do cachorro é : { rex . nome } " )
6
7 print ( f " O cachorro { rex . nome } est á dormindo ? { rex . esta_dormindo } " )
8 print ( f " Colocando { rex . nome } para dormir ... " )
9 rex . esta_dormindo = True # Modificamos o estado do objeto ’ rex ’
10 print ( f " Agora , { rex . nome } est á dormindo ? { rex . esta_dormindo } " )
11
12 rex . idade += 1 # Rex fez anivers á rio !
13 print ( f " Nova idade do { rex . nome }: { rex . idade } anos " )

Código 12.6: Acessando e modificando atributos de um objeto


O método __init__ e o parâmetro self são centrais para a POO em Python, permitindo
criar objetos com estados iniciais definidos. Na próxima seção, veremos como adicionar
comportamentos (métodos) a esses objetos.

12.4 Métodos: Os Comportamentos dos Objetos


Se os atributos são as características que definem o estado de um objeto, os métodos
são as ações ou comportamentos que esses objetos podem realizar. Métodos são,
essencialmente, funções que são definidas dentro de uma classe e que operam sobre os
dados (atributos) de uma instância específica daquela classe.
Quando você chama um método em um objeto, você está pedindo àquele objeto para
realizar uma de suas ações.

O que são Métodos de Instância?


Os métodos mais comuns são os métodos de instância. Eles operam sobre uma
instância particular (um objeto específico) da classe.
O Parâmetro self Novamente (Sempre Presente!)
Assim como no método __init__, o primeiro parâmetro de qualquer método de instância
em Python é, por convenção, chamado self.
12.4. Métodos: Os Comportamentos dos Objetos 259

• self é uma referência ao próprio objeto (a instância) sobre o qual o método está
sendo chamado.

• Python passa essa referência automaticamente.

• Dentro do método, self é usado para acessar ou modificar os atributos da instância


([Link]) ou para chamar outros métodos da mesma instância
(self.outro_metodo()).

Definindo Métodos de Instância


A sintaxe para definir um método dentro de uma classe é similar à de uma função, mas
está indentada dentro da classe e tem self como primeiro parâmetro:
1 class NomeDaClasse :
2 def __init__ ( self , ...) :
3 # ... atributos ...
4 pass
5
6 def nome_do_metodo ( self , parametro1_opcional , ...) :
7 # Corpo do m é todo ( indentado )
8 # Ex : self . atributo_x = p a r am e tr o 1 _o p ci o na l
9 # Pode ter uma instru ç ã o ’ return ’.
10 pass

Código 12.7: Sintaxe para definir um método de instância

Chamando Métodos de Instância


Uma vez que você tem um objeto, chama seus métodos usando a notação de ponto:
meu_objeto.nome_do_metodo(argumento1_para_param1_opcional, ...)
Você não passa o argumento para self explicitamente.

Exemplo Prático: Adicionando Comportamentos à Classe Cachorro


Vamos continuar com nossa classe Cachorro e adicionar alguns métodos:
1 class Cachorro :
2 def __init__ ( self , nome_cao , raca_cao , idade_cao ) :
3 " " " Construtor da classe Cachorro . " " "
4 self . nome = nome_cao
5 self . raca = raca_cao
6 self . idade = idade_cao
7 self . esta_dormindo = False # Todo cachorro come ç a acordado
8

9 # M é todo para o cachorro latir


10 def latir ( self ) :
11 " " " Faz o cachorro latir , se n ã o estiver dormindo . " " "
12 if self . esta_dormindo :
13 return f " { self . nome } est á sonhando com ossos ... Zzz ... "
14 else :
15 return f " { self . nome } ({ self . raca }) diz : Au au ! Woof ! "
16
17 # M é todo para fazer o cachorro dormir
260 Capítulo 12. Conceitos Fundamentais de Programação Orientada a Objetos

18 def dormir ( self ) :


19 " " " Coloca o cachorro para dormir , se ele n ã o estiver
dormindo . " " "
20 if not self . esta_dormindo :
21 self . esta_dormindo = True # Modifica o atributo de
inst â ncia
22 print ( f " { self . nome } se aninhou e foi dormir . " )
23 else :
24 print ( f " { self . nome } j á est á em um sono profundo ! " )
25
26 # M é todo para acordar o cachorro
27 def acordar ( self ) :
28 " " " Acorda o cachorro , se ele estiver dormindo . " " "
29 if self . esta_dormindo :
30 self . esta_dormindo = False # Modifica o atributo
31 print ( f " { self . nome } espregui ç ou e acordou ! " )
32 else :
33 print ( f " { self . nome } j á estava acordado e pronto para
brincar ! " )
34
35 # M é todo para obter uma descri ç ã o do cachorro
36 def obter_descricao ( self ) :
37 " " " Retorna uma string descrevendo o cachorro . " " "
38 estado_atual = " dormindo " if self . esta_dormindo else
" acordado ( a ) e alerta "
39 return f " { self . nome } é um ( a ) c ã o da ra ç a { self . raca } , tem
{ self . idade } anos e est á { estado_atual }. "
40
41 # Criando e usando os objetos com seus m é todos
42 rex = Cachorro ( " Rex " , " Golden Retriever " , 4)
43 luna = Cachorro ( " Luna " , " Beagle " , 2)
44
45 print ( rex . obter_descricao () )
46 print ( luna . obter_descricao () )
47
48 print ( f " \ n { rex . latir () } " )
49 luna . dormir ()
50 print ( luna . latir () )
51 luna . acordar ()
52 print ( luna . latir () )
53
54 print ( f " \ n { rex . obter_descricao () } " )
55 print ( f " { luna . obter_descricao () } " )

Código 12.8: Classe Cachorro com atributos e métodos


No exemplo, latir() e obter_descricao() retornam valores, enquanto dormir() e
acordar() modificam o estado do objeto e imprimem mensagens.
Os métodos definem os comportamentos dos objetos e operam sobre seus atributos
usando self. Eles são essenciais para encapsular a lógica relacionada a um tipo de objeto.

12.5 Os Quatro Pilares da POO (Introdução Breve e


Conceitual)
Até agora neste capítulo, introduzimos os blocos de construção da Programação
Orientada a Objetos: classes, objetos, atributos e métodos. No entanto, a POO é guiada
12.5. Os Quatro Pilares da POO (Introdução Breve e Conceitual) 261

por um conjunto de princípios de design que ajudam a criar software mais robusto e
flexível. Esses princípios são frequentemente chamados de os quatro pilares da POO:
Abstração, Encapsulamento, Herança e Polimorfismo.
Nesta seção, faremos uma introdução conceitual a cada um. Detalhes de implementação,
especialmente para Herança e Polimorfismo, virão em capítulos posteriores.

1. Abstração
A abstração é o processo de focar nos aspectos essenciais de um objeto, ignorando
detalhes irrelevantes para um determinado contexto. As próprias classes são uma forma de
abstração. Ao definir uma classe Carro, por exemplo, com atributos como cor e métodos
como acelerar(), abstraímos o conceito de carro sem nos preocuparmos com a mecânica
interna do motor naquele momento.

• Benefício: Reduz a complexidade, torna o sistema mais fácil de entender e usar.

2. Encapsulamento
O encapsulamento é o princípio de agrupar dados (atributos) e os métodos que
operam sobre esses dados dentro de uma única unidade (a classe/objeto). Busca também
proteger os dados internos contra acesso externo direto e modificações indevidas, expondo
uma interface controlada (métodos).

• Proteção de Dados em Python (Convenção):

– Atributos iniciados com um underscore (ex: _saldo) são, por convenção, para
uso interno.
– Atributos iniciados com dois underscores (ex: __segredo) ativam um mecanismo
chamado name mangling.

• Benefício: Melhora a integridade dos dados, reduz acoplamento, facilita a manuten-


ção.

3. Herança
A herança é um mecanismo que permite que uma nova classe (subclasse) herde
atributos e métodos de uma classe existente (superclasse).

• Relação “é um(a)”: Modela relações como “Cachorro é um Animal”.

• Benefício: Promove a reutilização de código, facilita a organização em hierarquias


e permite o polimorfismo.

• (Será detalhado no Capítulo 14).

4. Polimorfismo
Polimorfismo (“muitas formas”) refere-se à capacidade de objetos de diferentes classes
responderem à mesma mensagem (chamada de método) de maneiras diferentes, específicas
para cada classe.
262 Capítulo 12. Conceitos Fundamentais de Programação Orientada a Objetos

• Exemplo Conceitual: Um método falar() que produz “Au au!” para um objeto
Cachorro e “Miau!” para um objeto Gato.

• Benefício: Permite escrever código mais genérico e flexível.

• (Será detalhado no Capítulo 14, frequentemente junto com herança).

Concluindo o Capítulo sobre Conceitos Fundamentais de POO


Neste capítulo, abrimos as portas para o mundo da Programação Orientada a Objetos
em Python. Começamos entendendo a mudança de paradigma, focando na ideia de
objetos que possuem atributos (estado) e métodos (comportamento).
Vimos como as classes servem de moldes para criar esses objetos e aprendemos a sintaxe
básica para definir uma classe com a palavra-chave class. Detalhamos a importância do
método construtor __init__(self, ...) para inicializar os atributos de uma instância e
o papel central do parâmetro self. Também aprendemos a definir e chamar métodos de
instância.
Finalmente, tivemos uma introdução conceitual aos quatro pilares da POO: Abstração,
Encapsulamento, Herança e Polimorfismo. Entender esses conceitos é o primeiro passo
para projetar e construir programas mais robustos, modulares e fáceis de manter usando o
paradigma orientado a objetos.
No próximo capítulo, continuaremos nossa exploração da POO em Python, mergulhando
em mais detalhes sobre como trabalhar com classes e objetos, incluindo outros tipos de
atributos e métodos, e solidificando nosso entendimento com mais exemplos práticos.
Capítulo 13

Trabalhando (Mais) com Classes e


Objetos

No capítulo anterior, abrimos as portas para a Programação Orientada a Objetos


(POO) em Python. Introduzimos os conceitos fundamentais de classes como moldes para
criar objetos, que são as instâncias concretas desses moldes. Vimos como os objetos
possuem atributos (características que definem seu estado) e métodos (comportamentos
ou ações que podem realizar). O método especial __init__ e o importantíssimo parâmetro
self foram apresentados como peças centrais na inicialização e operação dos objetos.
Agora, neste capítulo, vamos expandir nosso conhecimento, explorando outras formas
de definir e usar atributos, aprofundando em métodos especiais que tornam nossos objetos
mais integrados ao Python, e discutindo convenções para o encapsulamento de dados. O
objetivo é que você se sinta cada vez mais confortável para modelar suas próprias soluções
usando classes e objetos.

13.1 Classes, Objetos, __init__ e self – Os Fundamen-


tos da POO
Antes de avançarmos para novos tópicos, vale a pena recapitular rapidamente os pilares
que sustentam a POO em Python, conforme vimos no Capítulo 12:

• Classe: É o “projeto” ou “molde”. Você define uma classe usando a palavra-chave


class seguida do nome da classe (convenção: NomeEmCamelCase).
1 class M i nh a Pr i me i r aC l as s e :
2 pass # Uma classe simples , sem conte ú do por enquanto

Código 13.1: Definição de classe simples

• Objeto (ou Instância): É a “coisa” real criada a partir do molde (classe). Você
cria um objeto chamando a classe como se fosse uma função: meu_objeto =
MinhaPrimeiraClasse().

• Método __init__(self, ...) (O Construtor): Um método especial dentro


da classe que é chamado automaticamente quando um novo objeto é criado. Sua
principal função é inicializar os atributos da instância.

263
264 Capítulo 13. Trabalhando (Mais) com Classes e Objetos

1 class Produto :
2 def __init__ ( self , nome_produto , preco_produto ) : #
’ self ’ é o primeiro
3 self . nome = nome_produto # Atributo de
inst â ncia ’ nome ’
4 self . preco = preco_produto # Atributo de
inst â ncia ’ preco ’
5 print ( f " Produto ’{ self . nome } ’ criado com pre ç o
R$ { self . preco :.2 f }! " )
6
7 caneta = Produto ( " Caneta Azul Especial " , 2.50)

Código 13.2: Exemplo de método __init__

• self: Dentro de qualquer método de uma classe, self é uma referência à instância
específica do objeto sobre a qual o método está operando. É através de self
que acessamos ou modificamos os atributos daquele objeto ([Link]) ou
chamamos outros métodos do mesmo objeto (self.outro_metodo()).
• Atributos de Instância: São as variáveis que pertencem a cada objeto indivi-
dualmente, armazenando seu estado. São tipicamente criados dentro do __init__
usando self.nome_atributo = valor.
• Métodos de Instância: São funções definidas dentro da classe que realizam ações
relacionadas ao objeto ou que manipulam seus atributos. O primeiro parâmetro é
sempre self.
1 class Retangulo :
2 def __init__ ( self , base , altura ) :
3 self . base_ret = base
4 self . altura_ret = altura
5
6 def calcular_area ( self ) : # ’ self ’ permite acesso aos
atributos
7 return self . base_ret * self . altura_ret
8
9 meu_retangulo = Retangulo (10 , 5)
10 area_calculada = meu_retangulo . calcular_area ()
11 print ( f " A á rea do ret â ngulo é : { area_calculada } " )

Código 13.3: Exemplo de método de instância

Com esses conceitos bem frescos na memória, estamos prontos para explorar como diferen-
ciar tipos de atributos, como fazer nossos objetos terem representações em string mais
amigáveis, e como o Python lida comaideia de “privacidade” de atributos.

13.2 Atributos de Classe vs. Atributos de Instância:


Diferenciação e Uso
No mundo da Programação Orientada a Objetos, os atributos são as variáveis que
armazenam os dados ou o estado dos nossos objetos. Em Python, podemos ter dois tipos
principais de atributos associados a classes: atributos de instância e atributos de
classe.
13.2. Atributos de Classe vs. Atributos de Instância: Diferenciação e Uso 265

Atributos de Instância (Revisão Rápida)


Os atributos de instância são específicos para cada objeto. São tipicamente definidos
em __init__ usando self.nome_atributo = valor. Cada objeto tem sua própria cópia
e valor para esses atributos.

Atributos de Classe
Os atributos de classe pertencem à classe em si, e não a uma instância específica. São
definidos diretamente dentro do escopo da classe, mas fora de qualquer método (geralmente
no topo).

• Compartilhamento: O valor de um atributo de classe é compartilhado por todas


as instâncias daquela classe. Se alterado na classe, reflete em todas as instâncias
(a menos que uma instância o “sombreie” com um atributo de instância de mesmo
nome).

• Casos de Uso Comuns: Constantes da classe, propriedades padrão ou comparti-


lhadas, contadores de instâncias.

• Acesso: Via nome da classe ([Link]) ou via instância ([Link],


se não sombreado).

• Modificação: Para afetar todas as instâncias, modifique via [Link]


= novo_valor. Modificar via [Link] = valor cria um novo atributo de
instância que sombreia o da classe para aquele objeto.

Exemplos Práticos para Diferenciar:

• Exemplo 1: Classe Veiculo com atributo de classe para rodas


1 class Veiculo :
2 # Atributo de classe
3 numero_de_rodas_padrao = 4
4

5 def __init__ ( self , marca_veiculo , modelo_veiculo ,


cor_veiculo ) :
6 # Atributos de inst â ncia
7 self . marca = marca_veiculo
8 self . modelo = modelo_veiculo
9 self . cor = cor_veiculo
10 self . rodas_esp ecificas = None # Para ve í culos n ã o
padr ã o
11
12 def descricao ( self ) :
13 num_rodas = self . rodas_e specific as if
self . rodas _especif icas is not None \
14 else Veiculo . n u m e r o _ d e _ r o d a s _ p a d r a o
15 return f " { self . marca } { self . modelo } ({ self . cor }) -
{ num_rodas } rodas . "
16
17 carro1 = Veiculo ( " Fiat " , " Mobi " , " Vermelho " )
18 moto1 = Veiculo ( " Yamaha " , " MT -03 " , " Azul " )
19 moto1 . rodas_es pecifica s = 2 # Atributo de inst â ncia para
esta moto
20
266 Capítulo 13. Trabalhando (Mais) com Classes e Objetos

21 print ( carro1 . descricao () )


22 print ( moto1 . descricao () )
23
24 print ( f " \ nPadr ã o de rodas da classe :
{ Veiculo . n u m e r o _ d e _ r o d a s _ p a d r a o } " )
25 Veiculo . n u m e r o _ d e _ r o d a s _ p a d r a o = 6 # Modifica para todas
as inst â ncias sem override
26 print ( f " Novo padr ã o para carro1 : { carro1 . descricao () } " ) #
Usar á 6 rodas
27
28 carro1 . n u m e r o _ d e _ r o d a s _ p a d r a o = 3 # Cria atributo de
INST Â NCIA em carro1 , sombreando
29 print ( f " Carro1 agora ( inst â ncia ) :
{ carro1 . n u m e r o _ d e _ r o d a s _ p a d r a o } rodas " )
30 print ( f " Classe Veiculo ainda ( classe ) :
{ Veiculo . n u m e r o _ d e _ r o d a s _ p a d r a o } rodas " )

Código 13.4: Atributo de classe e de instância na classe Veiculo

• Exemplo 2: Contador de instâncias de uma classe Robo


1 class Robo :
2 # Atributo de classe para contar inst â ncias
3 total_de_robos_criados = 0
4

5 def __init__ ( self , nome_robo ) :


6 self . nome = nome_robo # Atributo de inst â ncia
7 Robo . t o t a l _ d e _ r o b o s _ c r i a d o s += 1 # Incrementa o da
CLASSE
8 print ( f " Rob ô ’{ self . nome } ’ criado ! Total :
{ Robo . t o t a l _ d e _ r o b o s _ c r i a d o s } " )
9
10 robo_a = Robo ( " R2D2 " )
11 robo_b = Robo ( " C3PO " )
12 robo_c = Robo ( " Wall - E " )
13
14 print ( f " \ nTotal final de rob ô s ( via classe ) :
{ Robo . t o t a l _ d e _ r o b o s _ c r i a d o s } " )

Código 13.5: Usando atributo de classe como contador de instâncias

Quando Usar Cada Tipo de Atributo


• Use atributos de instância para dados que variam de objeto para objeto.
• Use atributos de classe para dados compartilhados ou que são uma propriedade
da classe em si.
Entender essa distinção é crucial para modelar corretamente o estado e as propriedades
dos seus objetos.

13.3 Métodos de Instância (Revisão e Mais Exemplos)


Como vimos brevemente no Capítulo 12, os métodos são funções definidas dentro de
uma classe que implementam os comportamentos dos objetos. Os métodos de instância
são o tipo mais comum e operam sobre os dados (atributos) de um objeto específico.
13.3. Métodos de Instância (Revisão e Mais Exemplos) 267

Revisão Essencial: O Papel do self


O primeiro parâmetro de qualquer método de instância deve ser self. self é a
referência à própria instância sobre a qual o método foi chamado. Python passa self
automaticamente. Usamos self para acessar e modificar os atributos da instância (ex:
[Link]) ou chamar outros métodos da mesma instância.
A estrutura de um método de instância é:
1 class Mi n h aC l as s eQ u a lq u er :
2 def __init__ ( self , atributo_inicial ) :
3 self . atributo = atributo_inicial
4
5 def m e u _ m e t o d o _ d e _ i n s t a n c i a ( self , pa r a me t ro _ a di c io n al ) :
6 # Corpo do m é todo : pode usar self . atributo e
p ar a m et r o_ a di c i on a l
7 print ( f " Atributo : { self . atributo } , Par â metro :
{ p a ra m et r o _a d ic i o na l } " )
8 self . atributo += p ar a me t r o_ a di c i on a l
9 return self . atributo

Código 13.6: Estrutura de um método de instância

Mais Exemplos Práticos


• Exemplo 1: Classe Retangulo com mais funcionalidades
1 class Retangulo :
2 def __init__ ( self , base_inicial , altura_inicial ) :
3 " " " Construtor : Inicializa um ret â ngulo . " " "
4 self . base = base_inicial
5 self . altura = altura_inicial
6 print ( f " Ret â ngulo criado : Base ={ self . base } ,
Altura ={ self . altura }. " )
7
8 def calcular_area ( self ) :
9 " " " Calcula e retorna a á rea do ret â ngulo . " " "
10 return self . base * self . altura
11
12 def c al cul ar _p eri me tr o ( self ) :
13 " " " Calcula e retorna o per í metro do ret â ngulo . " " "
14 return 2 * ( self . base + self . altura )
15
16 def redimensionar ( self , nova_base , nova_altura ) :
17 " " " Altera as dimens õ es do ret â ngulo . " " "
18 if nova_base > 0 and nova_altura > 0:
19 self . base = nova_base
20 self . altura = nova_altura
21 print ( f " Redimensionado para Base ={ self . base } ,
Altura ={ self . altura }. " )
22 else :
23 print ( " Erro : Dimens õ es devem ser positivas . " )
24
25 def __str__ ( self ) : # M é todo especial para
representa ç ã o em string
26 " " " Retorna uma representa ç ã o em string amig á vel do
ret â ngulo . " " "
27 return f " Ret â ngulo ( Base ={ self . base } ,
Altura ={ self . altura }) "
268 Capítulo 13. Trabalhando (Mais) com Classes e Objetos

28
29 # Usando a classe Retangulo
30 ret1 = Retangulo (10 , 5)
31 print ( f " Descri ç ã o : { ret1 } " )
32 print ( f " Á rea : { ret1 . calcular_area () } , Per í metro :
{ ret1 . ca lcu la r_ per im et ro () } " )
33 ret1 . redimensionar (12 , 6)
34 print ( f " Nova Á rea : { ret1 . calcular_area () } " )

Código 13.7: Classe Retangulo com métodos de cálculo e modificação

• Exemplo 2: Classe ContaBancaria mais elaborada


1 class ContaBancaria :
2 def __init__ ( self , numero_conta , titular_conta ,
saldo_inicial =0.0) :
3 self . numero_conta = numero_conta
4 self . titular = titular_conta
5 self . _saldo = saldo_inicial # Atributo " protegido "
por conven ç ã o
6 print ( f " Conta { self . numero_conta } de
{ self . titular } criada com R$ { self . _saldo :.2 f }. " )
7
8 def depositar ( self , valor_deposito ) :
9 if valor_deposito > 0:
10 self . _saldo += valor_deposito
11 print ( f " Dep ó sito de R$ { valor_deposito :.2 f }
realizado . " )
12 else :
13 print ( " Valor de dep ó sito inv á lido . " )
14 self . c o n s u l t a r _ s a l d o _ a t u a l ()
15

16 def sacar ( self , valor_saque ) :


17 if valor_saque <= 0:
18 print ( " Valor de saque inv á lido . " )
19 elif valor_saque > self . _saldo :
20 print ( " Saldo insuficiente . " )
21 else :
22 self . _saldo -= valor_saque
23 print ( f " Saque de R$ { valor_saque :.2 f }
realizado . " )
24 self . c o n s u l t a r _ s a l d o _ a t u a l ()
25

26 def c o n s u l t a r _ s a l d o _ a t u a l ( self ) :
27 print ( f " Saldo da conta { self . numero_conta }:
R$ { self . _saldo :.2 f } " )
28 return self . _saldo
29
30 def __str__ ( self ) :
31 return f " Conta [ N : { self . numero_conta } , Titular :
{ self . titular } , Saldo : R$ { self . _saldo :.2 f }] "
32
33 # Usando a classe ContaBancaria
34 conta_da_ana = ContaBancaria ( " 001 " , " Ana Paula " , 1000.00)
35 print ( f " \ nDetalhes : { conta_da_ana } " )
36 conta_da_ana . depositar (500.50)
37 conta_da_ana . sacar (200.00)
38 conta_da_ana . sacar (1500.00) # Saldo insuficiente
13.4. Métodos Especiais (Dunder Methods) - Além do __init__ 269

Código 13.8: Classe ContaBancaria com métodos de operação

Interação entre Atributos e Métodos


Os exemplos demonstram a simbiose entre atributos e métodos: os atributos guardam
o estado do objeto, e os métodos usam self para acessar e modificar esse estado,
definindo o comportamento dinâmico dos objetos.

13.4 Métodos Especiais (Dunder Methods) - Além do


__init__
Você já conheceu um método especial, o __init__, que é fundamental para inicializar
nossos objetos. Python possui muitos outros métodos especiais, facilmente reconhecíveis por
terem dois underscores no início e dois no final do seus nomes (ex: __algum_nome__).
Por causa desses “double underscores”, eles são apelidados de “dunder methods”.
Esses dunder methods não são chamados diretamente pelo seu nome (ex:
meu_objeto.__str__() é raro). Em vez disso, o Python os chama automaticamente em
resposta a certas operações ou funções embutidas. Ao defini-los em nossas classes, podemos
controlar como nossos objetos se comportam.

13.4.1 O Método __str__(self)


O método __str__(self) é chamado quando você usa str(meu_objeto) ou
print(meu_objeto). Ele deve retornar uma string que seja uma representação informal,
amigável e legível do objeto.
1 class Livro :
2 def __init__ ( self , titulo_livro , autor_livro , numero_paginas ) :
3 self . titulo = titulo_livro
4 self . autor = autor_livro
5 self . paginas = numero_paginas
6
7 def __str__ ( self ) : # Define como o objeto Livro ser á " impresso "
8 " " " Retorna uma representa ç ã o em string amig á vel do livro . " " "
9 return f " ’{ self . titulo } ’ por { self . autor } ({ self . paginas }
p á ginas ) "
10

11 livro_python = Livro ( " Python Descomplicado " , " Alan Afif Helal " , 700)
12 livro_aventura = Livro ( " O guia do mochileiro das gal á xias " ,
" Douglas Adams " , 208)
13
14 print ( livro_python )
15 print ( str ( livro_aventura ) )

Código 13.9: Definindo e usando o método __str__ na classe Livro

13.4.2 O Método __repr__(self)


O método __repr__(self) (de “representation”) é chamado por repr(meu_objeto).
Ele deve retornar uma string que seja uma representação oficial e inequívoca do objeto,
270 Capítulo 13. Trabalhando (Mais) com Classes e Objetos

idealmente uma que poderia recriar o objeto. É focado no desenvolvedor e na depuração.


Se __str__ não estiver definido, print() usa __repr__. No console interativo, digitar o
nome do objeto geralmente mostra o __repr__.
1 class Ponto2D :
2 def __init__ ( self , coord_x , coord_y ) :
3 self . x = coord_x
4 self . y = coord_y
5
6 def __str__ ( self ) : # Representa ç ã o para o usu á rio final
7 return f " Coordenadas : ({ self . x } , { self . y }) "
8
9 def __repr__ ( self ) : # Representa ç ã o para o desenvolvedor
10 return f " Ponto2D ( coord_x ={ self . x } , coord_y ={ self . y }) "
11
12 p1 = Ponto2D (3 , 7)
13 print ( str ( p1 ) ) # Chama p1 . __str__ ()
14 print ( repr ( p1 ) ) # Chama p1 . __repr__ ()
15 # No console interativo : p1 ( mostraria o __repr__ )

Código 13.10: Definindo __str__ e __repr__ na classe Ponto2D

13.4.3 Outros Dunder Methods Comuns (Breve Menção)


Existe uma grande quantidade de outros dunder methods. Alguns exemplos:

• __len__(self): Para que len(meu_objeto) funcione. Deve retornar um inteiro.

• __eq__(self, outro_objeto): Para definir o comportamento do operador de igual-


dade ==. Deve retornar True ou False.

• Operadores Aritméticos (ex: __add__, __sub__, __mul__): Permitem definir o


comportamento dos operadores aritméticos (+, -, *) para os objetos da sua classe.

• Acesso a Itens (ex: __getitem__, __setitem__): Permitem que seus objetos se


comportem como listas ou dicionários em relação ao acesso por colchetes [].

A Importância dos Dunder Methods


Os métodos especiais (dunder methods) são uma parte fundamental do que torna o
Python flexível. Ao implementá-los, você define como seus objetos interagem com a própria
linguagem, seus operadores e funções embutidas, tornando suas classes mais intuitivas e
“Pythônicas”. Para iniciantes, __init__ e __str__ são os mais imediatamente úteis.

13.5 “Escondendo” Atributos: Convenções para Atribu-


tos “Privados”
No Capítulo 12, mencionamos o encapsulamento como um pilar da POO, que inclui
proteger os dados internos de um objeto. Python adota uma filosofia de “Somos todos
adultos aqui” e não possui modificadores de acesso estritos como private de outras
linguagens. Em vez disso, usa convenções de nomenclatura.
13.5. “Escondendo” Atributos: Convenções para Atributos “Privados” 271

Convenção do Underscore Único (_nome_do_atributo_ou_metodo)


Um nome que começa com um único underscore é tratado como “para uso interno” ou
“não público”.

• Significado: É um sinal para outros programadores (e para você mesmo) de que


o membro é um detalhe de implementação e não deve ser acessado diretamente de
fora da classe.

• Não é uma restrição técnica: O Python não impede o acesso. É um “acordo de


cavalheiros”.

1 class Pessoa :
2 def __init__ ( self , nome_pessoa , ano_nascimento ) :
3 self . nome_publico = nome_pessoa
4 self . _ano_nascimento = ano_nascimento # " Protegido " por
conven ç ã o
5
6 def o b t e r _ i d a d e _ a p r o x i m a d a ( self , ano_atual =2025) :
7 return ano_atual - self . _ano_nascimento
8

9 def _ m et o do _ l og _ in t e rn o ( self , mensagem ) : # M é todo interno


10 print ( f " [ LOG ] { mensagem } " )
11
12 def apresentar ( self ) :
13 self . _m e to d o _l o g_ i n te r no ( f " Apresentando
{ self . nome_publico } " )
14 idade = self . o b t e r _ i d a d e _ a p r o x i m a d a ()
15 print ( f " Ol á , meu nome é { self . nome_publico } e tenho { idade }
anos . " )
16
17 joana = Pessoa ( " Joana D ’ Arc " , 1995)
18 joana . apresentar ()
19
20 # Acesso direto poss í vel , mas n ã o recomendado :
21 print ( f " Ano de nascimento ( acesso direto ) : { joana . _ano_nascimento } " )
22 # joana . _m e to d o _l o g_ i nt e r no (" Teste externo ") # Tamb é m n ã o
recomendado

Código 13.11: Usando underscore único para indicar atributo interno

Name Mangling (Mutilação de Nomes) com Duplo Underscore


(__nome_do_atributo)
Quando um nome de atributo ou método dentro de uma classe começa com dois
underscores (__) e não termina com mais de um underscore (ex: self.__dado_secreto),
Python automaticamente altera o nome para _NomeDaClasse__nome_do_atributo.

• Propósito Principal: Evitar conflitos de nomes acidentais quando se usa herança


(Capítulo 14).

• Não é Privacidade Real: Ainda é possível acessar o atributo usando o nome “mu-
tilado” (objeto._NomeDaClasse__atributo), mas isso é fortemente desencorajado.
272 Capítulo 13. Trabalhando (Mais) com Classes e Objetos

1 class CofreDigital :
2 def __init__ ( self , s e n h a_ m e s t r a _ i n i c i a l ) :
3 self . __senha_mestra = s e n h a _ m e s t r a_ i n i c i a l # Sofrer á name
mangling
4 self . _itens_guardados = []
5
6 def adicionar_item ( self , item , senha_tentativa ) :
7 if senha_tentativa == self . __senha_mestra : # Acesso interno
ao nome original
8 self . _itens_guardados . append ( item )
9 print ( f " Item ’{ item } ’ adicionado ao cofre . " )
10 return True
11 else :
12 print ( " Senha mestra incorreta ! " )
13 return False
14
15 meu_cofre = CofreDigital ( " PythonSeguro123 " )
16 meu_cofre . adicionar_item ( " Joia Rara " , " PythonSeguro123 " )
17
18 # Tentativa de acesso direto a __senha_mestra ( GERA AttributeError )
19 # print ( meu_cofre . __senha_mestra )
20
21 # Acesso usando o nome " mutilado " ( N Ã O FA Ç A em c ó digo normal !)
22 print ( f " Senha ( acesso n ã o recomendado ) :
{ meu_cofre . _ C o f r e D i g i t a l _ _ s e n h a _ m e s t r a } " )

Código 13.12: Exemplo de Name Mangling com duplo underscore

Getters, Setters e Propriedades (Breve Menção)


Se um controle mais fino sobre o acesso ou modificação de atributos for realmente neces-
sário (ex: para validação), Python oferece mecanismos como métodos “getter” e “setter”, ou,
de forma mais Pythônica, o uso do decorador @property e @nome_atributo.setter para
criar propriedades gerenciadas. Estes permitem que o acesso [Link] chame
métodos internamente. (Estes tópicos mais avançados podem ser explorados no Capítulo
15.)
A filosofia do Python para encapsulamento confia em convenções (especialmente o _)
para indicar “uso interno”. O __ é mais para evitar conflitos em herança.

Concluindo o Capítulo 13: Aprofundando em Classes e


Objetos
Neste capítulo, expandimos nosso entendimento sobre como trabalhar com classes e
objetos em Python, construindo sobre os conceitos fundamentais que introduzimos no
Capítulo 12.
Começamos com uma revisão para solidificar a ideia de classes como moldes e objetos
como instâncias, e o papel central do método __init__ e do parâmetro self. Em seguida,
diferenciamos os atributos de instância, que guardam o estado individual de cada objeto,
dos atributos de classe, que são compartilhados por todas as instâncias de uma classe.
Reforçamos nosso conhecimento sobre métodos de instância com exemplos mais
elaborados, vendo como eles definem os comportamentos dos nossos objetos. Demos um
13.5. “Escondendo” Atributos: Convenções para Atributos “Privados” 273

passo importante ao explorar os métodos especiais (ou dunder methods), com foco
especial no __str__ para criar representações em string amigáveis dos nossos objetos, e
uma breve menção ao __repr__.
Finalmente, discutimos as convenções de Python para o encapsulamento de
atributos, entendendo como o underscore simples (_nome) sinaliza um membro para uso
interno e como o duplo underscore (__nome) ativa o mecanismo de name mangling.
Com as ferramentas e conceitos abordados neste capítulo, você está agora mais pre-
parado para definir classes que modelam entidades com maior riqueza de detalhes, criar
objetos com estados distintos e comportamentos bem definidos, e entender as convenções
para o acesso e a visibilidade dos membros de uma classe. Estes são passos cruciais para
dominar a Programação Orientada a Objetos.
No próximo capítulo, exploraremos dois dos pilares mais poderosos e transformadores
da Programação Orientada a Objetos: a Herança e o Polimorfismo. Aprenderemos
como criar hierarquias de classes, reutilizar código de forma eficaz e escrever programas
ainda mais flexíveis e extensíveis.
Capítulo 14

Herança e Polimorfismo

Nos Capítulos 12 e 13, construímos uma base sólida sobre classes e objetos em Python.
Aprendemos a definir nossas próprias classes com atributos (que representam o estado) e
métodos (que definem o comportamento), e como criar instâncias (objetos) dessas classes.
Vimos também métodos especiais como __init__ para inicializar objetos e __str__ para
sua representação em string, além de convenções para atributos “internos”.
Até agora, nossas classes foram, em grande parte, entidades isoladas. No entanto,
no mundo real (e em sistemas de software complexos), as “coisas” frequentemente têm
relações entre si, especialmente relações de generalização e especialização. Por exemplo,
um “cachorro” é um tipo específico de “animal”; um “carro esportivo” é um tipo específico
de “carro”.
A Programação Orientada a Objetos nos oferece um mecanismo poderoso para modelar
essas relações e promover uma reutilização de código ainda maior: a herança. Juntamente
com a herança, exploraremos outro pilar fundamental da POO, o polimorfismo, que
permite tratar objetos de diferentes classes de forma uniforme, mas com comportamentos
específicos a cada uma.

14.1 Introdução à Herança: Criando Hierarquias de


Classes
A herança é um dos conceitos centrais da Programação Orientada a Objetos. Ela é
um mecanismo que permite que uma nova classe (que chamaremos de subclasse ou classe
filha/derivada) adquira (ou “herde”) as propriedades (atributos) e os comportamentos
(métodos) de uma classe já existente (que chamaremos de superclasse ou classe mãe/base).

14.1.1 O Conceito de “É um(a)” (Is-A Relationship)


A principal ideia por trás da herança é modelar uma relação do tipo “é um(a)” (ou
is-a relationship, em inglês). Se uma Classe B herda da Classe A, isso significa que um
objeto da Classe B é um tipo de objeto da Classe A, mas com possíveis especializações ou
adições. É uma relação de generalização/especialização: a superclasse é mais genérica, e a
subclasse é uma versão mais específica.
Pense nos seguintes exemplos:

• Um Cachorro é um Animal. (Animal é a superclasse, Cachorro é a subclasse)

274
14.1. Introdução à Herança: Criando Hierarquias de Classes 275

• Um Gato é um Animal. (Gato também é uma subclasse de Animal)


• Um CarroEsportivo é um Carro.
• Um Gerente é um Funcionario.
• Um Retangulo é uma FormaGeometrica.
Nesses casos, a subclasse compartilha características comuns com a superclasse, mas
também pode ter seus próprios atributos e métodos únicos ou modificar (sobrescrever) os
herdados para se adequar à sua especificidade.
Terminologia Importante:
• Superclasse (Superclass): A classe da qual se herda. Também é comumente
chamada de classe mãe ou classe base.
• Subclasse (Subclass): A classe que herda da superclasse. Também é chamada de
classe filha ou classe derivada.

14.1.2 Por que Usar Herança? Os Principais Benefícios


A herança não é apenas uma forma de organizar classes; ela traz vantagens significativas
para o design e a manutenção do software, sendo um dos principais impulsionadores da
reutilização de código e da criação de sistemas flexíveis.
1. Reutilização de Código (Princípio DRY - Don’t Repeat Yourself ):
Este é, talvez, o benefício mais celebrado e direto. Atributos e métodos que são
comuns a várias classes podem ser definidos uma única vez na superclasse. As
subclasses herdam automaticamente essas funcionalidades, evitando a necessidade
de copiar e colar o mesmo código em múltiplos lugares. Isso torna os programas:
• Menores e Mais Concisos: Menos código duplicado significa um código-fonte
mais enxuto.
• Mais Fáceis de Manter: Se você precisar corrigir um bug ou fazer uma
melhoria em uma funcionalidade comum, você a altera apenas na superclasse, e
todas as subclasses que a herdam se beneficiam automaticamente da mudança.
• Menos Propensos a Erros: Com menos código duplicado, há menos lugares
para cometer erros.
2. Organização Lógica e Criação de Hierarquias:
A herança permite que você organize suas classes em hierarquias que refletem relações
lógicas e classificações do domínio do seu problema ou do mundo real. Por exemplo,
no domínio biológico, temos hierarquias como Animal → Mamífero → Primata →
Humano. No software, podemos ter estruturas como Veiculo que se especializa em
Carro ou Motocicleta. Essa estrutura hierárquica torna o sistema como um todo
mais compreensível.
3. Extensibilidade:
A herança facilita a extensão de funcionalidades existentes de forma elegante. Se você
tem uma classe base bem definida e testada, pode criar novas subclasses para adicio-
nar comportamentos ou atributos específicos, ou para modificar comportamentos
existentes, sem precisar alterar o código original da superclasse.
276 Capítulo 14. Herança e Polimorfismo

4. Polimorfismo (Antecipando o Tópico):


Como veremos em detalhes mais adiante neste capítulo (Seção 14.6), a herança é
uma das principais bases para o polimorfismo. O polimorfismo permite que objetos
de diferentes subclasses sejam tratados de forma uniforme através da interface da
superclasse, mas cada um execute comportamentos de acordo com sua própria
implementação especializada.

14.1.3 Uma Analogia Simples: O Reino Animal (Novamente)


Vamos revisitar nossa analogia: Poderíamos ter uma classe geral Animal. Todo animal
tem um nome, uma idade e pode, por exemplo, comer() e emitir_som(). Agora, queremos
ser mais específicos. Um Cachorro é um Animal.

• Em vez de redefinir nome, idade, comer() e emitir_som() para a classe Cachorro,


podemos fazer Cachorro herdar de Animal.

• O Cachorro automaticamente terá os atributos nome e idade e saberá como comer().

• Podemos especializar o método emitir_som() para que o Cachorro faça “Au Au!”.

• O Cachorro também pode ter atributos próprios, como raca, e métodos pró-
prios, como abanar_rabo().

Da mesma forma, um Gato também é um Animal. Ele herdaria as características comuns,


poderia ter seu emitir_som() especializado para “Miau!” e métodos próprios como
arranhar_sofa().
Essa estrutura não apenas reutiliza o que é comum (definido em Animal), mas também
organiza os tipos de forma lógica e permite especializações.
A herança é, portanto, um conceito poderoso e um mecanismo fundamental da POO
para construir relações significativas entre classes, promover a reutilização de código e
criar sistemas de software mais organizados, flexíveis e extensíveis. Na próxima seção,
veremos a sintaxe específica de como implementar a herança em Python.

14.2 Sintaxe da Herança em Python: Definindo Sub-


classes
Agora que entendemos o conceito de herança e seus benefícios, vamos ver como
implementá-la em Python. A sintaxe para fazer uma classe herdar de outra é bastante
simples e intuitiva.

Como uma Classe Herda de Outra


Para definir uma subclasse que herda de uma superclasse, você especifica o nome da
superclasse entre parênteses logo após o nome da subclasse na linha de definição da classe.
A sintaxe geral é:
1 class Superclasse :
2 # Defini ç õ es da superclasse ( atributos e m é todos )
3 pass
4
14.2. Sintaxe da Herança em Python: Definindo Subclasses 277

5 class Subclasse ( Superclasse ) : # Subclasse herda de Superclasse


6 # Defini ç õ es adicionais ou modificadas da subclasse
7 # Se n ã o houver nada novo ou modificado , pode usar ’ pass ’
8 pass

Código 14.1: Sintaxe geral da herança


É isso! A Subclasse agora herda automaticamente todos os atributos e métodos (que não
sejam “privados” de uma forma muito específica com duplo underscore na superclasse) da
Superclasse.

Exemplo Simples: As Classes Animal e Cachorro


Vamos criar uma superclasse Animal bem simples e uma subclasse Cachorro que herda
dela.

1. Definindo a Superclasse Animal:


1 # Defini ç ã o da Superclasse Animal
2 class Animal :
3 def __init__ ( self , nome_do_animal ) :
4 " " " Construtor da classe Animal . " " "
5 self . nome = nome_do_animal # Todo animal ter á um
nome
6 print ( f " Um animal chamado ’{ self . nome } ’ foi
criado . " )
7
8 def fazer_som ( self ) :
9 " " " M é todo para o animal emitir um som gen é rico . " " "
10 print ( f " { self . nome } faz um som gen é rico de
animal . " )
11
12 def comer ( self ) :
13 " " " M é todo para o animal comer . " " "
14 print ( f " { self . nome } est á comendo . " )

Código 14.2: Definição da superclasse Animal

Nossa classe Animal tem um construtor que define o atributo de instância nome, e
dois métodos: fazer_som() e comer().

2. Definindo a Subclasse Cachorro que Herda de Animal:


Por enquanto, nossa classe Cachorro não adicionará nenhuma característica ou
comportamento novo, nem modificará os herdados. Ela simplesmente herdará tudo
de Animal.
1 # Defini ç ã o da Subclasse Cachorro , que herda da classe
Animal
2 class Cachorro ( Animal ) : # A sintaxe ( Animal ) indica a
heran ç a
3 # Como n ã o vamos adicionar ou modificar nada por
enquanto ,
4 # usamos ’ pass ’. O Cachorro j á herdou nome ,
fazer_som () e comer () .
5 pass

Código 14.3: Definição da subclasse Cachorro


278 Capítulo 14. Herança e Polimorfismo

3. Criando e Usando Objetos:


Agora, vamos criar instâncias tanto da superclasse Animal quanto da subclasse
Cachorro e ver como a herança funciona.
1 # Criando um objeto da superclasse Animal
2 print ( " --- Criando um Animal gen é rico ---" )
3 anima l_mister ioso = Animal ( " Criatura da Floresta " )
4 anima l_mister ioso . fazer_som ()
5 anima l_mister ioso . comer ()
6 print ( f " Nome do animal misterioso :
{ animal_m isterios o . nome } " )
7
8 print ( " \n - - - Criando um objeto da subclasse Cachorro ---" )
9 # Ao criar um objeto Cachorro , o Python procura um
__init__ em Cachorro .
10 # Como n ã o h á , ele usa o __init__ da superclasse Animal .
11 rex_o_cao = Cachorro ( " Rex " ) # " Rex " ser á passado para o
__init__ de Animal
12
13 # rex_o_cao pode usar os m é todos herdados de Animal :
14 rex_o_cao . fazer_som ()
15 rex_o_cao . comer ()
16

17 # E tamb é m possui o atributo ’ nome ’ herdado :


18 print ( f " O nome do nosso cachorro é : { rex_o_cao . nome } " )

Código 14.4: Usando objetos da superclasse e subclasse

Saída Esperada do Código Acima:

--- Criando um Animal genérico ---


Um animal chamado ’Criatura da Floresta’ foi criado.
Criatura da Floresta faz um som genérico de animal.
Criatura da Floresta está comendo.
Nome do animal misterioso: Criatura da Floresta

--- Criando um objeto da subclasse Cachorro ---


Um animal chamado ’Rex’ foi criado.
Rex faz um som genérico de animal.
Rex está comendo.
O nome do nosso cachorro é: Rex

O que Aconteceu no Exemplo?


• A classe Cachorro foi definida como class Cachorro(Animal):, indicando que ela
herda da classe Animal.

• Como a classe Cachorro não definiu seu próprio método __init__, quando cri-
amos uma instância rex_o_cao = Cachorro("Rex"), o Python automaticamente
procurou e utilizou o método __init__ da sua superclasse, Animal.

• O objeto rex_o_cao pôde chamar os métodos fazer_som() e comer(), e acessar o


atributo nome, porque ele herdou todos esses membros da classe Animal.
14.3. O Método super().__init__(...) 279

E se a Subclasse Não Chamar o __init__ da Superclasse Correta-


mente?
Se a superclasse Animal tivesse um __init__ que exigisse argumentos (como o nosso),
e a subclasse Cachorro definisse seu próprio __init__ mas não chamasse explicitamente
o __init__ da Animal, então os atributos definidos no __init__ da Animal não seriam
inicializados para os objetos Cachorro. Essa situação, e a forma correta de lidar com ela
usando super().__init__(), é o tema da próxima seção.
A herança simples, como demonstrada, já nos mostra o poder da reutilização. Na pró-
xima seção, veremos como as subclasses podem chamar os construtores de suas superclasses
e começar a adicionar suas próprias especializações.

14.3 O Método super().__init__(...)


Na seção anterior, a subclasse Cachorro usou o __init__ da superclasse Animal impli-
citamente porque não tinha seu próprio __init__. Mas, o que acontece quandoasubclasse
precisa de seu próprio __init__ para inicializar seus atributos específicos?
Nesse cenário, se a subclasse define seu próprio __init__, ela se torna responsável por
chamar explicitamente o construtor (__init__) da superclasse. Isso garante que a parte
herdada do objeto seja corretamente inicializada.

Por que Chamar o __init__ da Superclasse?


A superclasse pode ter lógica de inicialização importante para seus próprios atributos
queasubclasse herda. Seasubclasse define seu __init__ e não chama o da superclasse, os
atributos da superclasse podem não ser inicializados, levando a erros.

A Função super()
Python oferece a função embutida super() para chamar métodos da superclasse,
incluindo o __init__. Sintaxe: super().__init__(argumentos_da_superclasse)
• super() retorna um objeto temporário da superclasse.

• Você não passa self explicitamente; Python cuida disso.

Exemplo Prático: Classe Gato Herda de Animal


1 class Animal :
2 def __init__ ( self , nome_animal , especie_animal = " Desconhecida " ) :
3 " " " Construtor da classe Animal . " " "
4 print ( f " DEBUG ( Animal __init__ ) : Inicializando
’{ nome_animal } ’. " )
5 self . nome = nome_animal
6 self . especie = especie_animal
7
8 def fazer_som ( self ) :
9 print ( f " { self . nome } ({ self . especie }) faz um som gen é rico . " )
10
11 def comer ( self ) :
12 print ( f " { self . nome } est á comendo . " )
280 Capítulo 14. Herança e Polimorfismo

13
14 class Gato ( Animal ) : # Gato herda de Animal
15 def __init__ ( self , nome_do_gato , cor_do_pelo ) :
16 " " " Construtor da subclasse Gato . " " "
17 print ( f " DEBUG ( Gato __init__ ) : Gato ’{ nome_do_gato } ’ sendo
criado ... " )
18
19 # Passo 1: Chamar o __init__ da superclasse ( Animal )
20 super () . __init__ ( nome_do_gato , especie_animal = " Felina " )
21
22 # Passo 2: Inicializar atributos espec í ficos de Gato
23 self . cor_pelo = cor_do_pelo
24 print ( f " DEBUG ( Gato __init__ ) : Atributo ’ cor_pelo ’
definido . " )
25
26 def miar ( self ) :
27 print ( f " { self . nome } ( um gato { self . cor_pelo }) diz : Miau ! " )
28

29 def fazer_som ( self ) : # Sobrescrevendo o m é todo da superclasse


30 self . miar ()
31
32 # Criando inst â ncias
33 bichano = Gato ( " Bichano " , " preto e branco " )
34 print ( f " \ nNome do gato : { bichano . nome } " )
35 print ( f " Esp é cie : { bichano . especie } " )
36 print ( f " Cor do Pelo : { bichano . cor_pelo } " )
37 bichano . comer ()
38 bichano . fazer_som ()

Código 14.5: Usando super().__init__() em uma subclasse


Ordem de Chamada: Geralmente, super().__init__(...) é a primeira linha no
__init__ da subclasse.

O que Acontece se super().__init__() Não For Chamado?


Seasubclasse tiver um __init__ e não chamar super().__init__(), os atributos da
superclasse que seriam inicializados no __init__ dela não existirão no objeto da subclasse
(a menos que criados de outra forma). Isso pode levar a AttributeError se métodos
herdados tentarem usar esses atributos.
1 # class AnimalSimples :
2 # def __init__ ( self , nome ) :
3 # self . nome = nome
4 # def exibir_nome ( self ) :
5 # print ( f " Nome : { self . nome }")
6
7 # class CaoSemSuper ( AnimalSimples ) :
8 # def __init__ ( self , raca ) :
9 # # N Ã O chamou super () . __init__ ( nome_para_animal )
10 # self . raca = raca
11 # # self . nome N Ã O ser á inicializado pela superclasse
12
13 # cao_problema = CaoSemSuper (" Labrador ")
14 # cao_problema . exibir_nome () # GERARIA AttributeError :
’ CaoSemSuper ’ object has no attribute ’ nome ’

Código 14.6: Exemplo de subclasse sem chamar super().__init__() (problemático)


14.4. Sobrescrevendo Métodos: Especializando Comportamentos 281

(O código acima está comentado para evitar erros de compilação no livro, mas ilustra o
problema.)

Alternativa Mais Antiga: Superclasse.__init__(self, ...)


Em Python 2, era comum chamar NomeDaSuperclasse.__init__(self, ...), pas-
sando self explicitamente. Em Python 3, super() é a forma moderna e recomendada,
especialmente por lidar melhor com herança múltipla.
Chamar super().__init__(...) é essencial para manter a cadeia de inicialização da
herança, garantindo que todas as partes do seu objeto sejam devidamente configuradas.

14.4 Sobrescrevendo Métodos: Especializando Compor-


tamentos
A herança permite que uma subclasse receba automaticamente os métodos definidos
em sua superclasse. No entanto, muitas vezes, uma subclasse precisa que um método
herdado se comporte de uma maneira diferente ou mais específica. Essa capacidade de uma
subclasse fornecer sua própria implementação para um método que já existe na superclasse
é chamada de sobrescrita de métodos (ou method overriding).

O que é Sobrescrita de Métodos?


A sobrescrita ocorre quando uma subclasse define um método com o mesmo nome
e, geralmente, a mesma assinatura (parâmetros) de um método na sua superclasse.
Quando esse método é chamado em um objeto da subclasse, a versão da subclasse é
executada, “substituindo” a da superclasse para aquele objeto.

Por que Sobrescrever Métodos?


• Especialização de Comportamento: Para adaptar um comportamento geral da
superclasse às necessidades particulares da subclasse (ex: um Cachorro late, um
Gato mia, ambos especializando um fazer_som() genérico de Animal).

• Implementação Concreta de Métodos Abstratos (Antecipação): Superclasses


podem definir métodos que devem ser implementados por subclasses.

Exemplo Prático: Especializando fazer_som


Vamos revisitar nossas classes Animal, Cachorro e Gato.
1 class Animal :
2 def __init__ ( self , nome_animal ) :
3 self . nome = nome_animal
4 def fazer_som ( self ) :
5 print ( f " { self . nome } faz um som gen é rico ... " )
6 def comer ( self ) :
7 print ( f " { self . nome } est á comendo . " )
8
9 class Cachorro ( Animal ) :
10 def __init__ ( self , nome_cao , raca_cao ) :
282 Capítulo 14. Herança e Polimorfismo

11 super () . __init__ ( nome_cao )


12 self . raca = raca_cao
13
14 # Sobrescrevendo o m é todo fazer_som
15 def fazer_som ( self ) :
16 print ( f " { self . nome } ( um { self . raca }) diz : Au ! Au ! " )
17
18 class Gato ( Animal ) :
19 def __init__ ( self , nome_gato , cor_do_pelo ) :
20 super () . __init__ ( nome_gato )
21 self . cor_pelo = cor_do_pelo
22

23 # Sobrescrevendo o m é todo fazer_som


24 def fazer_som ( self ) :
25 print ( f " { self . nome } ( um gato { self . cor_pelo }) diz : Miau ! " )
26
27 animal_generico = Animal ( " Coisa " )
28 rex = Cachorro ( " Rex " , " Labrador " )
29 felix = Gato ( " Felix " , " Siam ê s " )
30
31 animal_generico . fazer_som ()
32 rex . fazer_som () # Chama Cachorro . fazer_som ()
33 felix . fazer_som () # Chama Gato . fazer_som ()
34

35 rex . comer () # M é todo herdado e n ã o sobrescrito

Código 14.7: Sobrescrevendo o método fazer_som

Estendendo o Comportamento da Superclasse com super()


Às vezes, você não quer substituir completamente o método da superclasse, mas sim
adicionar algo a ele. Dentro do método sobrescrito na subclasse, use
super().nome_do_metodo_da_superclasse(...) para chamar a implementação da su-
perclasse.
1 class Funcionario :
2 def __init__ ( self , nome_func , id_func , salario_base ) :
3 self . nome = nome_func
4 self . id = id_func
5 self . salario = salario_base
6
7 def exibir_detalhes ( self ) :
8 print ( f " ID : { self . id } , Nome : { self . nome } , Sal á rio Base :
R$ { self . salario :.2 f } " )
9
10 class Gerente ( Funcionario ) :
11 def __init__ ( self , nome_ger , id_ger , salario_base_ger ,
departamento_ger , bonus_ger =0) :
12 super () . __init__ ( nome_ger , id_ger , salario_base_ger )
13 self . departamento = departamento_ger
14 self . bonus = bonus_ger
15
16 def exibir_detalhes ( self ) : # Sobrescrevendo
17 print ( " --- Detalhes do Gerente ---" )
18 super () . exibir_detalhes () # Chama o m é todo da superclasse
19 # Adiciona informa ç õ es espec í ficas do Gerente
20 print ( f " Departamento : { self . departamento } " )
14.5. Adicionando Novos Métodos e Atributos na Subclasse 283

21 s a l a r i o _ t o t a l _ g e r e n t e = self . salario + self . bonus


22 print ( f " Sal á rio Total ( com b ô nus ) :
R$ { s a l a r i o _ t o t a l _ g e r e n t e :.2 f } " )
23 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - " )
24
25 func_comum = Funcionario ( " Carlos " , " F001 " , 3500.00)
26 gerente_ana = Gerente ( " Ana " , " G001 " , 7500.00 , " Tecnologia " , 1500.00)
27
28 func_comum . exibir_detalhes ()
29 print () # Linha em branco
30 gerente_ana . exibir_detalhes ()

Código 14.8: Sobrescrevendo e estendendo método com super()

A sobrescrita de métodos é essencial na POO para permitir que subclasses personalizem


ou estendam comportamentos herdados, sendo fundamental para o polimorfismo.

14.5 Adicionando Novos Métodos e Atributos na Sub-


classe
A herança não se limita apenas a permitir que uma subclasse use ou modifique o
que foi definido na superclasse. Uma parte crucial da especialização é a capacidade da
subclasse de adicionar seus próprios atributos e métodos, aqueles que são relevantes
especificamente para ela, mas não necessariamente paraasuperclasse mais genérica.
Ao herdar,asubclasse já começa com todas as características e comportamentos da
superclasse. A partir daí, ela pode:

1. Usar o que foi herdado diretamente.

2. Sobrescrever métodos herdados para especializar comportamentos (Seção 14.4).

3. Adicionar novos atributos e novos métodos para estender suas funcionalidades.

Adicionando Novos Atributos de Instância na Subclasse


A forma mais comum de adicionar atributos específicos de uma subclasse é dentro do
seu próprio método __init__, geralmente após chamar super().__init__(...).
1 class Funcionario :
2 def __init__ ( self , nome_funcionario , id_funcionario ) :
3 self . nome = nome_funcionario
4 self . id_registro = id_funcionario
5 print ( f " DEBUG : Funcion á rio ’{ self . nome } ’ inicializado . " )
6
7 def o b te r _i d e nt i fi c a ca o ( self ) :
8 return f " ID : { self . id_registro } , Nome : { self . nome } "
9
10 class Desenvolvedor ( Funcionario ) :
11 def __init__ ( self , nome_dev , id_dev , linguagem_principal ,
nivel_dev = " J ú nior " ) :
12 super () . __init__ ( nome_dev , id_dev ) # Chama o __init__ de
Funcionario
13
14 # Atributos ESPEC Í FICOS da subclasse Desenvolvedor
284 Capítulo 14. Herança e Polimorfismo

15 self . l i n g u a g e m _ p r o g r a m a c a o = l i n gu a ge m _p r i nc i pa l
16 self . nivel = nivel_dev
17 print ( f " DEBUG : Dev ’{ self . nome } ’ ({ self . nivel }) em
{ self . l i n g u a g e m _ p r o g r a m a c a o } inicializado . " )
18
19 dev_ana = Desenvolvedor ( " Ana Pereira " , " D001 " , " Python " , " Pleno " )
20 print ( f " \ n { dev_ana . o b te r _i d e nt i fi c a ca o () } " )
21 print ( f " Linguagem : { dev_ana . l i n g u a g e m _ p r o g r a m a c a o } , N í vel :
{ dev_ana . nivel } " )

Código 14.9: Adicionando atributos específicos na subclasse Desenvolvedor


Aqui, linguagem_programacao e nivel são atributos de instância específicos de Desen-
volvedor.

Adicionando Novos Métodos na Subclasse


Você pode definir métodos completamente novos em uma subclasse. Esses métodos só
estarão disponíveis para os objetos daquela subclasse (e suas futuras subclasses).
1 class Animal :
2 def __init__ ( self , nome_animal ) :
3 self . nome = nome_animal
4 def comer ( self ) :
5 print ( f " { self . nome } est á comendo . " )
6 def fazer_som ( self ) :
7 print ( f " { self . nome } faz um som gen é rico . " )
8
9 class Cachorro ( Animal ) :
10 def __init__ ( self , nome_cao , raca_cao ) :
11 super () . __init__ ( nome_cao )
12 self . raca = raca_cao
13
14 def fazer_som ( self ) : # M é todo sobrescrito
15 print ( f " { self . nome } ({ self . raca }) diz : Au ! Au ! " )
16
17 # NOVO M É TODO espec í fico para Cachorro
18 def buscar_bolinha ( self , objeto = " bolinha " ) :
19 print ( f " { self . nome } ({ self . raca }) correu e buscou a
{ objeto }! " )
20
21 class Gato ( Animal ) :
22 def __init__ ( self , nome_gato , cor_pelo ) :
23 super () . __init__ ( nome_gato )
24 self . cor_pelo = cor_pelo
25
26 def fazer_som ( self ) : # M é todo sobrescrito
27 print ( f " { self . nome } ( um gato { self . cor_pelo }) diz : Miau ! " )
28

29 # NOVO M É TODO espec í fico para Gato


30 def a f i a r _ u n h a s _ n o _ m o v e l ( self , movel = " sof á " ) :
31 print ( f " { self . nome } est á discretamente afiando as unhas no
{ movel }... " )
32
33 rex_esperto = Cachorro ( " Rex " , " Border Collie " )
34 felix_folgado = Gato ( " Felix " , " Laranja " )
35
36 rex_esperto . buscar_bolinha ( " frisbee " )
14.6. Introdução ao Polimorfismo: “Muitas Formas” 285

37 felix_folgado . a f i a r_ u n h a s _ n o _ m o v e l ( " poltrona " )


38
39 # animal_comum = Animal (" Criatura ")
40 # animal_comum . buscar_bolinha () # GERARIA AttributeError

Código 14.10: Adicionando métodos específicos às subclasses Cachorro e Gato

No exemplo, buscar_bolinha() é um comportamento exclusivo de Cachorro, e


afiar_unhas_no_movel() é de Gato.

A Subclasse como uma Especialização e Extensão


A subclasse herda o que é geral, pode especializar (sobrescrevendo métodos) e pode
estender (adicionando novos atributos e métodos). Ela se torna um tipo mais específico e,
frequentemente, mais rico em funcionalidades queasuperclasse. Isso é fundamental para
criar hierarquias de classes úteis e bem definidas.

14.6 Introdução ao Polimorfismo: “Muitas Formas”


A herança, como vimos, é poderosa para reutilização e organização. Agora, vamos
explorar o polimorfismo, o quarto pilar da POO que mencionamos no Capítulo 12. A
palavra “polimorfismo” significa, literalmente, “muitas formas” (poly = muitas, morphos =
forma). Na POO, refere-se à capacidade de objetos de diferentes classes serem tratados
através de uma interface comum (como um método com o mesmo nome), mas cada um
responder a essa interface de acordo com sua própria implementação específica.

Polimorfismo Através da Herança e Sobrescrita de Métodos


A forma mais clássica de polimorfismo ocorre com herança e sobrescrita de métodos.
Se subclasses herdam e sobrescrevem um método da superclasse, podemos chamar esse
método em objetos dessas diferentes subclasses, e cada um executará sua versão específica.
O código que chama o método não precisa saber o tipo exato do objeto.
Exemplo Prático: Uma Orquestra de Animais
1 # Defini ç õ es simplificadas das classes Animal , Cachorro , Gato
2 class Animal :
3 def __init__ ( self , nome_animal ) : self . nome = nome_animal
4 def fazer_som ( self ) : print ( f " { self . nome } faz um som gen é rico . " )
5 class Cachorro ( Animal ) :
6 def __init__ ( self , nome_cao ) : super () . __init__ ( nome_cao )
7 def fazer_som ( self ) : print ( f " { self . nome } diz : Au Au ! " ) #
Sobrescrito
8 class Gato ( Animal ) :
9 def __init__ ( self , nome_gato ) : super () . __init__ ( nome_gato )
10 def fazer_som ( self ) : print ( f " { self . nome } diz : Miau ! " ) #
Sobrescrito
11
12 meus_bichos = [ Cachorro ( " Rex " ) , Gato ( " Felix " ) , Animal ( " Criatura " ) ]
13
14 print ( " --- A Orquestra dos Bichos ---" )
15 for bicho_da_vez in meus_bichos :
16 # Chamamos fazer_som () , e Python executa a vers ã o correta
17 print ( f " Vez de { bicho_da_vez . nome }: " , end = " " )
286 Capítulo 14. Herança e Polimorfismo

18 bicho_da_vez . fazer_som ()

Código 14.11: Demonstrando polimorfismo com animais

No laço acima, bicho_da_vez.fazer_som() invoca o método apropriado para cada tipo


de animal, demonstrando o polimorfismo.

Polimorfismo e “Duck Typing” em Python


Em Python, o polimorfismo é fortemente associado ao conceito de “Duck Typing”
(Tipagem de Pato): “Se anda como um pato, nada como um pato e faz quack como um
pato, então provavelmente é um pato.”
Isso significa que o tipo exato de um objeto é menos importante do que os métodos
e atributos que ele possui (sua “interface”). Se um objeto “sabe” responder a uma
chamada de método, Python permite que você chame esse método nele, independentemente
de sua classe ou herança.
Exemplo Prático de Duck Typing:
1 class PatoDomestico :
2 def emitir_som ( self ) : print ( " Pato Dom é stico : Quack ! Quack ! " )
3 def mover ( self ) : print ( " Pato Dom é stico anda e nada . " )
4
5 class PardalUrbano :
6 def emitir_som ( self ) : print ( " Pardal Urbano : Piu ! Piu ! " )
7 def mover ( self ) : print ( " Pardal Urbano voa agilmente . " )
8
9 class RoboAspirador : # Classe n ã o relacionada a animais
10 def emitir_som ( self ) : print ( " Rob ô Aspirador : Vruuum ... " )
11 def mover ( self ) : print ( " Rob ô Aspirador desliza pelo ch ã o . " )
12
13 def demonstrar_ser ( q u a l q u e r _ s e r _ c o m _ s o m _ e _ m o v i m e n t o ) :
14 print ( f " \n - - - Demonstrando capacidades ---" )
15 q u a l q u e r _ s e r _ c o m _ s o m _ e _ m o v i m e n t o . emitir_som () # Espera o
m é todo
16 q u a l q u e r _ s e r _ c o m _ s o m _ e _ m o v i m e n t o . mover () # Espera o
m é todo
17
18 patolino = PatoDomestico ()
19 ligeirinho = PardalUrbano ()
20 roomba_do_alan = RoboAspirador ()
21
22 demonstrar_ser ( patolino )
23 demonstrar_ser ( ligeirinho )
24 demonstrar_ser ( roomba_do_alan ) # Funciona com todos !

Código 14.12: Exemplo de Duck Typing com diferentes "seres"

A função demonstrar_ser não se importa com o tipo do objeto, apenas se ele implementa
os métodos emitir_som() e mover().

Benefícios do Polimorfismo
• Flexibilidade: Permite escrever código que opera com objetos de diferentes tipos
de forma genérica.
14.7. Herança Múltipla: Herdando de Várias Fontes 287

• Extensibilidade: Facilita adicionar novos tipos ao sistema que se “encaixam” na


lógica existente, desde que implementem a interface esperada.

• Código Mais Limpo e Desacoplado: Reduz a necessidade de if/elif baseados


no tipo do objeto.

O polimorfismo leva a um design de software mais flexível e adaptável.

14.7 Herança Múltipla: Herdando de Várias Fontes


Até agora, nossos exemplos de herança foram de herança simples (uma subclasse
herda de uma única superclasse). No entanto, Python suporta herança múltipla, onde
uma classe pode herdar de duas ou mais superclasses.

O que é Herança Múltipla?


Uma classe derivada herda atributos e métodos de múltiplas classes base. Sintaxe:
class MinhaSubclasse(SuperclasseA, SuperclasseB, ...):
1 class SuperclasseA :
2 def metodo_da_A ( self ) : print ( " M é todo da Superclasse A " )
3 class SuperclasseB :
4 def metodo_da_B ( self ) : print ( " M é todo da Superclasse B " )
5
6 class MinhaSubclasse ( SuperclasseA , SuperclasseB ) : # Herda de A e B
7 def m e to d o_ d a _s u bc l a ss e ( self ) : print ( " M é todo da MinhaSubclasse " )
8
9 obj = MinhaSubclasse ()
10 obj . metodo_da_A ()
11 obj . metodo_da_B ()

Código 14.13: Exemplo simples de herança múltipla

Potenciais Benefícios
Pode ser útil para reutilizar funcionalidades de diferentes fontes ou para “misturar”
capacidades (conceito de “Mixins”).

Complexidades e Cuidados
• O Problema do Diamante (Diamond Problem): Ambiguidade pode surgir se
múltiplas superclasses (que compartilham um ancestral comum) definem ou herdam
um método com o mesmo nome.

• Ordem de Resolução de Métodos (MRO - Method Resolution Order):


Python usa um algoritmo chamado C3 Linearization para determinar a ordem em que
as classes base são pesquisadas. Você pode ver o MRO com [Link]().

• Aumento da Complexidade Geral: Pode tornar hierarquias difíceis de entender


e manter.
288 Capítulo 14. Herança e Polimorfismo

Recomendações
• Prefira Herança Simples: É mais clara e suficiente na maioria dos casos.

• Considere Composição: Muitas vezes, uma classe conter instâncias de outras


classes (relação “tem um(a)”) é mais flexível e robusto.

• Use Herança Múltipla com Extrema Cautela: Especialmente para iniciantes.


Se usar, entenda o MRO.

Para nosso livro “descomplicado”, focaremos na herança simples.

Concluindo o Capítulo sobre Herança e Polimorfismo


Neste capítulo, exploramos dois dos pilares mais transformadores da Programação
Orientada a Objetos: a Herança e o Polimorfismo.
Vimos como a herança nos permite criar hierarquias de classes, reutilizar código
e organizar classes baseadas na relação “é um(a)”. Aprendemos a sintaxe para definir
subclasses, a importância de super().__init__(), como sobrescrever métodos e adicionar
novos membros às subclasses. Também mencionamos a herança múltipla.
Introduzimos o polimorfismo, a capacidade de objetos de diferentes classes responde-
rem à mesma chamada de método de maneiras específicas. Vimos como isso funciona com
herança e sobrescrita, e também o conceito de “Duck Typing” em Python, que foca nos
comportamentos que um objeto suporta.
Juntos, herança e polimorfismo nos permitem construir sistemas de software mais
reutilizáveis, extensíveis, flexíveis e organizados. Dominar esses conceitos é crucial para a
POO.
No próximo capítulo, que é opcional (Capítulo 15: Tópicos Avançados em POO),
poderemos explorar algumas ideias mais avançadas. Caso contrário, nossa jornada nos
levará para a Parte V, sobre Manipulação de Arquivos e Tratamento de Erros.
Capítulo 15

Tópicos Avançados em POO

Nos capítulos anteriores desta parte sobre Programação Orientada a Objetos (POO),
construímos uma base sólida. Entendemos o que são classes e objetos, como definir atributos
para armazenar o estado e métodos para implementar comportamentos. Exploramos o
crucial método construtor __init__ e o papel central do parâmetro self. Também
mergulhamos nos poderosos conceitos de herança, que nos permite criar hierarquias de
classes e reutilizar código, e de polimorfismo, que confere flexibilidade ao modo como
objetos de diferentes tipos respondem a mesmas mensagens.
Com esses fundamentos, você já é capaz de criar uma vasta gama de aplicações
orientadas a objetos em Python. No entanto, a POO em Python oferece ainda mais
recursos e técnicas que podem levar seus designs de classe a um novo nível de sofisticação,
controle e elegância.

15.1 Introdução aos Tópicos Avançados em POO: Refi-


nando Suas Classes
Este capítulo é dedicado a explorar alguns desses tópicos mais avançados. Eles são
considerados “opcionais” no sentido de que você pode construir muitos programas úteis
sem eles, mas conhecê-los e saber quando aplicá-los pode resultar em código mais limpo,
mais seguro, mais flexível e mais alinhado com as melhores práticas de design de software.
Vamos abordar conceitos como:

• Propriedades (@property): Uma maneira elegante de gerenciar o acesso a atributos,


permitindo que você adicione lógica (como validação ou cálculo) quando um atributo
é lido ou definido, mas mantendo uma sintaxe de acesso simples para o usuário da
classe.

• Métodos de Classe (@classmethod): Métodos que são ligados à classe em si, e


não a uma instância particular. São úteis, por exemplo, para criar “métodos de
fábrica” alternativos para construir objetos.

• Métodos Estáticos (@staticmethod): Funções que residem dentro de uma classe


(por organização lógica), mas que não dependem nem do estado da instância (self)
nem da classe (cls).

• Classes Abstratas e Métodos Abstratos (usando o módulo abc): Uma forma


de definir “contratos” ou “interfaces” que as subclasses devem seguir, garantindo que

289
290 Capítulo 15. Tópicos Avançados em POO

certas funcionalidades sejam implementadas.


Cada um desses tópicos adiciona uma nova ferramenta ao seu arsenal de POO, permitindo
que você modele soluções de forma mais precisa e robusta. Embora possam parecer um
pouco mais complexos à primeira vista, entender seu propósito e aplicação pode fazer
uma grande diferença na qualidade e na manutenibilidade dos seus projetos Python mais
ambiciosos.
Vamos começar explorando as propriedades, uma forma Pythônica de controlar o acesso
a atributos.

15.2 Propriedades (@property): Controlando o Acesso


a Atributos
Quando definimos atributos, como [Link], geralmente os acessamos diretamente
([Link]). No entanto, às vezes queremos mais controle sobre o que acontece quando
um atributo é lido ou definido (ex: validação, cálculo dinâmico).
Uma abordagem tradicional seria usar métodos “getter” e “setter” (ex: get_preco(),
set_preco(valor)). Embora funcional, isso altera a forma de acesso ao atributo, tornando-
a menos direta. Python oferece uma maneira mais elegante e “Pythônica” através de
propriedades, usando o decorador @property.

O Decorador @property para Criar Getters


O decorador @property é colocado acima de um método que terá o mesmo nome do
atributo “público” que você deseja criar. Este método se torna o getter: é chamado
automaticamente quando você tenta ler o atributo.
1 class P ro du toC om Pr ope rt y :
2 def __init__ ( self , preco_inicial ) :
3 self . _preco_privado = preco_inicial # Atributo " interno "
4
5 @property # Transforma o m é todo ’ preco ’ em um getter
6 def preco ( self ) :
7 " " " Getter para a propriedade ’ preco ’. " " "
8 print ( " DEBUG : Getter de ’ preco ’ foi chamado . " )
9 return self . _preco_privado
10
11 produto_a = Pr od ut oCo mP ro per ty (50.75)
12 valor_preco = produto_a . preco # Chama o m é todo ’ preco ’ ( getter )
13 print ( f " O pre ç o do produto A é : R$ { valor_preco :.2 f } " )
14
15 # produto_a . preco = 60.00 # GERARIA AttributeError ( sem setter )

Código 15.1: Usando @property para um getter


Dentro do getter, você geralmente retorna o valor de um atributo “interno” (convenção:
_nome).

Definindo um Setter com @nome_da_propriedade.setter


Para permitir que o atributo seja modificado (e controlar como), defina um método set-
ter. Ele deve ter o mesmo nome da propriedade e ser decorado com @nome_da_propriedade.setter.
O setter recebe self e o novo_valor.
15.2. Propriedades (@property): Controlando o Acesso a Atributos 291

1 class P r o d u t o C o m P r o p e r t y E S e t t e r :
2 def __init__ ( self , nome_produto , preco_inicial ) :
3 self . nome = nome_produto
4 # Ao atribuir a self . preco aqui , o M É TODO SETTER j á ser á
chamado !
5 self . preco = preco_inicial # Chama o @preco . setter
6
7 @property
8 def preco ( self ) : # O getter
9 # print (" DEBUG : Getter de ’ preco ’ chamado .")
10 return self . _preco_interno # Atributo de armazenamento real
11
12 @preco . setter # Define o setter para a propriedade ’ preco ’
13 def preco ( self , novo_valor_preco ) :
14 print ( f " DEBUG : Setter de ’ preco ’ chamado com valor
{ novo_valor_preco } para ’{ self . nome } ’. " )
15 if isinstance ( novo_valor_preco , ( int , float ) ) and
novo_valor_preco >= 0:
16 self . _preco_interno = novo_valor_preco
17 else :
18 print ( f " Aviso : Pre ç o inv á lido ({ novo_valor_preco }) . O
pre ç o de ’{ self . nome } ’ n ã o foi alterado . " )
19
20 item1 = P r o d u t o C o m P r o p e r t y E S e t t e r ( " Caderno " , 15.00)
21 print ( f " Pre ç o inicial do { item1 . nome }: R$ { item1 . preco :.2 f } " )
22 item1 . preco = 18.50
23 print ( f " Novo pre ç o do { item1 . nome }: R$ { item1 . preco :.2 f } " )
24 item1 . preco = -5 # Tentativa inv á lida
25 print ( f " Pre ç o do { item1 . nome } ap ó s tentativa inv á lida : R$
{ item1 . preco :.2 f } " )

Código 15.2: Classe Produto com getter e setter para preco


Dentro do setter e getter, use um nome de atributo diferente para o armazenamento real
(ex: _preco_interno) para evitar recursão infinita.

Definindo um Deleter com @nome_da_propriedade.deleter (Menos


Comum)
Opcionalmente, um método deleter, decorado com @nome_da_propriedade.deleter,
é chamado quando você usa del objeto.nome_da_propriedade.
1 # ( Continuando a classe P r o d u t o C o m P r o p e r t y E S e t t e r )
2 # @preco . deleter
3 # def preco ( self ) :
4 # print ( f " DEBUG : Deleter de ’ preco ’ chamado para
’{ self . nome } ’.")
5 # if hasattr ( self , ’ _preco_interno ’) :
6 # del self . _preco_interno
7 # print (" Atributo de pre ç o interno foi deletado .")
8 # else :
9 # print (" Atributo de pre ç o interno j á n ã o existia .")
10
11 # # Supondo que item1 existe :
12 # # del item1 . preco # Chamaria o m é todo deleter

Código 15.3: Exemplo conceitual de um deleter para propriedade (não executado)


292 Capítulo 15. Tópicos Avançados em POO

Atributos Somente Leitura (Read-Only)


Se você definir um getter (@property) mas não um setter, o atributo se torna efetiva-
mente “somente leitura”. Tentar atribuir um valor a ele causará um AttributeError.
1 class Circulo :
2 def __init__ ( self , raio_circulo ) :
3 self . _raio = raio_circulo
4
5 @property # Define ’ area ’ como uma propriedade somente leitura
6 def area ( self ) :
7 " " " Calcula e retorna a á rea do c í rculo . " " "
8 return 3.14159 * ( self . _raio ** 2)
9
10 c = Circulo (10)
11 print ( f " A á rea do c í rculo é : { c . area :.2 f } " )
12
13 # c . area = 50 # GERARIA AttributeError : can ’t set attribute ’ area ’

Código 15.4: Propriedade somente leitura para área de um círculo

Vantagens de Usar Propriedades


• Interface Limpa: O acesso ao atributo continua simples ([Link]).

• Encapsulamento da Lógica: Validações e cálculos ficam nos métodos getter/setter.

• Flexibilidade para Evolução: Você pode começar com um atributo público e


depois adicionar controle com propriedades sem quebrar o código que usa a classe.

As propriedades são uma ferramenta elegante em Python para gerenciar o acesso a atributos,
combinando simplicidade com controle.

15.3 Métodos de Classe (@classmethod)


Já estamos familiarizados com os métodos de instância, que operam sobre um objeto
específico (self). Python também permite definir métodos de classe, que estão ligados
à classe em si, e não a uma instância particular.

O que são Métodos de Classe?


• São definidos dentro de uma classe.

• Marcados com o decorador @classmethod.

• O primeiro parâmetro, por convenção, é chamado cls e recebe uma referência à


própria classe.
15.3. Métodos de Classe (@classmethod) 293

Sintaxe
1 class MinhaClasse :
2 a tr ibu to _d e_c la ss e = " Compartilhado "
3
4 @classmethod
5 def m e u _ m e t o d o _ d e _ c l a s s e ( cls , a rg u m en t o_ a di c i on a l ) :
6 print ( f " Chamado na classe : { cls . __name__ } " )
7 print ( f " Atributo de classe : { cls . at ri but o_ de _cl as se } " )
8 # return cls (...) # Pode criar inst â ncias da classe ’ cls ’

Código 15.5: Sintaxe de um método de classe

Casos de Uso Comuns


1. Métodos de Fábrica (Factory Methods) – Construtores Alternativos:
Fornecem outras maneiras de criar instâncias da classe. Usar cls(...) dentro
do método garante que, se chamado em uma subclasse, criará uma instância da
subclasse.
1 class Data :
2 def __init__ ( self , dia_data , mes_data , ano_data ) :
3 self . dia = dia_data
4 self . mes = mes_data
5 self . ano = ano_data
6
7 def formatar_data ( self ) :
8 return
f " { self . dia :02 d }/{ self . mes :02 d }/{ self . ano :04 d } "
9
10 @classmethod
11 def d e_ str in g_ ddm ma aa a ( cls , string_data ) : # ’ cls ’ é a
classe ( Data ou subclasse )
12 " " " Cria um objeto Data a partir de uma string
’ dd / mm / aaaa ’. " " "
13 try :
14 dia_str , mes_str , ano_str =
string_data . split ( ’/ ’)
15 # Em um caso real , adicionar í amos mais
valida ç õ es aqui
16 return cls ( int ( dia_str ) , int ( mes_str ) ,
int ( ano_str ) )
17 except ValueError :
18 print ( " Formato de data inv á lido . Use
’ dd / mm / aaaa ’. " )
19 return None
20

21 data1 = Data (23 , 5 , 2025)


22 print ( f " Data 1 ( via __init__ ) : { data1 . formatar_data () } " )
23
24 data2 = Data . d e_ st rin g_ dd mma aa a ( " 15/10/2024 " )
25 if data2 :
26 print ( f " Data 2 ( via m é todo de classe ) :
{ data2 . formatar_data () } " )

Código 15.6: Método de classe como fábrica para objetos Data


294 Capítulo 15. Tópicos Avançados em POO

2. Acessar ou Modificar Atributos de Classe:


Como o método recebe a classe (cls), ele pode interagir com atributos da classe.
1 class C o n f i g u r a c a o A p l i c a t i v o :
2 m o d o _ d e _ e x i b i c a o _ p a d r a o = " Claro "
3 c on t ad o r _a l te r a co e s = 0
4

5 @classmethod
6 def obte r_modo_p adrao ( cls ) :
7 return cls . m o d o _ d e _ e x i b i c a o _ p a d r a o
8
9 @classmethod
10 def de f in i r _m o do _ p ad r ao ( cls , novo_modo ) :
11 if novo_modo in [ " Claro " , " Escuro " ]:
12 cls . m o d o _ d e _ e x i b i c a o _ p a d r a o = novo_modo
13 cls . co n ta d or _ a lt e ra c o es += 1
14 print ( f " Modo padr ã o da classe alterado para :
’{ cls . m o d o _ d e _ e x i b i c a o _ p a d r a o } ’ " )
15 else :
16 print ( f " Erro : Modo ’{ novo_modo } ’ inv á lido . " )
17
18 print ( f " Modo inicial :
{ C o n f i g u r a c a o A p l i c a t i v o . obter_mo do_padra o () } " )
19 C o n f i g u r a c a o A p l i c a t i v o . d e f in i r_ m o do _ pa d ra o ( " Escuro " )
20 print ( f " N ú mero de altera ç õ es de modo :
{ C o n f i g u r a c a o A p l i c a t i v o . c o nt a do r _ al t er a co e s } " )

Código 15.7: Método de classe para gerenciar atributo de classe

Métodos de classe são úteis para operações que dizem respeito à classe em si, como
construtores alternativos ou o gerenciamento de atributos de classe.

15.4 Métodos Estáticos (@staticmethod): Funções Utili-


tárias Dentro de Classes
Além dos métodos de instância (com self) e dos métodos de classe (com cls), Python
oferece um terceiro tipo: o método estático. Ele é, essencialmente, uma função comum
que reside dentro do namespace de uma classe por questões de organização ou por ter uma
relação lógica com ela, mas não recebe uma referência implícita nem à instância
(self) nem à classe (cls) como seu primeiro argumento.

O que são Métodos Estáticos?


• Definidos dentro de uma classe.

• Marcados com o decorador @staticmethod.

• Não recebem self ou cls automaticamente. Se precisam de parâmetros, são definidos


explicitamente.

• Chamados principalmente através da classe (NomeDaClasse.metodo_estatico()),


mas também podem ser chamados via instância.
15.4. Métodos Estáticos (@staticmethod): Funções Utilitárias Dentro de Classes 295

Sintaxe
1 class M i n h a C l a s s e C o m E s t a t i c o :
2 @staticmethod
3 def u m a _ f u n c a o _ u t i l i t a r i a ( parametro1 , parametro2 ) :
4 """
5 Este é um m é todo est á tico . N ã o tem ’ self ’ ou ’ cls ’.
6 Opera apenas com os argumentos que recebe .
7 """
8 # print (" M é todo est á tico chamado !")
9 return parametro1 + parametro2

Código 15.8: Sintaxe de um método estático

Por que Usar Métodos Estáticos? (Casos de Uso)


1. Agrupamento Lógico de Funções Utilitárias: Se uma função está logicamente
relacionada a uma classe, mas não precisa acessar dados da instância ou da classe.

2. Evitar self ou cls Desnecessários: Se um método dentro de uma classe não


usa self nem cls, ele é um bom candidato a ser estático.

Exemplo Prático: Classe ValidadoresDeDados


1 class V al id ado re sD eDa do s :
2
3 @staticmethod
4 def email_e_valido ( email_str ) :
5 " " " Verifica de forma simples se um email parece v á lido . " " "
6 if isinstance ( email_str , str ) and " @ " in email_str and " . "
in email_str . split ( ’@ ’) [ -1]:
7 return True
8 return False
9

10 @staticmethod
11 def c p f _ t e m _ f o r m a t o _ v a l i d o ( cpf_str ) :
12 " " " Verifica se o CPF ( string ) tem 11 d í gitos num é ricos . " " "
13 cpf_limpo = cpf_str . replace ( " . " , " " ) . replace ( " -" , " " )
14 return len ( cpf_limpo ) == 11 and cpf_limpo . isdigit ()
15

16 @staticmethod
17 def contar_vogais ( texto ) :
18 " " " Conta o n ú mero de vogais em um texto . " " "
19 vog ai s_ enc on tr ad as = 0
20 for letra in texto . lower () :
21 if letra in " aeiou á é í ó ú à ã õ â ê ô " :
22 v og ai s_e nc on tra da s += 1
23 return v oga is _e nc ont ra da s
24
25 # Usando os m é todos est á ticos ( chamados atrav é s da classe )
26 email1 = " contato@exemplo . com "
27 print ( f " Email ’{ email1 } ’ v á lido ?
{ Va li da dor es De Dad os . email_e_valido ( email1 ) } " )
28
29 frase = " Testando as vogais ! "
296 Capítulo 15. Tópicos Avançados em POO

30 print ( f " Vogais em ’{ frase } ’:


{ Va li da dor es De Dad os . contar_vogais ( frase ) } " )

Código 15.9: Classe com métodos estáticos para validação

Métodos Estáticos vs. Métodos de Classe vs. Métodos de Instância


• Instância: Recebe self, opera na instância.

• Classe: Recebe cls, opera na classe ou é fábrica. Decorador @classmethod.

• Estático: Não recebe self nem cls. Função utilitária no namespace da classe.
Decorador @staticmethod.

Acessando Atributos de Classe de um Método Estático


Se um método estático precisar acessar um atributo de classe, ele deve fazê-lo explici-
tamente através do nome da classe: NomeDaClasse.atributo_de_classe.
1 class Config :
2 VERSAO_APP = " 1.2.0 " # Atributo de classe
3
4 @staticmethod
5 def o b t e r _ i n f o r m a c o e s _ v e r s a o () :
6 return f " Vers ã o do Aplicativo : { Config . VERSAO_APP } "
7
8 print ( Config . o b t e r _ i n f o r m a c o e s _ v e r s a o () )

Código 15.10: Método estático acessando atributo de classe

Os métodos estáticos são úteis para agrupar funcionalidades que têm uma conexão lógica
com uma classe, mas não dependem do estado de instâncias ou da classe em si.

15.5 Classes Abstratas e Métodos Abstratos (Módulo


abc): Definindo Contratos
Ao projetar hierarquias de classes, muitas vezes queremos definir um “molde” ou
um “contrato” que as subclasses devem seguir, garantindo que implementem métodos
específicos. Para isso, Python oferece as Classes Base Abstratas (Abstract Base
Classes - ABCs) e os métodos abstratos.

O que são Classes e Métodos Abstratos?


• Uma classe abstrata não pode ser instanciada diretamente e serve como modelo
para subclasses.

• Um método abstrato é declarado na classe abstrata (sem implementação completa


ou com uma que deve ser sobrescrita) e deve ser implementado por subclasses
concretas.

Seu propósito é garantir uma interface comum e um design robusto.


15.5. Classes Abstratas e Métodos Abstratos (Módulo abc): Definindo Contratos 297

Como Criar: O Módulo abc


Python usa o módulo abc:

• ABC: Classe auxiliar da qual sua classe abstrata deve herdar.

• @abstractmethod: Decorador para marcar um método como abstrato.

Exemplo Prático: Hierarquia de FormaGeometricaPlana


1 # Primeiro , importamos ABC e abstractmethod do m ó dulo abc
2 from abc import ABC , abstractmethod
3
4 class F o r m a G e o m e t ri c a P l a n a ( ABC ) : # Herda de ABC
5 def __init__ ( self , nome_da_forma ) :
6 self . nome = nome_da_forma
7
8 @abstractmethod # Marca como m é todo abstrato
9 def calcular_area ( self ) :
10 " " " Subclasses concretas DEVEM implementar este m é todo . " " "
11 pass
12
13 @abstractmethod
14 def ca lc ul ar_ pe ri met ro ( self ) :
15 " " " Subclasses concretas DEVEM implementar este m é todo . " " "
16 pass
17

18 def quem_sou_eu ( self ) : # M é todo concreto , herdado


19 return f " Eu sou uma forma geom é trica plana chamada
’{ self . nome } ’. "
20
21 # Tentativa de instanciar F o r m a Ge o m e t r i c a P l a n a GERARIA ERRO :
22 # forma_generica = F o r m a G e o m e t r i c a Pl a n a (" Forma Qualquer ")
23 # TypeError : Can ’t instantiate abstract class ... with abstract
methods ...
24
25 class RetanguloPlano ( F o r m a G e o m e t r i c a P l an a ) :
26 def __init__ ( self , nome_ret , base_ret , altura_ret ) :
27 super () . __init__ ( nome_ret )
28 self . base = base_ret
29 self . altura = altura_ret
30
31 def calcular_area ( self ) : # Implementa ç ã o OBRIGAT Ó RIA
32 return self . base * self . altura
33
34 def ca lc ul ar_ pe ri met ro ( self ) : # Implementa ç ã o OBRIGAT Ó RIA
35 return 2 * ( self . base + self . altura )
36
37 class CirculoPlano ( F o r m a G e o m e t r i c a P l a n a ) :
38 PI_VALOR = 3.14159265
39
40 def __init__ ( self , nome_circ , raio_circ ) :
41 super () . __init__ ( nome_circ )
42 self . raio = raio_circ
43
44 def calcular_area ( self ) : # Implementa ç ã o OBRIGAT Ó RIA
45 return CirculoPlano . PI_VALOR * ( self . raio ** 2)
46
47 def ca lc ul ar_ pe ri met ro ( self ) : # Implementa ç ã o OBRIGAT Ó RIA
298 Capítulo 15. Tópicos Avançados em POO

48 return 2 * CirculoPlano . PI_VALOR * self . raio


49
50 m eu_retan gulo_obj = RetanguloPlano ( " Ret â ngulo Legal " , 10 , 7)
51 meu_circulo_obj = CirculoPlano ( " C í rculo Azul " , 5)
52
53 print ( f " \ n { meu_r etangulo _obj . quem_sou_eu () } " )
54 print ( f " Á rea : { meu_ret angulo_o bj . calcular_area () } , Per í metro :
{ meu_ret angulo_ob j . c alc ul ar _pe ri me tro () } " )
55
56 print ( f " \ n { meu_circulo_obj . quem_sou_eu () } " )
57 print ( f " Á rea : { meu_circulo_obj . calcular_area () :.2 f } , Per í metro :
{ meu_circulo_obj . c al cul ar _p eri me tr o () :.2 f } " )
58
59 # Polimorfismo com a interface garantida
60 lista_de_formas = [ meu_retangulo_obj , meu_circulo_obj ]
61 print ( " \ n Á reas de diversas formas : " )
62 for forma_obj in lista_de_formas :
63 print ( f " A forma ’{ forma_obj . nome } ’ tem á rea :
{ forma_obj . calcular_area () :.3 f } " )

Código 15.11: Exemplo de Classe Abstrata FormaGeometricaPlana e subclasses


Se uma subclasse não implementar todos os métodos abstratos, ela também se torna
abstrata.

Concluindo o Capítulo sobre Tópicos Avançados em POO


Neste capítulo opcional, demos um passo além dos fundamentos da Programação
Orientada a Objetos em Python, explorando conceitos como propriedades (@property)
para acesso elegante a atributos, métodos de classe (@classmethod) ligados à classe,
métodos estáticos (@staticmethod) como funções utilitárias dentro de classes, e fi-
nalmente, as classes base abstratas (ABCs) com métodos abstratos para definir
contratos e interfaces.
Esses tópicos adicionam camadas de sofisticação e robustez ao design POO. Embora
não sejam sempre necessários para programas mais simples, são ferramentas valiosas para
código mais complexo, reutilizável e manutenível.
Com este capítulo, concluímos nossa exploração principal da Programação Orientada a
Objetos (Parte IV) em Python dentro do escopo deste livro “descomplicado”. Você agora
tem uma base sólida para criar seus próprios tipos de dados, organizar seu código em
torno de objetos e começar a pensar em termos de abstração, encapsulamento, herança e
polimorfismo.
Na Parte V do nosso livro, mudaremos um pouco o foco para aprender como nossos
programas Python podem interagir com o mundo exterior, especificamente através da
Manipulação de Arquivos, e como podemos lidar com situações inesperadas de forma
elegante através do Tratamento de Exceções.
Parte V

Manipulação de Arquivos e Tratamento


de Erros

299
Capítulo 16

Leitura e Escrita de Arquivos

Até agora, os dados com os quais nossos programas Python trabalharam existiram
principalmente na memória do computador enquanto o programa estava em execução.
Variáveis guardavam informações, estruturas de dados como listas e dicionários as organi-
zavam, mas, uma vez que o programa terminava, esses dados eram perdidos (a menos que
os tivéssemos impresso no console e copiado manualmente).
Para que os programas sejam verdadeiramente úteis em muitas situações do mundo
real, eles precisam da capacidade de:
• Ler dados de fontes externas: Como arquivos de configuração, planilhas de
dados, documentos de texto, etc.

• Salvar dados de forma persistente: Para que possam ser recuperados e utilizados
em execuções futuras do programa ou por outros programas.
É aqui que entra a manipulação de arquivos. Python oferece funcionalidades robustas e
relativamente simples para ler e escrever em arquivos armazenados no sistema de arquivos
do seu computador.

16.1 Introdução à Manipulação de Arquivos: Persis-


tindo e Acessando Dados
Por que Interagir com Arquivos?
A capacidade de um programa ler e escrever arquivos é fundamental para uma vasta
gama de aplicações:
• Persistência de Dados: Salvar o estado de um programa, resultados, preferências
do usuário.

• Entrada de Dados em Larga Escala: Ler grandes volumes de dados (sensores,


logs).

• Saída de Dados Estruturados: Gerar relatórios, exportar dados para outros


softwares.

• Configuração de Programas: Ler parâmetros de configuração de arquivos.

• Comunicação entre Programas.

300
16.2. Abrindo e Fechando Arquivos: A Função open() e a Instrução with 301

O que é um Arquivo do Ponto de Vista do Programa?


Para o seu programa Python, um arquivo no disco é visto como uma sequência de
bytes. Como esses bytes são interpretados depende do tipo de arquivo:

1. Arquivos de Texto: Contêm caracteres legíveis por humanos. São armazenados


usando uma codificação de caracteres (ex: UTF-8, ASCII). Exemplos: .txt,
.csv, .json, .py. Nosso foco principal será em arquivos de texto.

2. Arquivos Binários: Contêm dados que não são diretamente texto legível, mas sim
uma sequência de bytes com significado específico para o programa que os interpreta.
Exemplos: imagens (.jpg, .png), áudio (.mp3), vídeo (.mp4), executáveis. Daremos
uma breve introdução.

Caminho do Arquivo (Path)


Para que seu programa possa encontrar um arquivo, ele precisa de sua localização,
especificada por um caminho de arquivo (path).

• Caminho Absoluto: Localização completa a partir do diretório raiz.

– Exemplo Windows: C:\Usuários\Alan\Documentos\meu_arquivo.txt


– Exemplo macOS/Linux: /home/alan/documentos/meu_arquivo.txt

• Caminho Relativo: Localização em relação ao diretório de trabalho atual do


script.

– Se na mesma pasta: meu_arquivo.txt.


– Se em subpasta dados/: dados/meu_arquivo.txt.

Ao longo deste capítulo, aprenderemos as funções e métodos essenciais para abrir, ler,
escrever e fechar arquivos. Começaremos comamaneira mais segura e recomendada de
trabalhar com arquivos: a instrução with.

16.2 Abrindo e Fechando Arquivos: A Função open() e


a Instrução with
O primeiro passo para ler ou escrever dados em um arquivo é abri-lo. Abrir um
arquivo estabelece uma conexão entre o seu programa Python e o arquivo físico no sistema
de armazenamento. Da mesma forma, após terminar de usar o arquivo, é crucial fechá-lo.

A Função Embutida open()


A principal ferramenta em Python para abrir arquivos é a função embutida open().
Sua sintaxe básica mais comum é:

objeto_arquivo = open(caminho_do_arquivo, modo, encoding="utf-8")

• caminho_do_arquivo (String): O nome e, opcionalmente, o caminho para o ar-


quivo.
302 Capítulo 16. Leitura e Escrita de Arquivos

• modo (String): Especifica a intenção de uso. Os principais modos são:

– ’r’: Leitura (padrão). Erro se o arquivo não existir (FileNotFoundError).


– ’w’: Escrita. Cria se não existir. ATENÇÃO: Sobrescreve completamente
o arquivo se ele já existir!
– ’a’: Anexar (append). Cria se não existir. Adiciona dados ao final do arquivo.
– ’x’: Criação Exclusiva. Falha se o arquivo já existir (FileExistsError).
– Modos com +: ’r+’ (leitura/escrita, deve existir), ’w+’ (escrita/leitura, cria/-
sobrescreve), ’a+’ (anexação/leitura, cria se não existir).
– Adicionar b para modos binários (ex: ’rb’, ’wb’).

• encoding (String, Parâmetro Nomeado): Crucial para arquivos de texto. Define


como caracteres são convertidos para/de bytes. Sempre use encoding="utf-8"
para arquivos de texto em português, a menos que saiba que a codificação é outra.
Se omitido, usa o padrão do sistema, o que pode causar problemas.

A função open() retorna um objeto arquivo (file handle).

A Necessidade de Fechar Arquivos: objeto_arquivo.close()


Após o uso, é essencial fechar um arquivo com objeto_arquivo.close(). Isso garante
que:

1. Dados em buffer sejam escritos no disco (evitando perda de dados).

2. Recursos do sistema operacional sejam liberados.

Esquecer de fechar pode levar a arquivos incompletos ou corrompidos.

A Instrução with open(...) as ...: A Forma Pythônica e Segura


Esta é a maneira moderna e preferida de trabalhar com arquivos, pois usa um
gerenciador de contexto que garante o fechamento automático do arquivo, mesmo se
ocorrerem erros.
Sintaxe:
1 with open ( " c am inh o_ do _ar qu iv o . txt " , " modo " , encoding = " utf -8 " ) as
variavel_arquivo :
2 # Bloco de c ó digo onde ’ variavel_arquivo ’ est á dispon í vel
3 # e o arquivo est á GARANTIDAMENTE aberto .
4 # Realize suas opera ç õ es de leitura / escrita aqui .
5 # ...
6 # FIM DO BLOCO ’ with ’: Neste ponto , o Python automaticamente
7 # garante que o arquivo seja FECHADO .

Código 16.1: Sintaxe da instrução with open()

Vantagens: Fechamento automático e garantido, código mais limpo e robusto.


16.2. Abrindo e Fechando Arquivos: A Função open() e a Instrução with 303

Exemplos Práticos com with open()


• Exemplo 1: Escrevendo em um arquivo novo (modo ’w’)
1 n o m e _ d o _ a r q u i v o _ d i a r i o = " meu_diario . txt "
2 with open ( nome_do_arquivo_diario , " w " , encoding = " utf -8 " )
as diario_pessoal :
3 diario_pessoal . write ( " Querido di á rio ,\ n " )
4 diario_pessoal . write ( " Hoje aprendi sobre a instru ç ã o
’ with ’.\ n " )
5 diario_pessoal . write ( f " Python é incr í vel em {2025}!\ n " )
6 print ( f " Arquivo ’{ n o m e _ d o _ a r q u i v o _ d i a r i o } ’ foi escrito e
fechado . " )

Código 16.2: Escrevendo em um arquivo com with open()

• Exemplo 2: Lendo de um arquivo (modo ’r’) – supondo que ele existe


1 # Supondo que o arquivo " meu_diario . txt " foi criado pelo
exemplo anterior
2 n o m e _ d o _ a r q u i v o _ p a r a _ l e r = " meu_diario . txt "
3 print ( f " \ nLendo o arquivo ’{ n o m e _ d o _ a r q u i v o _ p a r a _ l e r } ’: " )
4

5 # Se o arquivo n ã o existir , open () no modo ’r ’ causaria


FileN otFoundE rror .
6 # Veremos como tratar isso no Cap í tulo 17. Por agora ,
assumimos que existe .
7 with open ( nome_do_arquivo_para_ler , " r " , encoding = " utf -8 " )
as a r q u iv o _ p a r a _ l e i t u r a :
8 print ( f " Objeto arquivo aberto :
{ arquivo_para_leitura }")
9 print ( " ( Conte ú do ser á lido na pr ó xima se ç ã o ...) " )
10
11 print ( f " Arquivo ’{ n o m e _ d o _ a r q u i v o _ p a r a _ l e r } ’ foi lido ( ou
tentado ) e fechado . " )

Código 16.3: Abrindo arquivo para leitura com with open()

• Exemplo 3: Anexando conteúdo a um arquivo existente (modo ’a’)


1 # Usando o arquivo " meu_diario . txt " criado / modificado
anteriormente
2 n o m e _ d o _ a r q u i v o _ p a r a _ a n e x a r = " meu_diario . txt "
3 with open ( nome_do_arquivo_para_anexar , " a " ,
encoding = " utf -8 " ) as di ar io _pa ra _a nex ar :
4 di ari o_ pa ra_ an ex ar . write ( " \n - - - Adi ç ã o ao di á rio
- - -\ n " )
5 di ari o_ pa ra_ an ex ar . write ( " Aprendi sobre o modo ’a ’
para anexar !\ n " )
6 print ( f " \ nConte ú do foi anexado ao arquivo
’{ n o m e _ d o _ a r q u i v o _ p a r a _ a n e x a r } ’. " )

Código 16.4: Anexando a um arquivo com with open()

Após executar os exemplos de escrita e anexação, abra o arquivo meu_diario.txt no seu


editor de texto para ver o resultado!
Dominar a abertura e o fechamento correto de arquivos (preferencialmente com with)
é o primeiro passo essencial para qualquer operação de entrada ou saída de dados.
304 Capítulo 16. Leitura e Escrita de Arquivos

16.3 Lendo Arquivos de Texto


Uma vez que sabemos como abrir um arquivo usando with open(...) no modo
apropriado (geralmente ’r’ para leitura, com encoding="utf-8"), a próxima etapa é
aprender a ler o conteúdo que está dentro dele.

Principais Métodos para Ler Arquivos de Texto


Vamos supor que temos um arquivo chamado [Link] com algumas linhas de texto
para nossos exemplos.

1. [Link](tamanho_opcional): Lendo o Conteúdo Inteiro (ou Parcial)


Se chamado sem argumentos, lê o arquivo inteiro e retorna seu conteúdo como
uma única string. Com um argumento tamanho, lê essa quantidade de caracteres.
Cuidado: usar [Link]() em arquivos muito grandes pode consumir muita
memória.
1 # Supondo que ’ poema . txt ’ existe e tem conte ú do
2 try :
3 with open ( " poema . txt " , " r " , encoding = " utf -8 " ) as
f_poema :
4 conteud o_comple to = f_poema . read ()
5 print ( " --- Conte ú do completo com read () ---" )
6 print ( c onteudo_ completo )
7

8 with open ( " poema . txt " , " r " , encoding = " utf -8 " ) as
f_poema_parcial :
9 primeiros_chars = f_poema_parcial . read (20) # L ê os
primeiros 20 caracteres
10 print ( f " \n - - - Primeiros 20 caracteres :
’{ primeiros_chars } ’ ---" )
11 except F ileNotFo undError :
12 print ( " Erro : O arquivo ’ poema . txt ’ n ã o foi encontrado
para os exemplos de leitura . " )

Código 16.5: Usando [Link]()

(Nota para Alan: O try-except está aqui para o exemplo não quebrar se o arquivo
[Link] não for criado pelo leitor. Se preferir, podemos instruir a criação do
arquivo e remover o try-except.)
2. [Link](): Lendo uma Linha por Vez
Lê uma única linha do arquivo, da posição atual até encontrar \n (ou o fim do
arquivo). A string retornada inclui o \n (se presente). Retorna uma string vazia ()
no final do arquivo.
1 # Supondo que ’ poema . txt ’ existe
2 try :
3 with open ( " poema . txt " , " r " , encoding = " utf -8 " ) as
f_poema_linhas :
4 print ( " \n - - - Lendo com readline () ---" )
5 linha1 = f_poema_linhas . readline ()
6 # repr () mostra caracteres especiais como \ n
7 print ( f " Linha 1 ( repr ) : { repr ( linha1 ) } " )
8 if linha1 : # Para remover o \ n do final para
impress ã o
16.3. Lendo Arquivos de Texto 305

9 print ( f " Linha 1 ( strip ) : ’{ linha1 . strip () } ’ " )


10 except F ileNotFo undError :
11 print ( " Erro : O arquivo ’ poema . txt ’ n ã o foi
encontrado . " )

Código 16.6: Usando [Link]()

3. [Link](): Lendo Todas as Linhas para uma Lista


Lê todas as linhas restantes e as retorna como uma lista de strings. Cada string
na lista inclui o \n (se presente). Cuidado com arquivos muito grandes.
1 # Supondo que ’ poema . txt ’ existe
2 try :
3 with open ( " poema . txt " , " r " , encoding = " utf -8 " ) as
f_poema_todas :
4 print ( " \n - - - Lendo com readlines () ---" )
5 lista_das_linhas = f_poema_todas . readlines ()
6 print ( f " Resultado de readlines () é do tipo :
{ type ( lista_das_linhas ) } " )
7 for i , linha_item in enumerate ( lista_das_linhas ) :
8 print ( f " Item { i } da lista ( strip ) :
’{ linha_item . strip () } ’ " )
9 except FileNotFo undError :
10 print ( " Erro : O arquivo ’ poema . txt ’ n ã o foi
encontrado . " )

Código 16.7: Usando [Link]()

Iterando Diretamente sobre o Objeto Arquivo (Forma Mais Pythô-


nica)
A maneira mais idiomática (Pythônica) e eficiente em termos de memória para ler um
arquivo de texto linha por linha é usar o próprio objeto arquivo diretamente em um laço
for.
1 # Supondo que ’ poema . txt ’ existe
2 try :
3 with open ( " poema . txt " , " r " , encoding = " utf -8 " ) as f_poema_iter :
4 print ( " \n - - - Lendo linha por linha com itera ç ã o direta ---" )
5 for numero_da_linha , texto_da_linha in
enumerate ( f_poema_iter ) :
6 # texto_da_linha ainda inclui o ’\ n ’ no final , se
houver .
7 print ( f " Linha { numero_da_linha + 1}:
{ texto_da_linha . strip () } " )
8 except File NotFound Error :
9 print ( " Erro : O arquivo ’ poema . txt ’ n ã o foi encontrado . " )

Código 16.8: Iterando diretamente sobre o objeto arquivo

Esta abordagem lê apenas uma linha por vez na memória, sendo ideal para arquivos de
qualquer tamanho.
306 Capítulo 16. Leitura e Escrita de Arquivos

A Importância da Codificação (encoding) na Leitura


Reiterando o que foi dito na Seção 16.2, ao ler arquivos de texto, especificar o encoding
correto (geralmente "utf-8") é crucial para evitar erros UnicodeDecodeError ou proble-
mas com acentuação e caracteres especiais.

Exemplo Prático: Ler um Arquivo de Configuração Simples


Imagine um arquivo [Link] com o conteúdo:

usuario=alan_afif_helal
servidor=[Link]
porta=8080
# Linha de comentario
tema=escuro

Poderíamos lê-lo assim:


1 # Supondo que voc ê criou o arquivo ’ config . txt ’
2 n o m e _ a r q u i v o _ d e _ c o n f i g = " config . txt "
3 c o n f i g u r a c o e s _ d o _ a p p = {}
4

5 try :
6 with open ( nome_arquivo_de_config , " r " , encoding = " utf -8 " ) as
f_config :
7 print ( f " \ nLendo configura ç õ es de
’{ n o m e _ a r q u i v o _ d e _ c o n f i g } ’: " )
8 for linh a_lida_c onfig in f_config :
9 linha_processada = linha_l ida_conf ig . strip ()
10 if linha_processada and not
linha_processada . startswith ( " # " ) :
11 partes_da_linha = linha_processada . split ( " = " , 1)
12 if len ( partes_da_linha ) == 2:
13 chave_config = partes_da_linha [0]. strip ()
14 valor_config = partes_da_linha [1]. strip ()
15 c o n f i g u r a c o e s _ d o _ a p p [ chave_config ] =
valor_config
16
17 print ( " Configura ç õ es carregadas : " )
18 for chave , valor in c o n f i g u r a c o e s _ d o _ a p p . items () :
19 print ( f " ’{ chave } ’: ’{ valor } ’ " )
20
21 except File NotFound Error :
22 print ( f " Erro : Arquivo ’{ n o m e _ a r q u i v o _ d e _ c o n f i g } ’ n ã o
encontrado . " )

Código 16.9: Lendo e processando um arquivo de configuração

Este exemplo combina a leitura linha a linha com métodos de string (como strip() e
split()) para extrair informações.
A leitura de arquivos de texto é uma habilidade fundamental. Escolher o método
correto depende do tamanho do arquivo e de como você precisa processar seu conteúdo.
16.4. Escrevendo em Arquivos de Texto 307

16.4 Escrevendo em Arquivos de Texto


Além de ler, Python nos permite escrever dados em arquivos, essencial para salvar
resultados, criar logs, gerar relatórios, etc. O processo envolve abrir o arquivo em um modo
de escrita (’w’ ou ’a’), usar métodos para escrever, e garantir o fechamento (idealmente
com with). Lembre-se do encoding="utf-8".
Modos de Abertura para Escrita (Relembrando):

• ’w’ (Write): Cria/Sobrescreve o arquivo. CUIDADO: apaga conteúdo existente!

• ’a’ (Append): Cria/Anexa ao final do arquivo. Não apaga conteúdo existente.

• ’x’ (Exclusive Creation): Cria um novo arquivo. Falha se já existir.

Escrevendo Strings com o Método [Link](string)


Este método escreve a sua_string no arquivo. Ele não adiciona \n automaticamente.
1 # Escrevendo em um novo arquivo ( ou sobrescrevendo )
2 no me _ar qu iv o_p oe ma = " meu_poema . txt "
3 try :
4 with open ( nome_arquivo_poema , " w " , encoding = " utf -8 " ) as
f_escrita :
5 f_escrita . write ( " No meio do caminho tinha uma pedra .\ n " ) #
\ n para nova linha
6 f_escrita . write ( " Tinha uma pedra no meio do caminho .\ n " )
7 print ( f " Arquivo ’{ n om e_a rq ui vo_ po em a } ’ foi escrito . " )
8
9 # Anexando ao mesmo arquivo
10 with open ( nome_arquivo_poema , " a " , encoding = " utf -8 " ) as
f_anexar :
11 f_anexar . write ( " \n - - - Adi ç ã o - - -\ n " )
12 f_anexar . write ( " Mais um verso adicionado .\ n " )
13 print ( f " Conte ú do anexado a ’{ nom e_ ar qui vo _p oem a } ’. " )
14 except IOError as e :
15 print ( f " Erro de E / S : { e } " )

Código 16.10: Escrevendo e anexando a um arquivo com write()

Escrevendo Múltiplas Linhas


O método writelines() recebe um iterável de strings e escreve cada string no arquivo.
Ele também não adiciona \n automaticamente.
1 l i nh as_ d o_ r e la t or i o = [
2 " Relat ó rio Final \ n " ,
3 " - - - - - - - - - - - - - - - -\ n " ,
4 " Dados processados .\ n "
5 ]
6 n o m e _ a r q u i v o _ r e l a t o r i o = " relatorio_final . txt "
7 try :
8 with open ( nome_arquivo_relatorio , " w " , encoding = " utf -8 " ) as
f_relatorio :
9 f_relatorio . writelines ( l in h as _ d o_ r el a to r i o )
10 print ( f " Arquivo ’{ n o m e _ a r q u i v o _ r e l a t o r i o } ’ escrito com
writelines () . " )
308 Capítulo 16. Leitura e Escrita de Arquivos

11 except IOError as e :
12 print ( f " Erro de E / S ao escrever o relat ó rio : { e } " )

Código 16.11: Usando writelines() para escrever múltiplas linhas

Se as strings na sua lista não terminarem com \n, você pode adicioná-lo em um laço for
com write().

Buffering de Saída e flush() (Breve Menção)


Python pode usar buffering para otimizar a escrita, o que significa que os dados não são
gravados no disco imediatamente. O buffer é descarregado (flushed) quando o arquivo é
fechado (o que with open(...) faz automaticamente) ou com [Link]() explícito
(raramente necessário para usos comuns).

Exemplo Prático Combinado: Salvando uma Lista de Tarefas


1 # Lista de tarefas ( poderia vir de um input do usu á rio )
2 li s t a _ d e _ t a r e f a s _ d i a r i a s = [
3 " Revisar emails importantes " ,
4 " Preparar relat ó rio semanal " ,
5 " Estudar Python "
6 ]
7 n o m e _ a r q u i v o _ t a r e f a s = " tarefas_hoje . txt "
8

9 try :
10 with open ( nome_arquivo_tarefas , " w " , encoding = " utf -8 " ) as
f_tarefas :
11 f_tarefas . write ( " Minhas Tarefas para Hoje :\ n " )
12 f_tarefas . write ( " = = = = = = = = = = = = = = = = = = = = = = = = \ n " )
13 for indice , tarefa_item in
enumerate ( l i s t a _ d e _ t a r e f a s _ d i a r i a s ) :
14 f_tarefas . write ( f " { indice + 1}. { tarefa_item }\ n " )
15 f_tarefas . write ( " \n - - - Fim da Lista ---" )
16 print ( f " Lista de tarefas salva em ’{ n o me _ a r q u i v o _ t a r e f a s } ’. " )
17 except IOError as e :
18 print ( f " Erro de E / S ao salvar tarefas : { e } " )

Código 16.12: Salvando uma lista de tarefas em um arquivo

A capacidade de escrever em arquivos é fundamental. Os métodos write() e


writelines() são as ferramentas principais, e é crucial lembrar de incluir \n para novas
linhas e usar with open(...).

16.5 Trabalhando com Caminhos de Arquivo (Módulo


[Link])
Ao interagir com arquivos, frequentemente precisamos manipular os nomes e caminhos
dos arquivos de forma programática. Python oferece o submódulo [Link] (parte do
módulo os) com muitas funções úteis para isso, que funcionam de forma independente do
sistema operacional. Para usá-las, você geralmente fará import os ou import [Link].
16.5. Trabalhando com Caminhos de Arquivo (Módulo [Link]) 309

Construindo Caminhos de Forma Portável: [Link]() (Revi-


são)
Esta função junta componentes de caminho usando o separador correto do SO atual (/
ou \).
1 import os . path # Pode ser apenas ’ import os ’ e usar ’ os . path . funcao ’
2

3 pasta1 = " documentos "


4 pasta2 = " relatorios "
5 arquivo = " vendas_2025 . txt "
6
7 caminho_final = os . path . join ( pasta1 , pasta2 , arquivo )
8 print ( f " Caminho constru í do : { caminho_final } " )
9 # Sa í da Windows : documentos \ relatorios \ vendas_2025 . txt
10 # Sa í da macOS / Linux : documentos / relatorios / vendas_2025 . txt

Código 16.13: Usando [Link]() para portabilidade

Verificando Existência: [Link](caminho)


Retorna True se o caminho (arquivo ou diretório) existir, False caso contrário.
1 import os . path
2
3 # Supondo que ’ meu_diario . txt ’ foi criado em se ç õ es anteriores
4 a r qu ivo _ pa r a _c h ec a r = " meu_diario . txt "
5 if os . path . exists ( a r qu i vo _ p ar a _c h ec a r ) :
6 print ( f " ’{ a rq u i vo _ pa r a _c h ec a r } ’ existe . " )
7 else :
8 print ( f " ’{ a rq u i vo _ pa r a _c h ec a r } ’ N Ã O existe . " )

Código 16.14: Usando [Link]()

Verificando se é Arquivo ou Diretório


• [Link](caminho): True se existir e for um arquivo.

• [Link](caminho): True se existir e for um diretório.

1 import os . path
2 # ( Pode ser necess á rio criar ’ m i nh a _p a s ta _ ex e m pl o / ’ manualmente
para o teste de isdir )
3 # Supondo ’ meu_diario . txt ’ existe como arquivo .
4
5 caminho_arquivo = " meu_diario . txt "
6 caminho_pasta = " m i nh a _p a st a _ ex e mp l o " # Crie esta pasta para testar
7
8 if os . path . exists ( caminho_arquivo ) and
os . path . isfile ( caminho_arquivo ) :
9 print ( f " ’{ caminho_arquivo } ’ é um arquivo . " )
10

11 if os . path . exists ( caminho_pasta ) and os . path . isdir ( caminho_pasta ) :


12 print ( f " ’{ caminho_pasta } ’ é um diret ó rio . " )
13 else :
310 Capítulo 16. Leitura e Escrita de Arquivos

14 print ( f " ’{ caminho_pasta } ’ n ã o é um diret ó rio existente ( ou n ã o é


um diret ó rio ) . " )

Código 16.15: Usando [Link]() e [Link]()

Obtendo o Caminho Absoluto: [Link](caminho_relativo)


Converte um caminho relativo para um caminho absoluto completo.
1 import os . path
2
3 caminho_rel = " meu_script . py "
4 caminho_abs = os . path . abspath ( caminho_rel )
5 print ( f " Caminho relativo : ’{ caminho_rel } ’ " )
6 print ( f " Caminho absoluto : { caminho_abs } " )

Código 16.16: Usando [Link]()

Extraindo Partes de um Caminho


• [Link](caminho): Retorna a parte final (nome do arquivo/diretório).

• [Link](caminho): Retorna a parte do diretório (tudo exceto o basename).

• [Link](caminho): Retorna uma tupla (nome_base, .extensao).

1 import os . path
2

3 # Use um caminho apropriado para seu sistema para testar


4 # caminho_exemplo = "/ usr / local / bin / python3 " # Exemplo Linux / macOS
5 caminho_exemplo = " C :\\ Python311 \\ python . exe " # Exemplo Windows
( note as barras duplas )
6
7 nome_final = os . path . basename ( caminho_exemplo )
8 print ( f " Nome final ( basename ) : { nome_final } " )
9
10 diretorio = os . path . dirname ( caminho_exemplo )
11 print ( f " Diret ó rio ( dirname ) : { diretorio } " )
12
13 nome_raiz , extensao = os . path . splitext ( caminho_exemplo )
14 print ( f " Nome raiz : { nome_raiz } " )
15 print ( f " Extens ã o : { extensao } " )
16
17 nome_arq_simples , ext_arq_simples =
os . path . splitext ( " ma nual_do_ usuario . pdf " )
18 print ( f " Nome simples : ’{ nome_arq_simples } ’ , Extens ã o :
’{ ext_arq_simples } ’ " )

Código 16.17: Extraindo partes de um caminho com [Link]

Outras Funções Úteis (Breve Menção)


O módulo [Link] (e os) tem mais funcionalidades, como:
• [Link](caminho): Tamanho do arquivo em bytes.
16.6. Arquivos Binários (Breve Introdução) 311

• [Link](caminho): Timestamp da última modificação.

• [Link](diretorio): Lista arquivos e pastas de um diretório.

O módulo [Link] é indispensável para manipular caminhos de arquivos de forma robusta


e portável.

16.6 Arquivos Binários (Breve Introdução)


Até agora, focamos em arquivos de texto. No entanto, muitos arquivos (imagens, áudio,
executáveis) são arquivos binários, contendo dados que não são texto puro, mas sim
uma sequência de bytes.

Diferença Fundamental: Texto vs. Binário


• Modo Texto: Python decodifica bytes para strings na leitura e codifica strings
para bytes na escrita, usando um encoding.

• Modo Binário: Python lê e escreve os bytes exatamente como estão, sem


conversão de caracteres. O resultado da leitura é um objeto bytes.

Quando Usar o Modo Binário?


Use para arquivos de mídia (imagens, áudio, vídeo), executáveis, arquivos compactados,
formatos de documentos complexos, ou qualquer arquivo onde você precise dos bytes
exatos.

Abrindo Arquivos em Modo Binário


Adicione ’b’ ao modo na função open() (ex: ’rb’ para leitura binária, ’wb’ para
escrita binária, ’ab’ para anexação binária). Não especifique o parâmetro encoding
no modo binário.

Lendo e Escrevendo Dados Binários


• arquivo_binario.read(tamanho) retorna um objeto bytes.

• arquivo_binario.write(objeto_bytes) espera um objeto bytes (ou bytearray).


Para escrever uma string, você deve primeiro codificá-la (ex: minha_string.encode(’utf-8’)).

Exemplo Prático: Copiando um Arquivo de Imagem (Pequeno)


(Certifique-se de ter um arquivo logo_python.png no mesmo diretório para testar.)
1 # Exemplo : Copiando um arquivo de imagem
2 # CRIE ’ logo_python . png ’ para este exemplo .
3
4 n o me _ar q ui v o _o r ig e m = " logo_python . png "
5 n o m e _ a r q u i v o _ d e s t i n o = " c o p i a _ d o _ l o g o _p y t h o n . png "
6 t a m a n h o _ d o _ b l o c o _ l e i t u r a = 4096 # Ler em blocos de 4 KB
7
8 try :
312 Capítulo 16. Leitura e Escrita de Arquivos

9 with open ( nome_arquivo_origem , " rb " ) as arquivo_origem : # ’ rb ’


= leitura bin á ria
10 with open ( nome_arquivo_destino , " wb " ) as arquivo_destino : #
’ wb ’ = escrita bin á ria
11
12 print ( f " Copiando ’{ n o me _ a rq u iv o _ or i ge m } ’ para
’{ n o m e _ a r q u iv o _ d e s t i n o } ’... " )
13 while True :
14 bl o c o_ d e_ b y te s _l i do =
arquivo_origem . read ( t a m a n h o _ d o _ b l o c o _ l e i t u r a )
15 if not b l oc o _d e _ by t es _ l id o : # Fim do arquivo de
origem
16 break
17 arquivo_destino . write ( bl o c o_ d e_ b y te s _l i do )
18 print ( " C ó pia do arquivo bin á rio conclu í da ! " )
19
20 except File NotFound Error :
21 print ( f " Erro : Arquivo ’{ n o me _ ar q u iv o _o r ig e m } ’ n ã o encontrado . " )
22 except IOError as e :
23 print ( f " Erro de E / S durante a c ó pia : { e } " )

Código 16.18: Copiando um arquivo de imagem em modo binário


Este código copia os bytes brutos. Para entender formatos específicos (JPEG, MP3), são
necessárias bibliotecas especializadas.

Considerações Importantes
• Não tente imprimir bytes como se fossem texto diretamente.

• Para manipular formatos binários específicos, use bibliotecas Python adequadas.

O modo binário é essencial para arquivos não textuais, tratando dados como sequências
puras de bytes.

Concluindo o Capítulo sobre Leitura e Escrita de Arquivos


Neste capítulo, exploramos o processo de interação com arquivos em Python. Vimos
como a função open(), especialmente com a instrução with, nos permite abrir arquivos
em diferentes modos (’r’, ’w’, ’a’) e a importância do encoding para arquivos de texto.
Aprendemos os métodos para ler (read(), readline(), readlines()) e escrever
(write(), writelines()) em arquivos de texto, além da forma Pythônica de iterar sobre
o objeto arquivo. Também revisamos o módulo [Link] para manipular caminhos de
arquivo e demos uma breve introdução aos arquivos binários.
A capacidade de ler e escrever arquivos permite que seus programas persistam dados,
leiam configurações e gerem resultados de forma útil.
No próximo capítulo, continuaremos na Parte V, abordando o Tratamento de Exce-
ções, que nos ensinará como lidar com erros e situações inesperadas, incluindo erros ao
trabalhar com arquivos.
Capítulo 17

Tratamento de Exceções

Até agora em nossa jornada com Python, focamos em aprender a sintaxe da linguagem,
a usar diferentes tipos de dados, operadores, estruturas de controle, funções, módulos,
classes e, no capítulo anterior, como interagir com arquivos. Nossos exemplos, em sua
maioria, assumiram que tudo ocorreria conforme o esperado.
No entanto, no mundo real da programação, as coisas nem sempre saem como plane-
jado. Usuários podem digitar informações inesperadas, arquivos podem estar faltando ou
corrompidos, e muitos outros tipos de “imprevistos” podem acontecer durante a execução
de um programa. Se não estivermos preparados para lidar com essas situações, nossos
programas podem “quebrar” abruptamente.
É aqui que entra o tratamento de exceções. Python possui um mecanismo poderoso
para lidar com erros que ocorrem durante a execução (chamados de exceções), permitindo
que nossos programas respondam a essas situações de forma controlada.

17.1 Introdução a Erros e Exceções: Quando as Coisas


Saem do Script
Ao programar, podemos encontrar dois tipos principais de “problemas”: erros de sintaxe
e exceções.

Erros de Sintaxe (Syntax Errors)


Estes são erros porque o código não segue as regras gramaticais do Python. O
interpretador os detecta antes da execução, e o programa não roda. O Python geralmente
aponta para a linha do erro com uma mensagem como SyntaxError. Exemplos comuns:

• Esquecer os dois pontos (:) no final de um if, while, def, etc.

• Usar uma palavra reservada como nome de variável.

• Parênteses ou aspas desbalanceados.

• Indentação incorreta (IndentationError).

1 # Exemplo de erro de sintaxe ( faltam os ’: ’ aqui !)


2 idade = 20
3 if idade >= 18

313
314 Capítulo 17. Tratamento de Exceções

4 print ( " Maior de idade " )


5 # Se tentar rodar , receber á um SyntaxError .

Código 17.1: Exemplo de erro de sintaxe


A correção envolve revisar o código e garantir que ele siga as regras da linguagem.

Exceções (Exceptions - Erros em Tempo de Execução)


Diferentemente dos erros de sintaxe, as exceções são erros que ocorrem durante a
execução do programa, mesmo que a sintaxe esteja correta. Elas surgem quando uma
operação válida sintaticamente resulta em uma situação que o Python não consegue lidar
normalmente. Se uma exceção não é “tratada” (ou “capturada”), o programa para e exibe
um “traceback” (rastreamento do erro).
Exemplos comuns de exceções embutidas:
• ZeroDivisionError: Tentar dividir por zero (ex: 10 / 0).
• TypeError: Operação em tipo de dado inadequado (ex: “texto” + 5).
• NameError: Usar variável ou função não definida.
• IndexError: Acessar índice inválido em uma sequência.
• KeyError: Acessar chave inexistente em um dicionário.
• FileNotFoundError: Tentar abrir um arquivo inexistente para leitura.
• ValueError: Função recebe argumento de tipo correto, mas valor inapropriado (ex:
int("olá")).

1 # Exemplo que pode gerar ZeroDi visionEr ror ou ValueError


2 numerador = 100
3 denominador_str = input ( " Digite um denominador ( tente 0 ou
’ texto ’) : " )
4 denominador = int ( denominador_str )
5 resultado = numerador / denominador
6 print ( f " Resultado : { resultado } " )

Código 17.2: Exemplo que pode gerar uma exceção

Por que o Tratamento de Exceções é Importante?


O tratamento de exceções é crucial para criar programas robustos. Ele permite:
1. Evitar que o Programa Pare Abruptamente: Lidar com o erro de forma
elegante (ex: mensagem amigável, tentar novamente).
2. Lidar com Entradas Inválidas: Validar entradas e responder graciosamente.
3. Gerenciar Falhas de Recursos Externos: Arquivos, redes, bancos de dados, etc.
4. Construir Software Mais Resiliente: Programas mais tolerantes a falhas.
Neste capítulo, aprenderemos as ferramentas do Python para “tentar” executar código,
“capturar” exceções e “tratar” essas situações de erro.
17.2. O Bloco try-except: Capturando e Tratando Exceções 315

17.2 O Bloco try-except: Capturando e Tratando Ex-


ceções
A principal maneira de lidar com exceções em Python é utilizando a estrutura
try-except. A ideia é “tentar” (try) executar um bloco de código que pode gerar
uma exceção e, se uma exceção específica ocorrer, “capturá-la” (except) e executar um
bloco de código alternativo para tratar o erro.

Sintaxe Básica do Bloco try-except


A forma mais fundamental é:
1 try :
2 # Bloco de c ó digo " protegido "
3 # Instru ç õ es que PODEM gerar uma exce ç ã o
4 # ...
5 except T i p o D e E x c e c a o E s p e c i f i c a :
6 # Bloco de c ó digo de tratamento
7 # Executa SE T i p o D e E x c e c a o E s p e c i f i c a ocorrer no ’ try ’.
8 # ...
9 # O programa continua aqui ap ó s o try - except .

Código 17.3: Sintaxe básica do bloco try-except

• try: Inicia o bloco de código monitorado.

• Bloco try (Indentado): Contém o código que pode falhar.

• except TipoDeExcecaoEspecifica:: Define um bloco que trata a exceção nomeada,


se ela ocorrer no bloco try.

• Bloco except (Indentado): Contém o código para lidar com o erro.

Como Funciona o Fluxo try-except


1. O Python executa as instruções no bloco try.

2. Se nenhuma exceção ocorrer: O bloco except é ignorado. A execução continua


após a estrutura try-except.

3. Se uma exceção ocorrer no bloco try:

(a) A execução normal do bloco try é interrompida.


(b) Python procura uma cláusula except que corresponda ao tipo da exceção.
(c) Se encontrada, o bloco except correspondente é executado. Após sua execução,
o programa continua após toda a estrutura try-except.
(d) Se nenhuma except correspondente for encontrada, la exceção não é tratada e
o programa termina (ou la exceção se propaga).
316 Capítulo 17. Tratamento de Exceções

Exemplos Práticos
• Exemplo 1: Tratando ZeroDivisionError
1 print ( " Vamos tentar uma divis ã o . " )
2 numerador = 100
3 denom inador_v alor = 0 # For ç ando o erro
4
5 try :
6 print ( " Iniciando o bloco try ... " )
7 re sultado_ divisao = numerador / d enominad or_valor
8 print ( f " O resultado da divis ã o é
{ resultad o_divisa o }. " ) # N ã o ser á alcan ç ada
9 except ZeroDivis ionError :
10 print ( " Oops ! Ocorreu um erro de divis ã o por zero . " )
11 print ( " N ã o podemos dividir por zero . " )
12
13 print ( " O programa continua aqui . " )

Código 17.4: Tratando ZeroDivisionError

• Exemplo 2: Tratando ValueError na conversão de input()


1 print ( " \n - - - Conversor de idade ---" )
2 i da de_ hu ma na_ te xt o = input ( " Digite sua idade em anos
( apenas n ú meros ) : " )
3 try :
4 i da d e_ h u ma n a_ n u me r o = int ( i da de_ hu ma na_ te xt o )
5 if id a d e_ h um a na _ n um e ro < 0:
6 print ( " Idade n ã o pode ser negativa ! " )
7 else :
8 print ( f " Sua idade como n ú mero é
{ i d ad e _h u m an a _n u m er o }. " )
9 except ValueError :
10 print ( f " Erro : ’{ id ad e_h um an a_t ex to } ’ n ã o é um n ú mero
inteiro v á lido . " )
11 print ( " Fim do programa de convers ã o . " )

Código 17.5: Tratando ValueError de input()

• Exemplo 3: Tratando FileNotFoundError


1 nome_do_arquivo = input ( " Digite o nome do arquivo para ler
( ex : dados . txt ) : " )
2 try :
3 with open ( nome_do_arquivo , " r " , encoding = " utf -8 " ) as
arquivo_lido :
4 conteudo = arquivo_lido . read ()
5 print ( " \n - - - Conte ú do do Arquivo ---" )
6 print ( conteudo )
7 except FileNotFo undError :
8 print ( f " Erro : O arquivo ’{ nome_do_arquivo } ’ n ã o foi
encontrado ! " )
9 print ( f " \ nTentativa de leitura de ’{ nome_do_arquivo } ’
finalizada . " )

Código 17.6: Tratando FileNotFoundError ao abrir arquivo


17.3. Capturando Múltiplas Exceções: Lidando com Diferentes Tipos de Erros 317

A Importância de Ser Específico no except


É uma boa prática especificar o tipo de exceção que você espera capturar (ex: except
ValueError:). Isso evita capturar acidentalmente outros erros inesperados que indicam
um bug real no seu código.
O bloco try-except é a forma fundamental de tornar os programas Python mais
robustos, permitindo que eles lidem com erros em tempo de execução de forma controlada.

17.3 Capturando Múltiplas Exceções: Lidando com Di-


ferentes Tipos de Erros
Na seção anterior, vimos como usar try-except para uma exceção específica. No
entanto, um mesmo bloco try pode gerar diferentes tipos de exceções. Python nos permite
tratar cada uma delas apropriadamente.

Múltiplas Cláusulas except


Você pode ter várias cláusulas except após um try, cada uma para um tipo de
exceção. Python testa as cláusulas except na ordem em que aparecem, e a primeira que
corresponder ao tipo da exceção levantada terá seu bloco executado.
1 print ( " --- Calculadora de Divis ã o Segura ---" )
2 numerador_str = input ( " Digite o numerador : " )
3 denominador_str = input ( " Digite o denominador : " )
4
5 try :
6 numerador = float ( numerador_str )
7 denominador = float ( denominador_str )
8 print ( " Tentando realizar a divis ã o ... " )
9 resultado = numerador / denominador
10 print ( f " O resultado de { numerador } / { denominador } é
{ resultado :.2 f }. " )
11 except ValueError :
12 print ( " Erro de Valor : Uma das entradas n ã o é um n ú mero v á lido . " )
13 except Zero Division Error :
14 print ( " Erro Matem á tico : N ã o é poss í vel dividir um n ú mero por
zero . " )
15

16 print ( " \n - - - Fim da tentativa de c á lculo ---" )

Código 17.7: Tratando ValueError e ZeroDivisionError separadamente

Agrupando Múltiplas Exceções em uma Única Cláusula except


Se você quer executar o mesmo bloco de tratamento para diferentes tipos de exceção,
pode agrupá-los em uma tupla: except (TipoExcecao1, TipoExcecao2):
1 try :
2 numero_texto = input ( " Digite um n ú mero para dobrar : " )
3 if not numero_texto . isdigit () : # Verifica ç ã o simples antes
4 raise TypeError ( " Isso n ã o parece um n ú mero inteiro . " )
5 numero = int ( numero_texto )
6 print ( f " O dobro é { numero * 2} " )
318 Capítulo 17. Tratamento de Exceções

7 except ( ValueError , TypeError ) : # Captura ValueError OU TypeError


8 print ( " Ocorreu um erro de valor ou tipo . Insira um n ú mero
inteiro v á lido . " )

Código 17.8: Agrupando exceções em uma cláusula except

Capturando a Instância da Exceção com as nome_variavel


Para obter mais informações sobre a exceção (como a mensagem de erro específica),
capture a instância da exceção em uma variável usando as nome_variavel. Sintaxe:
except TipoDeExcecao as e:
1 try :
2 x = 10
3 y_str = input ( " Digite um divisor para 10 ( tente 0 ou texto ) : " )
4 y = int ( y_str )
5 print ( f " Resultado : { x / y } " )
6 except ( ValueError , Z eroDivis ionError ) as erro_capturado :
7 print ( f " \n - - - Um Erro Aconteceu ! ---" )
8 print ( f " Tipo do Erro : { type ( erro_capturado ) . __name__ } " )
9 print ( f " Mensagem do Erro : { erro_capturado } " )

Código 17.9: Capturando a instância da exceção

A Cláusula except Genérica (Usar com Cautela!)


• except Exception as e: Captura a maioria das exceções embutidas. É mais
seguro que except: sozinho, pois permite inspecionar o erro e.

• except: (sozinho) Captura absolutamente todas as exceções, incluindo SystemExit


e KeyboardInterrupt (Ctrl+C), que geralmente não queremos impedir.

Cuidado: Uma cláusula except muito genérica pode mascarar bugs. Tente ser específico
sempre que possível. Use como último recurso em uma cadeia de excepts.
1 print ( " \ nTentativa com captura gen é rica : " )
2 try :
3 valor = int ( input ( " Digite um valor ( pode ser texto , 0 , ou um
n ú mero > 1) : " ) )
4 resultado = 100 / valor
5 lista_ex = [10 , 20]
6 print ( lista_ex [ valor ]) # Pode dar IndexError
7 print ( f " Resultado da divis ã o : { resultado } " )
8 except ValueError :
9 print ( " Erro espec í fico : N ã o foi poss í vel converter para
inteiro . " )
10 except Zero Division Error :
11 print ( " Erro espec í fico : Divis ã o por zero . " )
12 except Exception as e_geral : # Captura outras exce ç õ es
13 print ( f " Ocorreu uma exce ç ã o n ã o prevista especificamente . " )
14 print ( f " Tipo : { type ( e_geral ) . __name__ } , Mensagem : { e_geral } " )

Código 17.10: Exemplo de captura genérica com Exception as e


17.4. A Cláusula else no Bloco try-except 319

Hierarquia de Exceções e a Ordem das Cláusulas except


As exceções formam uma hierarquia (ex: ZeroDivisionError é um ArithmeticError).
Ao usar múltiplas cláusulas except, a ordem importa: coloque as mais específicas antes
das mais genéricas.
1 # Ordem CORRETA ( mais espec í fico primeiro )
2 try :
3 # ...
4 except Zero Division Error : print ( " Divis ã o por zero ! " )
5 except ArithmeticError : print ( " Erro aritm é tico ! " )
6 except Exception : print ( " Outro erro ! " )
7

8 # Ordem INCORRETA ( Ze roDivisi onError nunca seria alcan ç ado )


9 try :
10 # ...
11 except ArithmeticError : print ( " Erro aritm é tico ! " ) # Capturaria
Zero DivisionE rror
12 except Zero Division Error : print ( " Divis ã o por zero ! " )

Código 17.11: Ordem correta vs. incorreta de cláusulas except


A capacidade de tratar diferentes tipos de exceções de forma específica ou agrupada torna
o tratamento de exceções muito mais robusto.

17.4 A Cláusula else no Bloco try-except


A estrutura try-except pode ser estendida com uma cláusula opcional else. No
contexto de try-except, o bloco else é executado se, e somente se, nenhuma exceção
ocorrer dentro do bloco try.

Sintaxe da Cláusula else com try-except


A estrutura completa é:
1 try :
2 # Bloco de c ó digo de " tentativa "
3 # ...
4 except TipoDeExcecaoA :
5 # Bloco de tratamento para ExcecaoA
6 # ...
7 # ( mais excepts opcionais )
8 else :
9 # Bloco de c ó digo do ’ else ’
10 # Executa SOMENTE SE nenhuma exce ç ã o ocorrer no bloco ’ try ’.
11 # ...
12 # ( finally opcional aqui )
13 # C ó digo continua ap ó s a estrutura

Código 17.12: Sintaxe do bloco try-except-else


O bloco else deve vir após todas as cláusulas except.

Como Funciona o Fluxo com else


1. O Python executa o bloco try.
320 Capítulo 17. Tratamento de Exceções

2. Se uma exceção ocorrer e for capturada por um except: O bloco except é


executado, e o bloco else é ignorado.

3. Se NENHUMA exceção ocorrer no bloco try: Todas as cláusulas except são


ignoradas, e o bloco else é executado.

4. A execução continua após toda a estrutura.

Por que Usar a Cláusula else?


• Clareza de Intenção: Separa o código da “tentativa” do código que depende do
sucesso dessa tentativa.

• Evitar Captura Acidental de Exceções: Código no else não é protegido pelas


cláusulas except anteriores. Se ele gerar uma exceção, ela será uma exceção não
tratada (a menos que o próprio else tenha um try-except).

Exemplos Práticos
• Exemplo 1: Divisão bem-sucedida
1 numer ador_ex_ else = 10
2 d e n o m i n a d o r _ e x _ e l s e _ s t r = input ( " Digite o denominador para
dividir 10: " )
3
4 try :
5 d en o mi n a do r _e x _ el s e = float ( d e n o m i n a d o r _ e x _ e l s e _ s t r )
6 re sul ta do _di v_ el se = num erador_e x_else /
d en o m in a do r _e x _ el s e
7 except ValueError :
8 print ( " Erro : Entrada inv á lida . Digite um n ú mero . " )
9 except ZeroDivis ionError :
10 print ( " Erro : N ã o é poss í vel dividir por zero . " )
11 else :
12 # S Ó executa se n ã o houver ValueError NEM
ZeroD ivisionE rror
13 print ( f " A divis ã o foi bem - sucedida ! Resultado :
{ re su lt ado _d iv _el se :.2 f } " )
14 print ( " Nenhuma exce ç ã o ocorreu durante o c á lculo . " )
15
16 print ( " Fim da opera ç ã o de divis ã o com ’ else ’. " )

Código 17.13: Divisão segura com bloco else

• Exemplo 2: Processamento de arquivo após abertura bem-sucedida


1 n o m e _ d o _ a r q u i v o _ p a r a _ p r o c e s s a r = " meu_relatorio . txt "
2 # ( Crie este arquivo com algum conte ú do para o teste
completo )
3 dados_do_arquivo = None
4
5 try :
6 print ( f " Tentando abrir :
’{ n o m e _ d o _ a r q u i v o _ p a r a _ p r o c e s s a r } ’... " )
7 with open ( nome_do_arquivo_para_processar , " r " ,
encoding = " utf -8 " ) as arquivo_proc :
17.5. A Cláusula finally: Código que Sempre Executa 321

8 print ( " Arquivo aberto ! Lendo o conte ú do ... " )


9 dados_do_arquivo = arquivo_proc . readlines ()
10 except F ileNotFo undError :
11 print ( f " Erro : Arquivo
’{ n o m e _ d o _ a r q u i v o _ p a r a _ p r o c e s s a r } ’ n ã o encontrado . " )
12 except Exception as e :
13 print ( f " Ocorreu um erro inesperado ao ler : { e } " )
14 else :
15 # S Ó executa se o ’ try ’ foi bem - sucedido
16 print ( " \ nBloco ’ else ’: Arquivo lido sem problemas . " )
17 if dados_do_arquivo :
18 print ( f " N ú mero de linhas no arquivo :
{ len ( dados_do_arquivo ) } " )
19 # Aqui voc ê faria o processamento real do
’ dados_do_arquivo ’
20 print ( " Conte ú do processado com sucesso
( simula ç ã o ) . " )
21 else :
22 print ( " O arquivo foi aberto , mas est á vazio . " )
23
24 print ( " Fim do processamento do relat ó rio . " )

Código 17.14: Processando arquivo com bloco else

A cláusula else no contexto try-except ajuda a organizar melhor o tratamento de


exceções e a lógica que depende do sucesso de um bloco de código propenso a erros.

17.5 A Cláusula finally: Código que Sempre Executa


Além das cláusulas try, except e else, a estrutura de tratamento de exceções do
Python oferece uma quarta cláusula opcional: finally. O propósito do bloco finally
é definir um conjunto de instruções que sempre será executado, não importa o que
aconteça nos blocos anteriores.

Sintaxe da Cláusula finally


A cláusula finally é a última parte da estrutura try:
1 try :
2 # Bloco de " tentativa "
3 # ...
4 except TipoDeExcecaoA :
5 # Tratamento para ExcecaoA
6 # ...
7 else : # Opcional
8 # Executa se n ã o houver exce ç ã o no try
9 # ...
10 finally :
11 # Bloco de c ó digo do ’ finally ’
12 # Este bloco é SEMPRE EXECUTADO .
13 # ...
14 # C ó digo continua ap ó s a estrutura

Código 17.15: Sintaxe do bloco try-except-else-finally


O bloco finally deve vir após todas as cláusulas except e else (se presente).
322 Capítulo 17. Tratamento de Exceções

Como Funciona o Fluxo com finally


O bloco finally é executado:
1. Após o bloco try ter sido executado com sucesso (e após o else, se houver).

2. Após um bloco except ter sido executado.

3. Mesmo se uma exceção ocorrer no try e não for capturada (o finally


executa, e depois a exceção se propaga).

4. Mesmo se houver return, break, ou continue nos blocos try, except, ou else.
O finally executa antes que o controle realmente saia.
Essencialmente, o finally é para código de “limpeza” que precisa ser executado.

Principal Caso de Uso: Liberar Recursos Externos


O uso mais comum é para garantir que recursos externos (arquivos abertos, conexões
de rede, conexões com bancos de dados, travas em programação concorrente, etc.) sejam
liberados corretamente, independentemente de erros.
Exemplo Prático (Ilustrativo, com Arquivo):
Lembre-se: para arquivos, a instrução with open(...) é a melhor prática e já cuida do
fechamento automático. Este exemplo serve para entender o mecanismo do finally.
1 # Exemplo para ilustrar o ’ finally ’
2 arquivo_objeto = None
3 n o m e _ d o _ a r q u i v o _ t e s t e = " dad os_com_f inally . txt "
4
5 try :
6 print ( f " \n - - - Iniciando bloco try para
’{ n o m e _ d o _ a r q u i v o _ t e s t e } ’ ---" )
7 arquivo_objeto = open ( nome_do_arquivo_teste , " w " ,
encoding = " utf -8 " )
8
9 e nt rad a_ do _us ua ri o = input ( " Digite ( ou
’ erro_divisao ’/ ’ erro_tipo ’) : " )
10
11 if en tr ad a_d o_ us uar io . lower () == " erro_divisao " :
12 r es u lt a d o_ c om _ fa l h a = 10 / 0 # Levanta ZeroDi visionEr ror
13 elif en tr ada _d o_ usu ar io . lower () == " erro_tipo " :
14 r es u lt a d o_ c om _ fa l h a = " texto " + 5 # Levanta TypeError
15 else :
16 arquivo_objeto . write ( en tra da _d o_u su ar io + " \ n " )
17 print ( " Dado escrito no arquivo . " )
18 except File NotFound Error :
19 print ( " Erro : O arquivo n ã o p ô de ser aberto / criado . " )
20 except Zero Division Error :
21 print ( " Erro no try : Ocorreu uma divis ã o por zero ! " )
22 except TypeError as e_tipo :
23 print ( f " Erro no try : Ocorreu um erro de tipo - { e_tipo } " )
24 except Exception as e_inesperado :
25 print ( f " Um erro inesperado ocorreu no try : { e_inesperado } " )
26 else :
27 print ( " Bloco ’ try ’ conclu í do sem exce ç õ es capturadas . " )
28 finally :
29 print ( " \n - - - Bloco finally sendo executado ---" )
17.6. Levantando Exceções com raise: Sinalizando Seus Próprios Erros 323

30 if arquivo_objeto :
31 if not arquivo_objeto . closed :
32 print ( " Fechando o arquivo no bloco finally ... " )
33 arquivo_objeto . close ()
34 print ( " Arquivo fechado . " )
35 else :
36 print ( " O arquivo j á estava fechado . " )
37 else :
38 print ( " O objeto arquivo n ã o foi criado . " )
39
40 print ( " --- Fim do exemplo com finally ---" )

Código 17.16: Ilustrando a cláusula finally com manipulação de arquivo

Experimente rodar o código com diferentes entradas para observar a execução do finally.

finally e a Instrução with


A instrução with (para objetos que são gerenciadores de contexto, como arquivos abertos
com open()) implementa um comportamento similar ao try...finally automaticamente,
garantindo a liberação do recurso. Por isso, para arquivos, with open(...) é mais conciso
e seguro. No entanto, finally continua essencial para outros tipos de gerenciamento de
recursos ou ações de limpeza gerais.
A cláusula finally garante a execução de código de limpeza crucial, tornando os
programas mais confiáveis.

17.6 Levantando Exceções com raise: Sinalizando Seus


Próprios Erros
Além de tratar exceções que o Python levanta automaticamente, nós também podemos
(e às vezes devemos) levantar nossas próprias exceções em nosso código. Isso é feito com a
instrução raise.

A Instrução raise
• Propósito: Sinalizar que uma condição de erro ou uma situação excepcional ocorreu.

• Sintaxe Básica:

– raise NomeDaExcecao()
– raise NomeDaExcecao("Mensagem descritiva do erro")
– raise (sozinho, dentro de um bloco except, para re-levantar a exceção captu-
rada)

• NomeDaExcecao pode ser uma exceção embutida (ValueError, TypeError, etc.) ou


uma personalizada.
324 Capítulo 17. Tratamento de Exceções

Quando Usar raise?


• Validação de entradas de funções/métodos.

• Sinalizar condições de erro específicas do domínio do problema.

• Impedir que um objeto entre em um estado inválido.

• Em métodos abstratos não implementados (raise NotImplementedError).

• Dentro de um bloco except para re-levantar uma exceção.

Exemplos Práticos
• Exemplo 1: Validando parâmetros de uma função
1 def c a l c u l a r _ d i v i s a o _ s e g u r a ( numerador , denominador ) :
2 " " " Divide , levantando exce ç õ es para entradas
inv á lidas . " " "
3 if not isinstance ( numerador , ( int , float ) ) or \
4 not isinstance ( denominador , ( int , float ) ) :
5 raise TypeError ( " Ambos os operandos devem ser
n ú meros . " )
6 if denominador == 0:
7 raise ValueError ( " O denominador n ã o pode ser
zero . " )
8 return numerador / denominador
9
10 testes = [(10 , 2) , (10 , 0) , ( " dez " , 2) ]
11 for num , den in testes :
12 try :
13 resultado = c a l c u l a r _ d i v i s a o _ s e g u r a ( num , den )
14 print ( f " { num } / { den } = { resultado } " )
15 except ( TypeError , ValueError ) as e :
16 print ( f " Erro ao tentar dividir { num } por { den }:
{e}")

Código 17.17: Levantando exceções para parâmetros inválidos

• Exemplo 2: Função que simula saque de conta bancária


1 def sacar_dinheiro ( saldo_atual , valor_saque ) :
2 " " " Simula um saque , levantando exce ç ã o para saldo
insuficiente . " " "
3 if not isinstance ( valor_saque , ( int , float ) ) or
valor_saque <= 0:
4 raise ValueError ( " Valor do saque deve ser
positivo . " )
5 if valor_saque > saldo_atual :
6 raise RuntimeError ( f " Saldo insuficiente
( R$ { saldo_atual :.2 f }) para sacar
R$ { valor_saque :.2 f }. " )
7
8 novo_saldo = saldo_atual - valor_saque
9 print ( f " Saque de R$ { valor_saque :.2 f } realizado . Novo
saldo : R$ { novo_saldo :.2 f } " )
10 return novo_saldo
11
17.6. Levantando Exceções com raise: Sinalizando Seus Próprios Erros 325

12 saldo_em_conta = 500.00
13 try :
14 saldo_em_conta = sacar_dinheiro ( saldo_em_conta , 100.00)
15 saldo_em_conta = sacar_dinheiro ( saldo_em_conta ,
700.00) # Deve dar erro
16 except ( ValueError , RuntimeError ) as e :
17 print ( f " Falha na opera ç ã o de saque : { e } " )

Código 17.18: Levantando RuntimeError para saldo insuficiente

• Exemplo 3: Re-levantando uma exceção (Reraise)


Uma função pode tratar parcialmente um erro e depois re-levantá-lo.
1 def l e r _ c o n f i g _ i m p o r t a n t e ( nome_arquivo_cfg ) :
2 try :
3 with open ( nome_arquivo_cfg , " r " , encoding = " utf -8 " )
as f_cfg :
4 token = f_cfg . readline () . strip ()
5 if not token :
6 raise ValueError ( " Token ausente no
arquivo . " )
7 print ( f " Token lido de ’{ nome_arquivo_cfg } ’:
{ token } " )
8 return token
9 except FileN otFoundE rror as e_fnf :
10 print ( f " [ LOG INTERNO ] Arquivo ’{ nome_arquivo_cfg } ’
n ã o encontrado : { e_fnf } " )
11 raise # Re - levanta a F ileNotFou ndError original
12 except ValueError as e_val :
13 print ( f " [ LOG INTERNO ] Erro de valor em
’{ nome_arquivo_cfg } ’: { e_val } " )
14 raise # Re - levanta a ValueError
15
16 # ( Para testar , crie arquivos como ’ config_ok . txt ’,
’ config_vazia . txt ’)
17 # no m e s _ a r q u i v o s _ t e s t e = [" config_ok . txt " ,
" config_vazia . txt " , " inexistente . txt "]
18 # for nome_arq in no m e s _ a r q u i v o s _ t e s t e :
19 # try :
20 # print ( f "\ nProcessando ’{ nome_arq } ’...")
21 # tok = l e r _ c o n f i g _ i m p o r t a n t e ( nome_arq )
22 # if tok : print (" Token obtido .")
23 # except ( FileNotFoundError , ValueError ) as e :
24 # print ( f " ERRO FINAL ao processar ’{ nome_arq } ’:
{ e }")

Código 17.19: Re-levantando uma exceção com raise

Escolhendo Qual Exceção Levantar


Tente usar as exceções embutidas mais apropriadas (ValueError, TypeError, etc.).
Se nenhuma se encaixar bem, pode-se usar uma mais genérica como RuntimeError ou,
idealmente (como veremos na próxima seção), criar sua própria exceção personalizada.
A instrução raise é a forma de o seu código sinalizar ativamente que uma condição de
erro ocorreu.
326 Capítulo 17. Tratamento de Exceções

17.7 Definindo Exceções Personalizadas


Embora Python tenha muitas exceções embutidas, às vezes é útil criar nossos próprios
tipos de exceção para tornar o código mais expressivo e permitir um tratamento de erros
mais granular e específico para nossa aplicação ou biblioteca.

Por que Criar Exceções Personalizadas?


• Semântica Clara: Dar nomes aos erros que fazem sentido no contexto do seu
problema (ex: SaldoInsuficienteError).

• Tratamento Específico: Permite que o código cliente capture e trate seus erros
específicos de forma diferenciada.

• Hierarquia de Erros: Você pode criar uma hierarquia de suas próprias exceções,
permitindo capturar um grupo de erros relacionados com uma única cláusula except.

Como Definir uma Exceção Personalizada


Crie uma nova classe que herda (direta ou indiretamente) da classe base embutida
Exception.

• Sintaxe Básica:
1 class M e u E r r o M u i t o E s p e c i f i c o ( Exception ) :
2 " " " Uma descri ç ã o opcional do que este erro
representa . " " "
3 pass # Geralmente , n ã o precisa de corpo .

Código 17.20: Definição básica de uma exceção personalizada

• Adicionando um Construtor (__init__) para Detalhes:


1 class ErroDeAplicacao ( Exception ) :
2 " " " Exce ç ã o base para erros da nossa aplica ç ã o . " " "
3 pass
4
5 class E r r o D e E n t r a d a I n v a l i d a ( ErroDeAplicacao ) :
6 def __init__ ( self , mensagem , campo_com_erro = None ) :
7 super () . __init__ ( mensagem ) # Passa a mensagem para
Exception
8 self . campo_com_erro = campo_com_erro
9
10 class V a l o r F o r a D o I n t e r v a l o E r r o r ( E r r o D e E n t r a d a I n v a l i d a ) :
11 def __init__ ( self , valor , minimo , maximo , campo = None ) :
12 msg = f " O valor { valor } est á fora do intervalo
({ minimo } -{ maximo }) . "
13 super () . __init__ ( mensagem = msg ,
campo_com_erro = campo )
14 self . valor_fornecido = valor
15 # ... outros atributos espec í ficos ...

Código 17.21: Exceção personalizada com construtor


17.7. Definindo Exceções Personalizadas 327

Levantando e Capturando Exceções Personalizadas


Levante com raise MinhaExcecao("Msg") e capture com except MinhaExcecao as
e:.
1 # ( Supondo as defini ç õ es de ErroDeAplicacao , ErroDeEntradaInvalida ,
2 # V a l o r F o r a D o I n t e r v a l o E r r o r do exemplo anterior )
3
4 def v a l i d a r _ i d a d e _ p a r a _ c a d a s t r o ( idade_str ) :
5 if not idade_str . isdigit () :
6 raise E r r o D e E n t r a d a I n v a l i d a ( " Idade deve conter apenas
d í gitos . " , campo_com_erro = " idade " )
7 idade = int ( idade_str )
8 if not (0 <= idade <= 120) :
9 raise V a l o r F o r a D o I n t e r v a l o E r r o r ( idade , 0 , 120 ,
campo = " idade " )
10 print ( f " Idade ’{ idade } ’ é v á lida . " )
11 return True
12
13 id ad es_ pa ra _te st ar = [ " 30 " , " abc " , " -5 " , " 150 " ]
14 for teste_idade in ida de s_ par a_ te st ar :
15 print ( f " \ nTestando idade : ’{ teste_idade } ’ " )
16 try :
17 v a l i d a r _ i d a d e _ p a r a _ c a d a s t r o ( teste_idade )
18 print ( " Valida ç ã o bem - sucedida ! " )
19 except V a l o r F o r a D o I n t e r v a l o E r r o r as e_intervalo :
20 print ( f " Erro de Intervalo Espec í fico : { e_intervalo } " )
21 except E r r o D e E n t r a d a I n v a l i d a as e_entrada :
22 print ( f " Erro de Entrada Personalizado : { e_entrada } " )
23 except Exception as e_inesperado :
24 print ( f " Um erro Python inesperado ocorreu : { e_inesperado } " )
25 print ( " -" * 20)

Código 17.22: Usando exceções personalizadas

Concluindo o Capítulo sobre Tratamento de Exceções


Neste capítulo, mergulhamos no importante mundo do tratamento de exceções em
Python. Diferenciamos erros de sintaxe de exceções e entendemos por que lidar com estas
últimas é crucial.
A estrutura try-except foi nossa principal ferramenta, e vimos como usar múltiplas
cláusulas except, agrupar exceções, e acessar a instância do erro. Exploramos também
as cláusulas else (código para quando não há exceção) e finally (código que sempre
executa, ideal para limpeza). Aprendemos a levantar nossas próprias exceções com raise
e como definir exceções personalizadas para dar mais semântica e controle aos erros
específicos de nossas aplicações.
O tratamento de exceções é fundamental para construir software robusto, confiável e
que lida graciosamente com imprevistos. Com as ferramentas desta Parte V (Manipulação
de Arquivos e Tratamento de Exceções), seus programas Python se tornam muito mais
capazes de interagir com o mundo real e de lidar com suas imperfeições.
Na Parte VI, mudaremos nosso foco para algumas das poderosas Bibliotecas Es-
senciais que o ecossistema Python oferece, especialmente aquelas úteis para computação
numérica, análise e visualização de dados.
Parte VI

Bibliotecas Essenciais para Engenharia

328
Capítulo 18

NumPy para Computação Numérica

Até este ponto do livro, construímos uma base sólida nos fundamentos da linguagem
Python. No entanto, quando se trata de computação numérica intensiva – como cálculos
com grandes conjuntos de números, operações de álgebra linear, etc. – usar apenas as listas
e os operadores básicos do Python pode se tornar tanto verboso quanto computacionalmente
ineficiente.
É aqui que entra o NumPy, uma das bibliotecas mais fundamentais e poderosas do
ecossistema Python para computação científica e análise de dados.
Vamos aprender algumas bibliotecas úteis para a Engenharia. Mas tenha em mente
que se trata de uma mera introdução. Esses conceitos podem ser aprofundados através de
livros específicos para cada uma dessas bibliotecas. Mas o conhecimento aqui não será em
vão. Além de você aprender o básico delas, terá adquirido conhecimento suficiente pra
saber onde procurar caso precise de algo mais avançado.

18.1 Introdução ao NumPy: Computação Numérica


Eficiente com Arrays
O que é NumPy?
NumPy (uma contração de Numerical Python) é uma biblioteca de código aberto para
Python que adiciona suporte para arrays multidimensionais grandes e eficientes e
uma vasta coleção de funções matemáticas de alto nível para operar nesses arrays.
Ele é o pilar sobre o qual muitas outras bibliotecas científicas e de análise de dados em
Python são construídas.

Por que Usar NumPy em Vez de Listas Python?


As razões são principalmente relacionadas à eficiência e à conveniência para cálculos
numéricos:
1. Performance Superior:
• Arrays Homogêneos e Compactos: Arrays NumPy (ndarray) armazenam
dados do mesmo tipo de forma compacta.
• Operações Vetorizadas Implementadas em C: Muitas operações são exe-
cutadas como laços otimizados em C (vetorização), resultando em performance
drasticamente superior.

329
330 Capítulo 18. NumPy para Computação Numérica

2. Conveniência para Matemática e Álgebra Linear:

• Sintaxe mais natural e concisa para operações em arrays e matrizes.


• Inclui muitas funções matemáticas prontas e funcionalidades de álgebra linear.

3. Menor Uso de Memória: Geralmente consomem menos memória do que listas


Python equivalentes para dados numéricos.

O Objeto Fundamental: ndarray (Array N-Dimensional)


O coração do NumPy é seu objeto ndarray. É uma grade de valores, todos do mesmo
tipo, indexados por uma tupla de inteiros não negativos. O número de dimensões é o
posto (rank ) do array; a forma (shape) é uma tupla com o tamanho de cada dimensão.

• Um array de posto 1 é um vetor.

• Um array de posto 2 é uma matriz.

NumPy como Base do Ecossistema Científico Python


NumPy serve como fundação para bibliotecas como SciPy, Pandas, Matplotlib e Scikit-
learn. Aprender NumPy é um passo fundamental para usar Python em computação
numérica, científica ou análise de dados.

Instalação do NumPy (Breve Menção)


Se você usa Anaconda, o NumPy provavelmente já está instalado. Caso contrário, pode
instalar via pip no seu terminal:

pip install numpy

Após a instalação, a convenção para importá-lo em seus scripts é:


1 import numpy as np

Código 18.1: Importando NumPy com o alias convencional

Quase todos os exemplos usarão np para se referir ao NumPy.


Neste capítulo, vamos explorar como criar arrays NumPy, realizar operações básicas,
executar cálculos vetorizados e algumas funções comuns.

18.2 Criando Arrays NumPy (ndarray)


Após importar o NumPy (geralmente com import numpy as np), o primeiro passo
para usá-lo é criar seus arrays ndarray. Existem várias maneiras de fazer isso.
18.2. Criando Arrays NumPy (ndarray) 331

18.2.1 Criando Arrays a Partir de Sequências Python Existentes


A forma mais fundamental é com [Link](objeto_sequencia, dtype=None).

• Arrays Unidimensionais (Vetores):


1 import numpy as np # Importando o NumPy
2
3 l i s t a _ py t h o n _ s i m p l e s = [10 , 20 , 30 , 40 , 50]
4 v e t o r _ n u m p y _ a _ p a r t i r _ l i s t a = np . array ( l i s t a _ p yt h o n _ s i m p l e s )
5 print ( f " Vetor NumPy : { v e t o r _ n u m p y _ a _ p a r t i r _ l i s t a } " )
6 print ( f " Tipo do objeto :
{ type ( v e t o r _ n u m p y _ a _ p a r t i r _ l i s t a ) } " )
7 print ( f " Tipo de dados ( dtype ) :
{ v e t o r _ n u m p y _ a _ p a r t i r _ l i s t a . dtype } " )

Código 18.2: Criando vetor NumPy a partir de lista Python

• Arrays Bidimensionais (Matrizes): A partir de uma lista de listas.


1 dados_matriz = [[1 , 2 , 3] , [4 , 5 , 6] , [7 , 8 , 9]]
2 matriz_numpy = np . array ( dados_matriz )
3 print ( f " \ nMatriz NumPy 3 x3 :\ n { matriz_numpy } " )

Código 18.3: Criando matriz NumPy a partir de lista de listas

• Especificando o Tipo de Dado (dtype):


1 vetor _inicial _int = [1 , 2 , 3 , 4]
2 a r r a y _ f l o a t _ e x p l i c i t o = np . array ( vetor_inicial_int ,
dtype = np . float64 )
3 print ( f " \ nArray com dtype float64 :
{ array_float_explicito }")
4 print ( f " dtype : { a r r a y _ f l o a t _ e x p l i c i t o . dtype } " )
5
6 array_booleano = np . array ([0 , 1 , 0 , 2] , dtype = bool ) # 0 é
False , n ão - zero é True
7 print ( f " Array booleano : { array_booleano } " )

Código 18.4: Especificando dtype ao criar array NumPy

18.2.2 Criando Arrays com Valores Específicos


Funções para criar arrays preenchidos:

• [Link](shape, dtype=float): Array de zeros.

• [Link](shape, dtype=float): Array de uns.

• [Link](shape, valor, dtype=None): Array preenchido com valor.

• [Link](shape, dtype=float): Array não inicializado (valores “lixo”).


332 Capítulo 18. NumPy para Computação Numérica

1 # Supondo ’ import numpy as np ’ j á feito


2 vetor_de_zeros = np . zeros (5)
3 print ( f " \ nVetor de zeros : { vetor_de_zeros } " )
4
5 matriz_de _uns_int = np . ones ((3 , 2) , dtype = int )
6 print ( f " Matriz de uns ( inteiros ) :\ n { matriz_d e_uns_in t } " )
7
8 m a t r i z _ p r e e n c h i d a _ c o m _ s e t e = np . full ((2 , 3) , 7)
9 print ( f " Matriz preenchida com 7:\ n { m a t r i z _ p r e e n c h i d a _ c o m _ s e t e } " )
10
11 a r r a y _ s e m _ i n i c i a l i z a r = np . empty ((2 , 2) )
12 print ( f " Array ’ vazio ’ ( n ã o inicializado ) :\ n { a r r a y _ s e m _ i n i c i a l i z a r } " )

Código 18.5: Criando arrays com zeros, ones, full, empty

18.2.3 Criando Arrays com Sequências Numéricas


• [Link](inicio, fim, passo, dtype=None): Similar ao range() do Python,
mas retorna um ndarray e aceita passo float.

• [Link](inicio, fim, num_pontos, endpoint=True, dtype=None): Array


com num_pontos espaçados uniformemente. endpoint=True inclui o fim.

1 # Supondo ’ import numpy as np ’


2 array_de_0_a_9 = np . arange (10)
3 print ( f " \ nnp . arange (10) : { array_de_0_a_9 } " )
4
5 a r r a y _ c o m _ p a s s o _ f l o a t = np . arange (0.0 , 2.0 , 0.5)
6 print ( f " np . arange (0.0 , 2.0 , 0.5) : { a r r a y _ c o m _ p a s s o _ f l o a t } " )
7
8 po nt os_ en tr e_0 _e _1 = np . linspace (0 , 1 , 5)
9 print ( f " np . linspace (0 , 1 , 5) : { p ont os _e ntr e_ 0_ e_1 } " )

Código 18.6: Criando arrays com arange e linspace

18.2.4 Criando Arrays Especiais


• [Link](N, M=None, k=0, dtype=float): Matriz identidade NxM (ou NxN).

• [Link](v, k=0): Cria matriz diagonal a partir de um vetor v, ou extrai a diagonal


de uma matriz v.

1 # Supondo ’ import numpy as np ’


2 m a t r i z _ i d e n t i d a d e _ 3 x 3 = np . eye (3)
3 print ( f " \ nMatriz identidade 3 x3 :\ n { m a t r i z _ i d e n t i d a d e _ 3 x 3 } " )
4
5 m a tr iz_ c om _ d ia g on a l = np . diag ([10 , 20 , 30])
6 print ( f " Matriz com diagonal [10 , 20 , 30]:\ n { ma t ri z _ co m _d i ag o n al } " )

Código 18.7: Criando matriz identidade e diagonal com NumPy


18.3. Atributos Fundamentais de um ndarray 333

18.2.5 Criando Arrays com Valores Aleatórios ([Link])


O submódulo random do NumPy é usado para gerar arrays com números aleatórios.
• [Link](d0, ..., dn): Valores de distribuição uniforme [0.0, 1.0).

• [Link](d0, ..., dn): Valores de distribuição normal padrão (Gaussi-


ana).

• [Link](baixo, alto, size, dtype=int): Inteiros aleatórios no in-


tervalo [baixo, alto).

1 # Supondo ’ import numpy as np ’


2 # Matriz 2 x3 com valores aleat ó rios uniformes entre 0 e 1
3 m a t r i z _ a l e a t o r i a _ u n i f o r m e = np . random . rand (2 , 3)
4 print ( f " \ nMatriz aleat ó ria ( uniforme
0 -1) :\ n { m a t r i z _ a l e a t o r i a _ u n i f o r m e } " )
5
6 # Vetor com 5 amostras de uma distribui ç ã o normal padr ã o
7 v e t o r _ a l e a t o r i o _ n o r m a l = np . random . randn (5)
8 print ( f " Vetor aleat ó rio ( normal padr ã o ) :\ n { v e t o r _ a l e a t o r i o _ n o r m a l } " )
9

10 # Matriz 3 x4 de inteiros aleat ó rios entre 1 ( inclusivo ) e 10


( exclusivo )
11 m a t r i z _ i n t e i r o s _ a l e a t o r i o s = np . random . randint (1 , 10 , size =(3 , 4) )
12 print ( f " Matriz de inteiros aleat ó rios (1 a
9) :\ n { m a t r i z _ i n t e i r o s _ a l e a t o r i o s } " )

Código 18.8: Criando arrays com números aleatórios usando NumPy


Estas são as formas mais fundamentais de criar arrays NumPy, cobrindo uma grande
variedade de necessidades iniciais.

18.3 Atributos Fundamentais de um ndarray


Uma vez que um array NumPy (ndarray) é criado, ele possui diversos atributos que
fornecem informações importantes sobre sua estrutura e os dados que contém. Esses
atributos são acessados usando a notação de ponto (meu_array.nome_do_atributo) e
não são métodos (não usam parênteses ()).

1. [Link] (Número de Dimensões / Rank)


Retorna um inteiro indicando o número de eixos (dimensões) do array.
1 import numpy as np
2
3 vetor = np . array ([1 , 2 , 3])
4 matriz = np . array ([[1 , 2] , [3 , 4]])
5 print ( f " Vetor ndim : { vetor . ndim } " ) # Sa í da : 1
6 print ( f " Matriz ndim : { matriz . ndim } " ) # Sa í da : 2

Código 18.9: Atributo ndim de arrays NumPy

2. [Link] (Forma do Array)


Retorna uma tupla de inteiros indicando o tamanho do array ao longo de cada
dimensão. Para um vetor com n elementos, é (n,). Para uma matriz mxn, é (m, n).
334 Capítulo 18. NumPy para Computação Numérica

1 # Usando ’ vetor ’ e ’ matriz ’ do exemplo anterior


2 print ( f " Shape do vetor : { vetor . shape } " ) # Sa í da : (3 ,)
3 print ( f " Shape da matriz : { matriz . shape } " ) # Sa í da : (2 , 2)

Código 18.10: Atributo shape de arrays NumPy

3. [Link] (Número Total de Elementos)


Retorna um inteiro que é o número total de elementos no array (produto dos elementos
de shape).
1 # Usando ’ vetor ’ e ’ matriz ’
2 print ( f " Size do vetor : { vetor . size } " ) # Sa í da : 3
3 print ( f " Size da matriz : { matriz . size } " ) # Sa í da : 4

Código 18.11: Atributo size de arrays NumPy

4. [Link] (Tipo de Dado dos Elementos)


Retorna um objeto que descreve o tipo de dado dos elementos (ex: int64, float32,
bool). Todos os elementos em um ndarray são do mesmo tipo.
1 array_int = np . array ([1 , 5 , 10])
2 array_float = np . array ([1.0 , 2.5] , dtype = np . float32 )
3 print ( f " dtype de array_int : { array_int . dtype } " )
4 print ( f " dtype de array_float : { array_float . dtype } " )

Código 18.12: Atributo dtype de arrays NumPy

5. [Link] (Tamanho de Cada Elemento em Bytes)


Retorna o tamanho em bytes de um único elemento do array (depende do dtype).
1 # Usando array_int e array_float dos exemplos anteriores
2 print ( f " itemsize de array_int ({ array_int . dtype }) :
{ array_int . itemsize } bytes " )
3 print ( f " itemsize de array_float ({ array_float . dtype }) :
{ array_float . itemsize } bytes " )

Código 18.13: Atributo itemsize de arrays NumPy

6. [Link] (Tamanho Total do Array em Bytes na Memória)


Retorna o total de bytes consumidos pelos dados do array.
1 # Usando array_float ( size =2 , itemsize =4 para float32 )
2 print ( f " nbytes de array_float : { array_float . nbytes } bytes " )
3 print ( f " Verifica ç ã o : { array_float . size *
array_float . itemsize } " )

Código 18.14: Atributo nbytes de arrays NumPy

Exemplo Consolidado: Inspecionando um Array


1 # Criando um array bidimensional ( matriz )
2 matriz_inspecao = np . array (
3 [[10 , 11 , 12] , [20 , 21 , 22]] ,
4 dtype = np . int16 # Inteiros de 16 bits (2 bytes )
5 )
18.4. Indexação e Fatiamento (Slicing) de Arrays NumPy 335

6
7 print ( " \n - - - Atributos da Matriz ’ matriz_inspecao ’ ---" )
8 print ( f " A Matriz :\ n { matriz_inspecao } " )
9 print ( f " ndim ( dimens õ es ) : { matriz_inspecao . ndim } " )
10 print ( f " shape ( forma - linhas , colunas ) : { matriz_inspecao . shape } " )
11 print ( f " size ( total de elementos ) : { matriz_inspecao . size } " )
12 print ( f " dtype ( tipo dos elementos ) : { matriz_inspecao . dtype } " )
13 print ( f " itemsize ( bytes por elemento ) : { matriz_inspecao . itemsize } " )
14 print ( f " nbytes ( total de bytes dos dados ) :
{ matriz_inspecao . nbytes } " )
15 print ( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " )

Código 18.15: Inspecionando todos os atributos fundamentais de um array


Conhecer esses atributos é essencial para entender a estrutura dos seus arrays NumPy, o
que é fundamental para realizar operações de manipulação de dados que veremos a seguir.

18.4 Indexação e Fatiamento (Slicing) de Arrays NumPy


Assim como listas Python, arrays NumPy (ndarray) permitem acessar elementos indi-
vidualmente (indexação) e subconjuntos (fatiamento). NumPy estende essas capacidades,
especialmente para arrays multidimensionais, e introduz formas avançadas como indexação
booleana e “fancy indexing”.

18.4.1 Indexação e Fatiamento em Arrays Unidimensionais (1D)


Para vetores NumPy, funciona de forma muito similar às listas:

• Indexação: Baseada em zero, com índices positivos e negativos.

• Fatiamento: Sintaxe vetor[inicio:fim:passo].

1 import numpy as np
2
3 vetor_a = np . array ([10 , 20 , 30 , 40 , 50 , 60])
4
5 print ( f " vetor_a [0]: { vetor_a [0]} , vetor_a [ -1]: { vetor_a [ -1]} " ) #
Indexa ç ã o
6

7 fatia1 = vetor_a [1:4] # Elementos dos í ndices 1 , 2 , 3


8 print ( f " Fatia vetor_a [1:4]: { fatia1 } " )
9
10 vetor_invertido = vetor_a [:: -1] # Inverte o vetor
11 print ( f " Vetor invertido : { vetor_invertido } " )

Código 18.16: Indexação e Fatiamento em Vetor NumPy


Importante: Fatias de Arrays NumPy são “Visões” (Views)!
Diferente de listas Python, fatias de arrays NumPy geralmente são visões da memória do
array original. Modificar uma fatia-visão modifica o array original. Para uma cópia
independente, use .copy() sobre a fatia: fatia_copia = vetor_a[1:4].copy().
1 array_original = np . arange (5) # [0 1 2 3 4]
2 fatia_visao = array_original [1:4]
3 print ( f " Original : { array_original } , Vis ã o : { fatia_visao } " )
336 Capítulo 18. NumPy para Computação Numérica

4
5 fatia_visao [0] = 99 # Modifica a vis ã o E o original
6 print ( f " Original ap ó s modificar vis ã o : { array_original } " ) # Ser á [
0 99 2 3 4]
7
8 fatia_copia = array_original [1:4]. copy ()
9 fatia_copia [0] = 777 # Modifica apenas a c ó pia
10 print ( f " Original ap ó s modificar c ó pia : { array_original } " ) #
Permanece [ 0 99 2 3 4]
11 print ( f " C ó pia modificada : { fatia_copia } " ) # Ser á [777 2 3]

Código 18.17: Fatias NumPy como visões vs. cópias

18.4.2 Indexação e Fatiamento em Arrays Bidimensionais (2D -


Matrizes)
Para matrizes, usamos uma tupla de índices [linha, coluna] ou fatias para cada
dimensão.

• Acesso a Elementos Individuais: matriz[linha, coluna] (preferido) ou


matriz[linha][coluna].

• Fatiamento de Linhas: matriz[indice_linha, :] (ou matriz[indice_linha])


para uma linha; matriz[inicio_l:fim_l, :] para múltiplas linhas.

• Fatiamento de Colunas: matriz[:, indice_coluna] para uma coluna; matriz[:,


inicio_c:fim_c] para múltiplas colunas.

• Fatiamento de Blocos: matriz[inicio_l:fim_l, inicio_c:fim_c].

1 matriz_b = np . array ([[10 , 20 , 30] , [40 , 50 , 60] , [70 , 80 , 90]])


2 print ( f " Matriz B :\ n { matriz_b } " )
3
4 # Elemento na linha 0 , coluna 1
5 el_0_1 = matriz_b [0 , 1]
6 print ( f " Elemento [0 , 1]: { el_0_1 } " ) # Sa í da : 20
7
8 # Segunda linha completa ( í ndice 1)
9 linha_1 = matriz_b [1 , :] # ou matriz_b [1]
10 print ( f " Segunda linha : { linha_1 } " ) # Sa í da : [40 50 60]
11
12 # Terceira coluna completa ( í ndice 2)
13 coluna_2 = matriz_b [: , 2]
14 print ( f " Terceira coluna : { coluna_2 } " ) # Sa í da : [30 60 90]
15
16 # Submatriz 2 x2 do canto superior esquerdo
17 sub_2x2 = matriz_b [:2 , :2]
18 print ( f " Submatriz 2 x2 superior esquerda :\ n { sub_2x2 } " )

Código 18.18: Indexação e Fatiamento em Matrizes NumPy

Fatias 2D também são visões. A lógica se estende para arrays com mais dimensões.
18.4. Indexação e Fatiamento (Slicing) de Arrays NumPy 337

18.4.3 Indexação Booleana (Mascaramento)


Permite selecionar elementos com base em uma condição. Cria-se um array booleano
(máscara) e usa-se como índice. Geralmente retorna uma cópia.
1 dados = np . array ([5 , 12 , 8 , 25 , 3 , 17 , 9 , 20])
2 print ( f " Dados originais : { dados } " )
3
4 # Elementos maiores que 10
5 mascara_gt10 = dados > 10
6 print ( f " M á scara ( dados > 10) : { mascara_gt10 } " )
7 filtrados_gt10 = dados [ mascara_gt10 ]
8 print ( f " Elementos > 10: { filtrados_gt10 } " )
9
10 # Elementos > 5 E < 20 ( use & para ’ and ’, | para ’ or ’, ~ para ’ not ’
com arrays )
11 f i l t r a d o s _ e n t r e _ 5 _ e _ 2 0 = dados [( dados > 5) & ( dados < 20) ]
12 print ( f " Elementos > 5 E < 20: { f i l t r a d o s _ e n t r e _ 5 _ e _ 2 0 } " )

Código 18.19: Indexação Booleana em NumPy

18.4.4 Indexação com Array de Inteiros (Fancy Indexing)


Permite selecionar elementos usando outro array (ou lista) de índices inteiros. O array
resultante tem a forma do array de índices. Geralmente retorna uma cópia.

• Arrays 1D: vetor[[idx1, idx2, idx3]]

• Arrays 2D: matriz[[lin1, lin2], [col_a, col_b]] seleciona (lin1,col_a) e


(lin2,col_b). matriz[[lin1, lin3], :] seleciona linhas inteiras 1 e 3.

1 vetor_fancy = np . array ([ ’w ’ , ’x ’ , ’y ’ , ’z ’ ])
2 indices = np . array ([0 , 3 , 1 , 1])
3 print ( f " \ nSele ç ã o fancy no vetor : { vetor_fancy [ indices ]} " ) # Sa í da :
[ ’ w ’ ’z ’ ’x ’ ’x ’]
4
5 matriz_fancy = np . arange (10 , 19) . reshape ((3 , 3) ) # Matriz 3 x3 de 10
a 18
6 print ( f " Matriz para fancy indexing :\ n { matriz_fancy } " )
7 # Selecionando elementos (0 ,0) , (1 ,2) , (2 ,1)
8 elementos_sel = matriz_fancy [[0 , 1 , 2] , [0 , 2 , 1]]
9 print ( f " Elementos (0 ,0) , (1 ,2) , (2 ,1) : { elementos_sel } " )
10
11 # Selecionando linhas 2 e 0
12 linhas_sel = matriz_fancy [[2 , 0]]
13 print ( f " Linhas 2 e 0 selecionadas :\ n { linhas_sel } " )

Código 18.20: Fancy Indexing em NumPy

Relembrando: Fatias são Visões, Outras Indexações Podem Ser


Cópias
• Fatiamento padrão (:) geralmente cria visões. Modificar a visão modifica o original.
Use .copy() para independência.
338 Capítulo 18. NumPy para Computação Numérica

• Indexação Booleana e Fancy Indexing geralmente criam cópias.

A indexação e o fatiamento em NumPy são ferramentas ricas e otimizadas, fundamentais


para qualquer manipulação de dados.

18.5 Operações com Arrays: Vetorização e Broadcas-


ting
Uma das principais razões para usar NumPy é sua capacidade de realizar operações em
arrays inteiros de forma eficiente e concisa, através da vetorização e do broadcasting.

18.5.1 Operações Aritméticas Elemento a Elemento (Vetorização)


Quando você realiza operações aritméticas (+, -, *, /, **) entre arrays NumPy de mesma
forma, ou entre um array e um escalar, elas são aplicadas elemento a elemento. Esse
comportamento é chamado de vetorização e é implementado em C para alta performance.

• Operações entre dois arrays (de mesma forma):


1 import numpy as np
2
3 array_a = np . array ([10 , 20 , 30 , 40])
4 array_b = np . array ([2 , 5 , 3 , 8])
5

6 soma_arrays = array_a + array_b # Adi ç ã o elemento a


elemento
7 print ( f " A : { array_a } , B : { array_b } " )
8 print ( f " A + B : { soma_arrays } " ) # Sa í da : [12 25 33 48]
9
10 produto_arrays = array_a * array_b # Multiplica ç ã o
elemento a elemento
11 print ( f " A * B : { produto_arrays } " ) # Sa í da : [ 20 100 90
320]

Código 18.21: Operações aritméticas vetorizadas entre arrays NumPy

• Operações entre um array e um escalar:


A operação com o escalar é aplicada a cada elemento do array.
1 # Usando array_a = np . array ([10 , 20 , 30 , 40])
2 array_a = np . array ([10 , 20 , 30 , 40]) # Redefinindo para o
exemplo isolado
3

4 array_mais_5 = array_a + 5
5 print ( f " \ nArray A + 5: { array_mais_5 } " ) # Sa í da : [15 25 35
45]
6
7 array_vezes_3 = array_a * 3
8 print ( f " Array A * 3: { array_vezes_3 } " ) # Sa í da : [ 30 60
90 120]

Código 18.22: Operações vetorizadas entre array e escalar


18.5. Operações com Arrays: Vetorização e Broadcasting 339

18.5.2 Funções Universais (ufuncs)


NumPy fornece funções universais (ufuncs) que operam em arrays ndarray elemento
a elemento. Muitas operações aritméticas são atalhos para ufuncs (ex: + para [Link]()).
Elas incluem funções matemáticas, trigonométricas, lógicas, etc.
1 # Supondo ’ import numpy as np ’
2 array_ang ulos_rad = np . array ([0 , np . pi /2 , np . pi ]) # np . pi é a
constante pi
3
4 senos_dos _angulos = np . sin ( arra y_angulo s_rad ) # np . sin () é uma ufunc
5 print ( f " \ nSeno dos â ngulos
{ array_a ngulos_ra d }:\ n { senos_d os_angulo s } " )
6
7 numeros_p ara_raiz = np . array ([4.0 , 9.0 , 16.0])
8 raizes_quadradas = np . sqrt ( nume ros_para _raiz ) # np . sqrt () é uma
ufunc
9 print ( f " Ra í zes quadradas de { numer os_para_r aiz }:
{ raizes_quadradas } " )

Código 18.23: Exemplos de funções universais (ufuncs) do NumPy

18.5.3 Broadcasting: Operando com Arrays de Formas Diferentes


O broadcasting descreve como NumPy trata operações entre arrays de formas (shapes)
diferentes, mas compatíveis. Se certas regras forem atendidas, o array menor é “esticado”
(broadcasted) para que sua forma se torne compatível com a do array maior, permitindo
que a operação elemento a elemento ocorra sem criar cópias desnecessárias dos dados.
Regras do Broadcasting (Simplificadas): NumPy compara as formas dos arrays
dimensão por dimensão (da direita para a esquerda). Duas dimensões são compatíveis se
são iguais ou se uma delas é 1. Se as formas não têm o mesmo número de dimensões, a
forma do array menor é preenchida com 1s à esquerda.
Exemplos de Broadcasting:

• Array + Escalar: O escalar é “esticado”.


1 matriz_m = np . array ([[1 , 2 , 3] , [4 , 5 , 6]])
2 re s ul t ad o _ m_ m ai s _ 10 = matriz_m + 10
3 print ( f " \ nMatriz M :\ n { matriz_m } " )
4 print ( f " Matriz M + 10:\ n { r es u l ta d o_ m _ ma i s_ 1 0 } " )

Código 18.24: Broadcasting de um escalar com uma matriz

• Array 2D (Matriz) + Array 1D (Vetor):


1 matriz_dados = np . array ([[10 , 20 , 30] ,
2 [40 , 50 , 60] ,
3 [70 , 80 , 90]])
4 vetor_linha_soma = np . array ([1 , 2 , 3])
5
6 # Somando o vetor_linha_soma a cada linha da matriz_dados
7 r e s u l t a d o _ s o m a _ b r o a d c a s t = matriz_dados + vetor_linha_soma
8 print ( f " \ nMatriz Dados :\ n { matriz_dados } " )
9 print ( f " Vetor Linha Soma : { vetor_linha_soma } " )
10 print ( f " Matriz + Vetor Linha :\ n { r e s u l t a d o _ s o m a _ b r o a d c a s t } " )
11
340 Capítulo 18. NumPy para Computação Numérica

12 # Somando um vetor coluna ( precisa ter shape (N ,1) )


13 vetor _coluna_s oma = np . array ([[100] , [200] , [300]]) #
Vetor 3 x1
14 r e s u l t a d o _ c o l u n a _ b r o a d c a s t = matriz_dados +
vetor _coluna_ soma
15 print ( f " \ nVetor Coluna Soma :\ n { ve tor_colu na_soma } " )
16 print ( f " Matriz + Vetor
Coluna :\ n { r e s u l t a d o _ c o l u n a _ b r o a d c a s t } " )

Código 18.25: Broadcasting de um vetor linha com uma matriz

A combinação de vetorização, ufuncs e broadcasting torna o NumPy extremamente eficaz


para computação numérica, resultando em código mais rápido, curto e legível.

18.6 Funções Matemáticas e Estatísticas Básicas com


NumPy
O NumPy não é apenas sobre operações elemento a elemento; ele oferece um rico
conjunto de funções para realizar cálculos agregados sobre arrays, como somas, mé-
dias, e outras estatísticas descritivas. Muitas dessas funções podem ser chamadas como
[Link](array) ou como um método do array: [Link]().

Funções Agregadas Comuns


• [Link](array, axis=None) ou [Link](axis=None):
Soma todos os elementos. Com axis, soma ao longo do eixo especificado (0 para
colunas, 1 para linhas em arrays 2D).
1 import numpy as np
2
3 matriz_vendas = np . array ([[10 , 15 , 20] , [12 , 18 , 22] , [8 ,
10 , 15]])
4 print ( f " Matriz de Vendas :\ n { matriz_vendas } " )
5
6 t ot al_ ge ra l_v en da s = np . sum ( matriz_vendas )
7 print ( f " Soma total : { t ota l_ ge ral _v en das } " )
8
9 total _por_pro duto = np . sum ( matriz_vendas , axis =0) # Soma
das colunas
10 print ( f " Total por produto ( colunas ) : { total_ por_produ to } " )
11
12 total_por_loja = np . sum ( matriz_vendas , axis =1) # Soma das
linhas
13 print ( f " Total por loja ( linhas ) : { total_por_loja } " )

Código 18.26: Usando [Link]() com e sem eixo

• [Link](array, axis=None) e [Link](array, axis=None):


Encontram o valor mínimo e máximo, respectivamente.
1 # Usando matriz_vendas do exemplo anterior
2 v en da_ mi ni ma_ ge ra l = np . min ( matriz_vendas )
3 print ( f " \ nVenda m í nima geral : { ven da _m ini ma _g era l } " )
4 m e n o r _ ve n d a _ p o r _ l o j a = matriz_vendas . min ( axis =1)
18.6. Funções Matemáticas e Estatísticas Básicas com NumPy 341

5 print ( f " Menor venda por loja : { m e n o r _ v e n d a_ p o r _ l o j a } " )

Código 18.27: Usando [Link]() e [Link]()

• [Link](array, axis=None), [Link](array, axis=None),


[Link](array, axis=None):
Calculam a média aritmética, o desvio padrão e a variância.
1 temperaturas_dia = np . array ([22.5 , 23.1 , 24.0 , 23.5 , 22.8 ,
21.9 , 22.2])
2 print ( f " \ nTemperaturas : { temperaturas_dia } " )
3 m ed ia_ te mp era tu ra s = np . mean ( temperaturas_dia )
4 print ( f " M é dia : { m ed ia _te mp er atu ra s :.2 f } C " )
5 d es vio _p ad rao _t em p = np . std ( temperaturas_dia )
6 print ( f " Desvio Padr ã o : { de sv io _pa dr ao _te mp :.2 f } C " )

Código 18.28: Média, desvio padrão e variância com NumPy

• [Link](array, axis=None) e [Link](array, q, axis=None):


Calculam a mediana (valor central) e o q-ésimo percentil.
1 dados_salarios = np . array ([2000 , 2200 , 2100 , 2500 , 2300 ,
3000 , 2000 , 15000 , 2400])
2 print ( f " \ nSal á rios : { dados_salarios } " )
3 mediana_salarios = np . median ( dados_salarios )
4 print ( f " Mediana dos sal á rios : R$ { mediana_salarios :.2 f } " )
5 quartis_salarios = np . percentile ( dados_salarios , [25 , 50 ,
75])
6 print ( f " Quartis (25% , 50% , 75%) : R$ { quartis_salarios } " )

Código 18.29: Mediana e percentis com NumPy

• [Link](array, axis=None) e [Link](array, axis=None):


Retornam o índice do valor mínimo e máximo, respectivamente.
1 # Usando temperaturas_dia
2 temperaturas_dia = np . array ([22.5 , 23.1 , 24.0 , 23.5 , 22.8 ,
21.9 , 22.2])
3 i nd ice _t em p_m in im a = np . argmin ( temperaturas_dia )
4 print ( f " \ n Í ndice da temp . m í nima : { i nd ice _t em p_m in im a }
( valor : { temperaturas_dia [ in dic e_ te mp_ mi ni ma ]}) " )

Código 18.30: Encontrando índices de mínimo e máximo com argmin/argmax

Funções Cumulativas
NumPy também oferece funções para cálculos cumulativos:

• [Link](array, axis=None): Soma cumulativa.

• [Link](array, axis=None): Produto cumulativo.

1 v a lo res _ se q u en c ia i s = np . array ([1 , 2 , 3 , 4 , 5])


2 print ( f " \ nValores : { v a lo r es _ se q u en c ia i s } " )
3 s o m a _ c u m u l a t i v a _ v a l o r e s = np . cumsum ( v a lo r e s_ s eq u en c i ai s )
342 Capítulo 18. NumPy para Computação Numérica

4 print ( f " Soma cumulativa : { s o m a _ c u m u l a t i v a _ v a l o r e s } " ) # Sa í da : [ 1


3 6 10 15]
5 p r o d u t o _ c u m u l a t i v o _ v a l o r e s = np . cumprod ( v a lo r es _ se q u en c ia i s )
6 print ( f " Produto cumulativo : { p r o d u t o _ c u m u l a t i v o _ v a l o r e s } " ) # Sa í da :
[ 1 2 6 24 120]

Código 18.31: Soma e produto cumulativos com NumPy


Estas funções são essenciais para resumir e analisar dados numéricos de forma eficiente. O
parâmetro axis é crucial para aplicá-las em arrays multidimensionais.

18.7 Álgebra Linear Básica com NumPy (Submódulo


[Link])
A Álgebra Linear é fundamental em engenharia e ciências. O NumPy oferece o submó-
dulo linalg ([Link]) com funções padrão para essas operações. Para funcionalidades
ainda mais avançadas, existe o [Link] (Capítulo ??).

Operações Fundamentais
• Produto Escalar e Multiplicação de Matrizes:
Use [Link](a, b) ou o operador a @ b (Python 3.5+).
– Para vetores 1D: calcula o produto escalar.
– Para matrizes 2D: realiza a multiplicação de matrizes.
1 import numpy as np
2
3 vetor1 = np . array ([1 , 2 , 3])
4 vetor2 = np . array ([4 , 0 , -1])
5 produto_escalar = np . dot ( vetor1 , vetor2 ) # ou vetor1 @
vetor2
6 print ( f " Produto escalar de { vetor1 } e { vetor2 }:
{ produto_escalar } " ) # Sa í da : 1
7

8 matriz_M1 = np . array ([[1 , 2] , [3 , 4]])


9 matriz_M2 = np . array ([[5 , 6] , [7 , 8]])
10 produto_matrizes = matriz_M1 @ matriz_M2
11 print ( f " \ nMatriz M1 :\ n { matriz_M1 }\ nMatriz
M2 :\ n { matriz_M2 } " )
12 print ( f " Produto M1 @ M2 :\ n { produto_matrizes } " )

Código 18.32: Produto escalar e multiplicação de matrizes com NumPy

Lembre-se que * entre arrays NumPy realiza multiplicação elemento a elemento.


• Transposta de uma Matriz:
Inverte linhas e colunas. Use o atributo .T ou a função [Link]().
1 matriz_original = np . array ([[10 , 20 , 30] , [40 , 50 , 60]])
2 print ( f " \ nMatriz Original :\ n { matriz_original } " )
3 m a t r i z _ t r a n s p o s t a _ r e s u l t a d o = matriz_original . T
4 print ( f " Matriz Transposta :\ n { m a t r i z _ t r a n s p o s t a _ r e s u l t a d o } " )

Código 18.33: Transposta de uma matriz com NumPy


18.7. Álgebra Linear Básica com NumPy (Submódulo [Link]) 343

Funções do Módulo [Link]


• Inversa de uma Matriz ([Link](matriz)):
Calcula A−1 de uma matriz quadrada A (deve ser não singular).
1 matri z_a_inve rter = np . array ([[4 , 7] , [2 , 6]])
2 print ( f " \ nMatriz a Inverter :\ n { ma triz_a_i nverter } " )
3 # Nota : A matriz deve ser invert í vel .
4 try :
5 matriz_inversa = np . linalg . inv ( matriz_ a_invert er )
6 print ( f " Inversa da Matriz :\ n { matriz_inversa } " )
7 identidade_calc = ma triz_a_i nverter @ matriz_inversa
8 print ( f " Verifica ç ã o ( A @
A_inv ) :\ n { np . round ( identidade_calc , decimals =5) } " )
9 except np . linalg . LinAlgError as e :
10 print ( f " Erro ao calcular inversa : { e } " )

Código 18.34: Inversa de uma matriz com [Link]

• Determinante de uma Matriz ([Link](matriz)):


Calcula o determinante de uma matriz quadrada.
1 # Usando ma triz_a_i nverter do exemplo anterior
2 matri z_a_inve rter = np . array ([[4 , 7] , [2 , 6]])
3 d e t e r m i n a n t e _ d a _ m a t r i z = np . linalg . det ( matri z_a_inve rter )
4 print ( f " \ nDeterminante da matriz :
{ d e t e r m i n a n t e _ d a _ m a t r i z :.2 f } " )

Código 18.35: Determinante de uma matriz com [Link]

• Solução de Sistemas Lineares ([Link](A, b)):


Resolve o sistema Ax = b para x. A matriz A deve ser quadrada e não singular.
1 # Sistema : 2* x1 + 3* x2 = 8; 4* x1 + x2 = 6
2 matriz_A_sistema = np . array ([[2 , 3] , [4 , 1]])
3 vetor_b_sistema = np . array ([8 , 6])
4 try :
5 solucao_x_vetor = np . linalg . solve ( matriz_A_sistema ,
vetor_b_sistema )
6 print ( f " \ nSolu ç ã o do sistema ( x1 , x2 ) :
{ solucao_x_vetor } " )
7 print ( f " Verifica ç ã o ( A @ x ) : { matriz_A_sistema @
solucao_x_vetor } " )
8 except np . linalg . LinAlgError as e :
9 print ( f " Erro ao resolver sistema : { e } " )

Código 18.36: Resolvendo sistema linear Ax = b com [Link]

• Autovalores e Autovetores ([Link](matriz)):


Calcula os autovalores (λ) e autovetores (v) de uma matriz quadrada (Av = λv).
Retorna uma tupla (autovalores, autovetores).
1 matriz_para_eig = np . array ([[4 , -2] , [1 , 1]])
2 autovalores_calc , autovetores_calc =
np . linalg . eig ( matriz_para_eig )
3
4 print ( f " \ nMatriz :\ n { matriz_para_eig } " )
5 print ( f " Autovalores : { autovalores_calc } " )
344 Capítulo 18. NumPy para Computação Numérica

6 print ( f " Autovetores ( colunas ) :\ n { autovetores_calc } " )

Código 18.37: Autovalores e autovetores com [Link]

• Produto Vetorial ([Link](a, b)):


Calcula o produto vetorial entre dois vetores (geralmente 3D).
1 vetor_p = np . array ([1 , 0 , 0]) # Eixo x
2 vetor_q = np . array ([0 , 1 , 0]) # Eixo y
3 pr o du t o_ v e to r ia l _ pq = np . cross ( vetor_p , vetor_q )
4 print ( f " \ nProduto vetorial de { vetor_p } e { vetor_q }:
{ p r od u to _ v et o ri a l _p q } " ) # Sa í da : [0 0 1]

Código 18.38: Produto vetorial com [Link]

O submódulo [Link] fornece ferramentas essenciais para álgebra linear, fundamen-


tais em muitas aplicações de engenharia e ciências.

18.8 Leitura e Escrita de Arrays NumPy em Arquivos


(Simples)
Após manipular dados com NumPy, frequentemente precisamos salvar nossos arrays
para uso posterior ou compartilhamento. NumPy oferece funções para formatos binários
específicos (.npy, .npz) e formatos de texto.

18.8.1 Salvando e Carregando no Formato Binário NumPy (.npy,


.npz)
Este formato é eficiente e preserva dtype e forma.

• [Link](’[Link]’, array): Salva um único array.

• array_carregado = [Link](’[Link]’): Carrega um array.

1 import numpy as np
2

3 array_original = np . arange (12 , dtype = float ) . reshape (3 , 4)


4 print ( f " Array original :\ n { array_original } " )
5
6 nome_arquivo_npy = " meu_array_numpy . npy "
7 np . save ( nome_arquivo_npy , array_original )
8 print ( f " \ nArray salvo em ’{ nome_arquivo_npy } ’. " )
9
10 array_lido_npy = np . load ( nome_arquivo_npy )
11 print ( f " Array carregado de ’{ nome_arquivo_npy } ’:\ n { array_lido_npy } " )

Código 18.39: Salvando e carregando um array NumPy com .npy

• [Link](’[Link]’, nome1=arr1, ...): Salva múltiplos arrays em um ar-


quivo .npz.

• np.savez_compressed(’[Link]’, ...): Similar, mas com compressão.


18.8. Leitura e Escrita de Arrays NumPy em Arquivos (Simples) 345

• Carregar de .npz: dados = [Link](’[Link]’), depois acesse com


dados[’nome1’]. Lembre-se de [Link]().

1 vetor_a = np . array ([1 , 2 , 3])


2 matriz_b = np . array ([[10 , 20] , [30 , 40]])
3 nome_arquivo_npz = " dados_multiplos . npz "
4
5 np . savez ( nome_arquivo_npz , v = vetor_a , m = matriz_b )
6 print ( f " \ nM ú ltiplos arrays salvos em ’{ nome_arquivo_npz } ’. " )
7
8 dados_lidos_npz = np . load ( nome_arquivo_npz )
9 print ( f " Vetor ’v ’ carregado : { dados_lidos_npz [ ’ v ’]} " )
10 print ( f " Matriz ’m ’ carregada :\ n { dados_lidos_npz [ ’ m ’]} " )
11 dados_lidos_npz . close () # Boa pr á tica

Código 18.40: Salvando e carregando múltiplos arrays com .npz

18.8.2 Salvando e Carregando Arrays em Formato Texto


Para interoperabilidade e leitura humana.

• [Link](’[Link]’, array, fmt=’%.18e’, delimiter=’ ’, ...): Salva ar-


ray em texto.

– fmt: Formato dos números (ex: ’%d’, ’%.2f’).


– delimiter: Separador de colunas (ex: ’,’ para CSV).
– header, footer, comments.

1 m a t r i z _ p a r a _ s a l v a r _ t e x t o = np . array ([[1.1 , 2.22 , 3.333] ,


[4.4444 , 5.55555 , 6.0]])
2 n o m e _ a r q u i v o _ c s v _ n u m p y = " matriz_numpy . csv "
3
4 np . savetxt ( nome_arquivo_csv_numpy ,
matriz_para_salvar_texto ,
5 fmt = ’ %.3 f ’ , delimiter = ’; ’ , header = ’ Col1 ; Col2 ; Col3 ’ ,
comments = ’ ’)
6 print ( f " \ nMatriz salva em ’{ n o m e _ a r q u i v o _ c s v _ n u m p y } ’. " )

Código 18.41: Salvando array NumPy como arquivo de texto (CSV simples)

• [Link](’[Link]’, dtype=float, comments=’#’, delimiter=None, ...):


Carrega dados de um arquivo de texto.

– dtype: Tipo de dado resultante.


– comments: Caractere de comentário.
– delimiter: Separador de colunas.
– skiprows: Pular N linhas do início.
– usecols: Tupla de índices de colunas a serem lidas.
– encoding: (ex: ’utf-8’).
346 Capítulo 18. NumPy para Computação Numérica

1 # Supondo que ’ matriz_numpy . csv ’ foi criado pelo exemplo


anterior
2 try :
3 matriz_lida_csv = np . loadtxt ( nome_arquivo_csv_numpy ,
4 dtype = float , delimiter = ’; ’ ,
5 skiprows =1 , encoding = " utf -8 " ) # Pula cabe ç alho
6 print ( f " Matriz carregada de
’{ n o m e _ a r q u i v o _ c s v _ n u m p y } ’:\ n { matriz_lida_csv } " )
7 except Exception as e :
8 print ( f " Erro ao carregar ’{ n o m e _ a r q u i v o _ c s v _ n u m p y } ’:
{e}")

Código 18.42: Carregando array NumPy de arquivo de texto

Texto vs. Binário NumPy: Binário (.npy) é eficiente e preserva tipos/forma para uso
em Python/NumPy. Texto é legível e interoperável, mas pode perder precisão e ser mais
lento.

Concluindo o Capítulo sobre NumPy


Neste capítulo, demos os primeiros passos no poderoso mundo da computação numérica
com Python usando a biblioteca NumPy. Introduzimos seu objeto fundamental, o ndarray,
e suas vantagens em performance e conveniência.
Aprendemos a criar arrays de diversas formas, a inspecionar seus atributos, a realizar
operações de indexação e fatiamento (incluindo a diferença entre visões e cópias, indexação
booleana e fancy indexing). Exploramos a vetorização, as funções universais (ufuncs) e
o broadcasting, que tornam os cálculos com arrays eficientes e concisos. Vimos funções
matemáticas, estatísticas e de álgebra linear básica, e como salvar e carregar arrays.
O NumPy é a espinha dorsal da computação científica em Python e a base para muitas
outras bibliotecas essenciais como Pandas e Matplotlib, que veremos a seguir.
No próximo capítulo, aprenderemos como visualizar os dados com os quais estamos
trabalhando, usando a biblioteca Matplotlib.
Capítulo 19

Matplotlib e Seaborn para Visualização


de Dados

Nos capítulos anteriores, especialmente com o NumPy, aprendemos a manipular e


analisar dados numéricos de forma eficiente. No entanto, números e tabelas, por si sós,
nem sempre são a melhor maneira de entender padrões complexos, tendências ou relações
escondidas em nossos dados. Muitas vezes, uma representação visual pode transmitir
informações de forma muito mais clara, rápida e impactante. Como diz o ditado, “uma
imagem vale mais que mil palavras”.
É aqui que entra a visualização de dados. Transformar dados brutos em gráficos,
mapas ou outros formatos visuais nos permite explorar, entender e comunicar insights de
maneira eficaz.

19.1 Introdução à Visualização de Dados com Python


Por que Visualizar Dados?
A visualização de dados é uma ferramenta analítica e de comunicação poderosa com
diversos propósitos:
1. Exploração e Entendimento: Identificar padrões, tendências, correlações, distri-
buições e anomalias (outliers) nos dados.
2. Comunicação de Resultados: Apresentar descobertas de forma clara, concisa e
impactante para diferentes públicos.
3. Suporte à Tomada de Decisão: Auxiliar na tomada de decisões baseadas em
evidências.
4. Depuração de Modelos e Dados: Identificar problemas em algoritmos ou na
qualidade dos dados.
5. Engajamento: Gráficos são geralmente mais atraentes do que grandes blocos de
texto ou números.

Visualização de Dados com Python: Um Ecossistema Rico


Python se tornou uma ferramenta poderosa para visualização de dados devido ao seu
ecossistema de bibliotecas.

347
348 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

• Matplotlib: A biblioteca fundamental e mais utilizada, base para muitas outras.


Oferece grande controle e é ideal para gráficos estáticos de qualidade. Seu módulo
pyplot fornece uma interface conveniente.

• Seaborn: Construída sobre o Matplotlib, facilita a criação de gráficos estatísticos


atraentes e informativos com menos código. Integra-se bem com DataFrames do
Pandas.

• Outras Bibliotecas (Breve Menção): Plotly e Dash (gráficos interativos e


dashboards web), Bokeh (visualizações interativas para web), Altair (visualização
estatística declarativa), ggplot (plotnine) (inspirada no ggplot2 do R).

Neste capítulo, nosso foco principal será em Matplotlib e Seaborn.

O que Você Aprenderá Neste Capítulo


• Como criar os tipos mais comuns de gráficos (linha, barra, dispersão, histograma)
usando Matplotlib.

• Como personalizar seus gráficos Matplotlib com títulos, rótulos, legendas, cores, etc.

• Como trabalhar com múltiplos gráficos em uma mesma figura (subplots).

• Como salvar seus gráficos em arquivos.

• Como usar o Seaborn para criar visualizações estatísticas comuns.

• Dicas básicas para criar visualizações eficazes.

Preparando o Ambiente (Importações Comuns)


Ao longo deste capítulo, utilizaremos principalmente as seguintes importações. É uma
convenção comum usar apelidos (aliases):
1 # Importa ç õ es que usaremos frequentemente neste cap í tulo
2 import matplotlib . pyplot as plt # A interface pyplot do Matplotlib
3 import seaborn as sns # Seaborn para gr á ficos estat í sticos
4 import numpy as np # NumPy para criar dados de exemplo
5
6 # O Pandas ser á muito usado com Seaborn , mas o introduziremos no
Cap . 20
7 # import pandas as pd

Código 19.1: Importações comuns para visualização de dados


Com essas ferramentas em mãos, estamos prontos para começar a transformar dados em
conhecimento visual!

19.2 Matplotlib: A Biblioteca Fundamental para Gráfi-


cos
Quando se fala em criar gráficos em Python, a primeira biblioteca que geralmente vem
à mente é o Matplotlib. Ela é a biblioteca de plotagem 2D (e com algumas capacidades
19.2. Matplotlib: A Biblioteca Fundamental para Gráficos 349

3D) mais popular e fundamental no ecossistema Python. O Matplotlib é conhecido por


sua flexibilidade e pelo controle granular que oferece sobre todos os aspectos de um
gráfico, permitindo criar desde gráficos simples até figuras complexas com qualidade de
publicação.

19.2.1 Introdução ao Matplotlib e o Módulo pyplot


Dentro do Matplotlib, o módulo que mais utilizaremos para criar gráficos de forma
rápida é o [Link]. Ele fornece uma coleção de funções que fazem o Matplotlib
funcionar de maneira similar ao MATLAB.
Convenção de Importação: A convenção universal é usar o alias plt:
1 import matplotlib . pyplot as plt

Código 19.2: Importação padrão do pyplot


Pense no plt como seu “conjunto de pincéis e ferramentas” para desenhar gráficos.

Conceitos Fundamentais: Figura (Figure) e Eixos (Axes)


• Figura (Figure): É o contêiner de mais alto nível, a “tela” ou “janela” onde tudo é
desenhado. Uma figura pode conter um ou mais gráficos (eixos).
• Eixos (Axes): Esta é a área onde os dados são efetivamente plotados, com seus
eixos X e Y. Uma figura pode ter múltiplos eixos (subplots).
Quando usamos funções como [Link](...), [Link](...), etc., o pyplot geral-
mente atua sobre os “eixos atuais” da “figura atual”.

Nosso Primeiro Gráfico com pyplot


Vamos criar um gráfico de linha muito simples.
1 # Importando as bibliotecas necess á rias
2 import matplotlib . pyplot as plt
3 import numpy as np # NumPy para criar dados de exemplo
4
5 # --- Preparando os Dados ---
6 x_valores = np . array ([1 , 2 , 3 , 4 , 5 , 6])
7 y_valores = x_valores ** 2 # y = x ao quadrado
8
9 # --- Criando o Gr á fico ---
10 # 1. plt . plot () cria o gr á fico de linha
11 plt . plot ( x_valores , y_valores )
12
13 # 2. Adicionando informa ç õ es ao gr á fico
14 plt . title ( " Meu Primeiro Gr á fico de Linha com Matplotlib ! " )
15 plt . xlabel ( " Valores do Eixo X ( Entrada ) " )
16 plt . ylabel ( " Valores do Eixo Y ( Quadrado da Entrada ) " )
17
18 # 3. Adicionando uma grade ( opcional )
19 plt . grid ( True )
20
21 # 4. plt . show () exibe o gr á fico
22 plt . show ()

Código 19.3: Primeiro gráfico de linha simples com Matplotlib


350 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

Executando o Código: Se estiver em um script Python normal, [Link]() abrirá uma


nova janela com o gráfico. Em ambientes como Jupyter Notebook, usando %matplotlib
inline no início, os gráficos aparecem na saída da célula (e [Link]() pode não ser
estritamente necessário, mas é boa prática).

Estrutura Típica de um Script de Plotagem com pyplot


1. Importar [Link] as plt (e numpy as np).

2. Preparar os dados.

3. Usar funções de plt para criar e customizar o gráfico.

4. Chamar [Link]() para exibir.

A interface pyplot oferece uma maneira direta de começar a criar visualizações. Nas próxi-
mas subseções, exploraremos diferentes tipos de gráficos e mais opções de personalização.

19.2.2 Gráficos de Linha ([Link]())


Os gráficos de linha são ideais para mostrar tendências ao longo do tempo ou a relação
entre duas variáveis contínuas onde a ordem dos pontos importa. A função principal para
isso no pyplot é [Link]().
Criando um Gráfico de Linha Básico (Revisão)
A forma mais simples é [Link](valores_x, valores_y). Se apenas uma sequência é
passada, ela é usada para o eixo Y, e os índices para o eixo X.
1 import matplotlib . pyplot as plt
2 import numpy as np
3
4 dias = np . arange (1 , 6)
5 leituras = np . array ([10 , 12 , 9 , 15 , 14])
6 plt . figure ( figsize =(6 ,3) ) # Tamanho da figura opcional
7 plt . plot ( dias , leituras )
8 plt . title ( " Leituras Di á rias " ) ; plt . xlabel ( " Dia " ) ;
plt . ylabel ( " Valor " )
9 plt . grid ( True ) ; # plt . show () # plt . show () ser á chamado no final dos
exemplos da se ç ã o

Código 19.4: Gráfico de linha básico


(Nota: [Link]() será omitido nos exemplos intermediários desta subseção para permitir
que múltiplas plotagens sejam combinadas ou para evitar múltiplas janelas, e será chamado
em um exemplo final consolidado.)
Plotando Múltiplas Linhas no Mesmo Gráfico
Chame [Link]() várias vezes antes de [Link]().
1 # Supondo imports plt e np
2 x_coords = np . linspace (0 , 2 * np . pi , 50)
3 y_seno = np . sin ( x_coords )
4 y_cosseno = np . cos ( x_coords )
5

6 plt . figure ( figsize =(8 ,4) ) # Nova figura para este exemplo
7 plt . plot ( x_coords , y_seno )
8 plt . plot ( x_coords , y_coords_cosseno )
19.2. Matplotlib: A Biblioteca Fundamental para Gráficos 351

9 plt . title ( " Seno e Cosseno " ) ; plt . xlabel ( " Â ngulo ( rad ) " ) ;
plt . ylabel ( " Valor " )
10 plt . grid ( True ) ; # plt . show ()

Código 19.5: Plotando múltiplas linhas (seno e cosseno)

Personalizando a Aparência das Linhas


A função [Link]() aceita argumentos opcionais como:

• color (ou c): Cor da linha (ex: ’red’, ’b’, ’#00FF00’).

• linestyle (ou ls): Estilo da linha (ex: ’-’ (sólida), ’–’ (tracejada), ’:’ (ponti-
lhada), ’-.’ (ponto-traço)).

• marker: Tipo de marcador para os pontos (ex: ’o’ (círculo), ’.’ (ponto), ’s’
(quadrado), ’x’, ’+’).

• linewidth (ou lw): Largura da linha (ex: 1.5, 2).

• Formato Curto: Uma string combinando cor, marcador e estilo (ex: ’ro-’ para
vermelho, círculo, linha sólida; ’g–s’ para verde, tracejada, quadrado).

Exemplo Consolidado de Personalização com Legendas, Limites e Mais:


1 # Supondo imports plt e np , e x_coords , y_seno , y_cosseno definidos
2 plt . figure ( figsize =(10 , 6) )
3
4 plt . plot ( x_coords , y_seno , color = ’ crimson ’ , linestyle = ’ - ’ ,
marker = ’o ’ ,
5 linewidth =1.5 , markersize =4 , label = ’ Seno ( x ) ’)
6
7 plt . plot ( x_coords , y_coords_cosseno , ’b - - x ’ , linewidth =2 ,
markersize =5 , label = ’ Cosseno ( x ) ’)
8
9 plt . title ( " Fun ç õ es Trigonom é tricas Personalizadas " )
10 plt . xlabel ( " Â ngulo $ \\ theta$ ( radianos ) " ) # Usando LaTeX para
s í mbolos
11 plt . ylabel ( " Valor da Fun ç ã o $f (\\ theta ) $ " )
12
13 # Adicionando legendas ( requer ’ label ’ nos plots )
14 plt . legend ( loc = ’ lower left ’)
15

16 plt . grid ( True , linestyle = ’: ’ , alpha =0.7) # Grade customizada


17
18 # Definindo limites para os eixos
19 plt . xlim (0 , 2 * np . pi )
20 plt . ylim ( -1.5 , 1.5)
21

22 plt . axhline (0 , color = ’ black ’ , linewidth =0.5) # Linha horizontal em


y =0
23 plt . axvline ( np . pi , color = ’ gray ’ , linestyle = ’ -. ’ , linewidth =0.7) #
Linha vertical em x = pi
24
25 plt . show () # Exibe o gr á fico final

Código 19.6: Gráfico de linha personalizado com seno e cosseno

Títulos, Rótulos, Legendas, Limites e Grades:


352 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

• [Link](), [Link](), [Link](): Essenciais para descrever seu gráfico.

• [Link](): Mostra as legendas definidas com o argumento label em cada


[Link](). O argumento loc controla sua posição.

• [Link](min, max), [Link](min, max): Definem os intervalos visíveis dos


eixos.

• [Link](True): Adiciona uma grade para facilitar a leitura.

Os gráficos de linha são versáteis, e o Matplotlib oferece um vasto controle sobre sua
aparência.

19.2.3 Gráficos de Dispersão (Scatter Plots - [Link]())


Enquanto os gráficos de linha são excelentes para tendências sequenciais, os gráficos de
dispersão (scatter plots) são ideais para visualizar a relação entre duas variáveis nu-
méricas. Cada par de valores (x, y) é representado como um ponto no gráfico, ajudando
a identificar correlações, agrupamentos e outliers. A função principal é [Link]().
Criando um Gráfico de Dispersão Básico
A forma mais simples é [Link](valores_x, valores_y).
1 import matplotlib . pyplot as plt
2 import numpy as np
3
4 # Dados de exemplo ( fict í cio )
5 alturas_m = np . array ([1.65 , 1.70 , 1.58 , 1.80 , 1.85 , 1.68 , 1.78 ,
1.82])
6 pesos_kg = np . array ([60 , 72 , 55 , 85 , 88 , 67 , 80 , 90])
7
8 plt . figure ( figsize =(8 , 5) )
9 plt . scatter ( alturas_m , pesos_kg )
10 plt . title ( " Gr á fico de Dispers ã o : Altura ( m ) vs . Peso ( kg ) " )
11 plt . xlabel ( " Altura ( m ) " )
12 plt . ylabel ( " Peso ( kg ) " )
13 plt . grid ( True , linestyle = ’: ’ , alpha =0.7)
14 # plt . show () # Mostraremos um exemplo consolidado no final

Código 19.7: Gráfico de dispersão básico: Altura vs. Peso


Personalizando a Aparência dos Pontos
A função [Link]() aceita diversos parâmetros para customização:

• s: Tamanho dos marcadores. Pode ser um escalar (todos iguais) ou um array


(tamanhos individuais).

• c: Cor dos marcadores. Pode ser uma cor única, uma sequência de cores, ou uma
sequência de números mapeados para cores via cmap (mapa de cores).

• marker: Tipo de marcador (ex: ’o’, ’.’, ’s’, ’x’, ’+’).

• alpha: Transparência (0.0 a 1.0). Útil para pontos sobrepostos.

• edgecolors: Cor da borda dos marcadores.

• label: Para uso em legendas com [Link]().


19.2. Matplotlib: A Biblioteca Fundamental para Gráficos 353

Exemplo Consolidado de Personalização:


1 # Supondo imports de plt e np
2 np . random . seed (42) # Para repr odutibili dade
3 num_pontos = 60
4 x_dados = np . random . rand ( num_pontos ) * 15
5 y_dados = 0.8 * x_dados + np . random . randn ( num_pontos ) * 3
6
7 t a ma nho s _d o s _p o nt o s = np . random . rand ( num_pontos ) * 180 + 20
8 valores_para_cor = x_dados * y_dados # Vari á vel para mapear para
cores
9
10 plt . figure ( figsize =(10 , 6) )
11 dispersao_plot = plt . scatter ( x_dados , y_dados ,
12 s = tamanhos_dos_pontos ,
13 c = valores_para_cor ,
14 marker = ’^ ’ ,
15 alpha =0.65 ,
16 cmap = ’ coolwarm ’ , # Experimente ’ viridis ’, ’ plasma ’, ’ magma ’
17 edgecolors = ’ black ’ ,
18 linewidths =0.5 ,
19 label = ’ Dados Simulados ’)
20

21 plt . colorbar ( dispersao_plot , label = ’ Intensidade da Cor ( X * Y ) ’) #


Barra de cores
22
23 plt . title ( " Gr á fico de Dispers ã o com Tamanho e Cor Vari á veis " )
24 plt . xlabel ( " Vari á vel X Simulada " )
25 plt . ylabel ( " Vari á vel Y Simulada " )
26 plt . legend ()
27 plt . grid ( True , linestyle = ’: ’ , alpha =0.5)
28 plt . show ()

Código 19.8: Gráfico de dispersão personalizado


Este exemplo demonstra como um gráfico de dispersão pode visualizar múltiplas dimensões
de dados simultaneamente.
Quando Usar Gráficos de Dispersão?
• Investigar a relação ou correlação entre duas variáveis numéricas.
• Identificar agrupamentos (clusters) ou outliers.
Eles se diferenciam dos gráficos de linha porque o foco está na relação entre pares de
pontos, e não necessariamente em uma progressão ordenada conectando-os.

19.2.4 Gráficos de Barras ([Link](), [Link]())


Os gráficos de barras são ideais para comparar valores numéricos entre diferentes
categorias discretas. A altura (ou comprimento) de cada barra representa a magnitude
da quantidade. Matplotlib oferece [Link]() para barras verticais e [Link]() para
horizontais.

Gráficos de Barras Verticais ([Link]())


Sintaxe básica: [Link](x_categorias, alturas_barras, width=0.8, color=None,
...)
354 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

• x_categorias: Posições ou nomes das categorias no eixo x.

• alturas_barras: Valores (alturas) de cada barra.

1 import matplotlib . pyplot as plt


2 import numpy as np
3

4 no me s_d os _p rod ut os = [ ’ Produto Alfa ’ , ’ Produto Beta ’ , ’ Produto


Gama ’ , ’ Produto Delta ’]
5 unidades_ vendidas = [250 , 180 , 310 , 150]
6 cores_barras = [ ’ skyblue ’ , ’ lightcoral ’ , ’ lightseagreen ’ , ’ gold ’]
7
8 plt . figure ( figsize =(8 , 5) )
9 plt . bar ( nomes_dos_produtos , unidades_vendidas ,
10 color = cores_barras , width =0.7)
11
12 plt . title ( " Volume de Vendas por Produto " , fontsize =15)
13 plt . xlabel ( " Produto " , fontsize =12)
14 plt . ylabel ( " Unidades Vendidas " , fontsize =12)
15 plt . grid ( True , axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
16 plt . xticks ( rotation =10 , ha = ’ right ’) # Rota ç ã o para nomes longos
17 plt . ylim (0 , max ( unidade s_vendida s ) * 1.15)
18 plt . tight_layout ()
19 # plt . show () # Omitido para exemplos sequenciais , ser á chamado no
final da se ç ã o

Código 19.9: Gráfico de barras verticais de vendas por produto

Gráficos de Barras Horizontais ([Link]())


Sintaxe básica: [Link](y_categorias, larguras_barras, height=0.8, color=None,
...) Úteis quando os nomes das categorias são longos.

• y_categorias: Posições ou nomes das categorias no eixo y.

• larguras_barras: Valores (comprimentos) de cada barra.

1 # Supondo plt e np importados


2 nomes_cidades = [ ’S ã o Paulo ’ , ’ Rio de Janeiro ’ , ’ Belo Horizonte ’ ,
’ Salvador ’]
3 pop_milhoes = [12.3 , 6.7 , 2.5 , 2.9]
4
5 plt . figure ( figsize =(9 , 5) )
6 barras_h = plt . barh ( nomes_cidades , pop_milhoes , color = ’ teal ’ ,
height =0.6)
7
8 plt . title ( " Popula ç ã o de Algumas Capitais ( Milh õ es ) " , fontsize =15)
9 plt . xlabel ( " Popula ç ã o ( em milh õ es ) " , fontsize =12)
10 plt . ylabel ( " Cidades " , fontsize =12)
11 plt . grid ( True , axis = ’x ’ , linestyle = ’: ’ , alpha =0.6)
12 plt . gca () . invert_yaxis () # Primeira cidade no topo
13
14 # Adicionando valores à s barras
15 for barra in barras_h :
16 largura = barra . get_width ()
17 plt . text ( largura + 0.05 , barra . get_y () + barra . get_height () /2 ,
19.2. Matplotlib: A Biblioteca Fundamental para Gráficos 355

18 f ’{ largura :.1 f } M ’ , va = ’ center ’ , ha = ’ left ’ , fontsize =8)


19
20 plt . tight_layout ()
21 plt . show () # Exibe os dois gr á ficos ( vertical e horizontal ) criados
at é agora

Código 19.10: Gráfico de barras horizontais de população

(Nota: Os dois exemplos de código acima produzirão seus respectivos gráficos quando
[Link]() for chamado. No exemplo LaTeX, o [Link]() foi colocado no final do
segundo bloco para exibir ambos, mas em um script ou notebook, cada um poderia ter seu
[Link]() ou ser exibido automaticamente.)
Gráficos de Barras Agrupadas ou Empilhadas: Para comparar múltiplas séries,
é possível criar gráficos de barras agrupadas ou empilhadas. Isso pode exigir manipulação
manual das posições com Matplotlib, mas bibliotecas como Seaborn ou Pandas facilitam
essa tarefa.
Quando Usar Gráficos de Barras?

• Comparar valores discretos entre diferentes categorias.

• Mostrar rankings ou magnitudes.

São ferramentas eficazes para comparações categóricas.

19.2.5 Histogramas ([Link]()): Entendendo a Distribuição dos


Dados
Os histogramas são usados para visualizar a distribuição de frequência de um
conjunto de dados numéricos. Eles agrupam os dados em intervalos (ou “caixas”, bins) e
mostram quantas observações caem em cada intervalo, ajudando a entender a forma, a
tendência central e a dispersão dos dados. A função principal é [Link]().
Como Funciona um Histograma?

1. O intervalo dos dados é dividido em subintervalos (bins).

2. Conta-se quantas observações caem em cada bin (frequência).

3. Uma barra é desenhada para cada bin, com altura proporcional à frequência (ou
densidade).

Criando um Histograma Básico com [Link]()


Sintaxe: [Link](x, bins=None, color=None, edgecolor=None, ...)

• x: O array ou sequência de dados numéricos.

• bins (opcional): Número de bins (inteiro) ou as bordas dos bins (sequência).

1 import matplotlib . pyplot as plt


2 import numpy as np
3

4 # Dados de exemplo : notas de 100 alunos (0 a 10)


5 np . random . seed (42)
6 notas_da_turma = np . random . normal ( loc =7.0 , scale =1.8 , size =100)
356 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

7 notas_da_turma = np . clip ( notas_da_turma , 0 , 10) # Garante que


fiquem entre 0 e 10
8
9 plt . figure ( figsize =(9 , 6) )
10 plt . hist ( notas_da_turma , bins =10 , color = ’ dodgerblue ’ ,
edgecolor = ’ black ’ , alpha =0.75)
11
12 plt . title ( " Histograma da Distribui ç ã o de Notas " , fontsize =16)
13 plt . xlabel ( " Notas dos Alunos (0 -10) " , fontsize =12)
14 plt . ylabel ( " Frequ ê ncia ( N ú mero de Alunos ) " , fontsize =12)
15 plt . grid ( axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
16 # plt . show () % Omitido para exemplos sequenciais

Código 19.11: Histograma da distribuição de notas de uma turma


Ajustando o Número de Bins (bins)
A escolha do número de bins é crucial e pode alterar a percepção da distribuição.

• Poucos bins: Pode esconder detalhes.

• Muitos bins: Pode criar um gráfico “ruidoso”.

• Experimentar é comum. bins=’auto’ também é uma opção.

1 # Usando ’ notas_da_turma ’ do exemplo anterior


2 plt . figure ( figsize =(15 , 4) )
3
4 plt . subplot (1 , 3 , 1)
5 plt . hist ( notas_da_turma , bins =5 , color = ’ lightcoral ’ ,
edgecolor = ’ black ’)
6 plt . title ( " Histograma com 5 Bins " ) ; plt . xlabel ( " Notas " ) ;
plt . ylabel ( " Frequ ê ncia " )
7 plt . grid ( axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
8
9 plt . subplot (1 , 3 , 2)
10 plt . hist ( notas_da_turma , bins =12 , color = ’ mediumseagreen ’ ,
edgecolor = ’ black ’)
11 plt . title ( " Histograma com 12 Bins " ) ; plt . xlabel ( " Notas " ) ;
plt . ylabel ( " Frequ ê ncia " )
12 plt . grid ( axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
13
14 plt . subplot (1 , 3 , 3)
15 plt . hist ( notas_da_turma , bins =25 , color = ’ gold ’ , edgecolor = ’ black ’)
16 plt . title ( " Histograma com 25 Bins " ) ; plt . xlabel ( " Notas " ) ;
plt . ylabel ( " Frequ ê ncia " )
17 plt . grid ( axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
18
19 plt . tight_layout ()
20 plt . show () # Exibe todos os gr á ficos de histograma

Código 19.12: Efeito do número de bins no histograma


Outras Opções Importantes de [Link]():

• density=True: Normaliza o histograma para que a área total seja 1 (eixo Y vira
densidade).

• cumulative=True: Cria um histograma cumulativo.


19.2. Matplotlib: A Biblioteca Fundamental para Gráficos 357

• histtype: Define o tipo de histograma (ex: ’bar’, ’step’).

Quando Usar Histogramas? Para entender a forma da distribuição de dados


numéricos, identificar tendência central, dispersão, simetria, picos (modas) ou lacunas.
Histogramas vs. Gráficos de Barras: Histogramas são para dados numéricos (eixo
X é contínuo, barras geralmente se tocam). Gráficos de barras são para dados categóricos
(eixo X tem categorias discretas, barras geralmente espaçadas).

19.2.6 Gráficos de Pizza (Pie Charts - [Link]()): Mostrando


Proporções (Uso com Moderação)
Os gráficos de pizza (ou de setores) são usados para representar a proporção de
diferentes categorias que compõem um todo (100%). Cada “fatia” representa uma categoria,
e seu tamanho é proporcional à sua contribuição. A função no pyplot é [Link]().
Criando um Gráfico de Pizza Básico com [Link]()
Sintaxe:
[Link](x, labels=None, colors=None, autopct=None, explode=None, ...)

• x: Lista de valores numéricos (tamanhos das fatias).

• labels: Lista de strings com os rótulos para cada fatia.

• colors: Lista de cores para as fatias.

• autopct: Formato para exibir a porcentagem na fatia (ex: ’%1.1f%%’).

• explode: Tupla para “destacar” (separar) fatias do centro (ex: (0, 0.1, 0)).

• startangle: Ângulo inicial da primeira fatia (em graus).

• shadow: Booleano para adicionar sombra.

1 import matplotlib . pyplot as plt


2
3 # Dados de exemplo : despesas mensais
4 categoria s_gastos = [ ’ Moradia ’ , ’ Alimenta ç ã o ’ , ’ Transporte ’ ,
’ Lazer ’ , ’ Outros ’]
5 v a l o r e s _ g a s t o s _ r e a i s = [1200 , 800 , 300 , 250 , 150]
6 cores_fatias = [ ’ gold ’ , ’ lightcoral ’ , ’ lightskyblue ’ , ’ lightgreen ’ ,
’ orchid ’]
7 explode_fatias = (0 , 0.1 , 0 , 0 , 0) # Destaca a segunda fatia
8
9 plt . figure ( figsize =(7 , 7) ) # Figura com propor ç ã o igual
10
11 plt . pie ( valores_gastos_reais ,
12 explode = explode_fatias ,
13 labels = categorias_gastos ,
14 colors = cores_fatias ,
15 autopct = ’ %1.1 f %% ’ , # Formato da porcentagem
16 shadow = True ,
17 startangle =140) # Â ngulo inicial
18
19 plt . title ( " Distribui ç ã o Percentual das Despesas Mensais " ,
fontsize =15)
358 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

20 # plt . axis ( ’ equal ’) # Garante que seja um c í rculo ( geralmente


padr ã o para pie )
21 plt . show ()

Código 19.13: Gráfico de pizza de despesas mensais


Considerações sobre o Uso de Gráficos de Pizza:

• Dificuldade em Comparações Precisas: O olho humano não compara bem


ângulos ou áreas de fatias.

• Melhor para Poucas Categorias: Idealmente, não mais que 5-7 categorias. Com
muitas fatias, fica poluído.

• Alternativas: Para comparações mais precisas, um gráfico de barras é frequente-


mente melhor.

• Quando Usar: Se o objetivo é mostrar a composição percentual simples de um


todo e há poucas categorias distintas.

Use gráficos de pizza com critério, ciente de suas limitações.

19.2.7 Trabalhando com Múltiplos Gráficos (Subplots)


Frequentemente, queremos exibir vários gráficos relacionados em uma única figura
para facilitar comparações ou apresentar diferentes aspectos de um conjunto de dados.
Matplotlib permite isso através do conceito de subplots (subgráficos).

Criando uma Figura (Opcional, mas Útil)


Toda plotagem ocorre em uma Figure. Embora o pyplot crie uma implicitamente,
podemos criar uma explicitamente para controlar seu tamanho e outras propriedades: fig
= [Link](figsize=(largura, altura)).

Usando [Link](num_linhas, num_colunas, indice_plot)


Esta função cria e/ou ativa um subplot em uma grade.

• L, C: Número total de linhas e colunas na grade.

• I: Índice do subplot (começa em 1, da esquerda para a direita, de cima para baixo).

Chamadas subsequentes de [Link](), [Link](), etc., aplicam-se ao subplot ativo.


Exemplo: Dois subplots lado a lado (1 linha, 2 colunas)
1 import matplotlib . pyplot as plt
2 import numpy as np
3
4 x = np . linspace (0 , 2 * np . pi , 100)
5 y_sin = np . sin ( x )
6 y_cos = np . cos ( x )
7

8 plt . figure ( figsize =(12 , 4.5) )


9
10 # Primeiro subplot (1 linha , 2 colunas , í ndice 1)
19.2. Matplotlib: A Biblioteca Fundamental para Gráficos 359

11 plt . subplot (1 , 2 , 1)
12 plt . plot (x , y_sin , color = ’ dodgerblue ’ , label = ’ sin ( x ) ’)
13 plt . title ( " Fun ç ã o Seno " ) ; plt . xlabel ( " Â ngulo " ) ; plt . ylabel ( " sin ( x ) " )
14 plt . grid ( True , linestyle = ’: ’) ; plt . legend ()
15
16 # Segundo subplot (1 linha , 2 colunas , í ndice 2)
17 plt . subplot (1 , 2 , 2)
18 plt . plot (x , y_cos , color = ’ crimson ’ , label = ’ cos ( x ) ’)
19 plt . title ( " Fun ç ã o Cosseno " ) ; plt . xlabel ( " Â ngulo " ) ;
plt . ylabel ( " cos ( x ) " )
20 plt . grid ( True , linestyle = ’: ’) ; plt . legend ()
21

22 plt . suptitle ( " Fun ç õ es Lado a Lado " , fontsize =16 , y =1.02)
23 plt . tight_layout ( rect =[0 , 0 , 1 , 0.95])
24 # plt . show () # Omitido para exemplos sequenciais

Código 19.14: Dois subplots lado a lado com [Link]()


Exemplo: Grade 2x2 de subplots
1 # Supondo plt e np importados
2 x_c = np . arange (0 , 5 , 0.1)
3 plt . figure ( figsize =(10 , 8) )
4

5 plt . subplot (2 , 2 , 1) ; plt . plot ( x_c , x_c , ’g - ’) ; plt . title ( ’y = x ’) ;


plt . grid ( True )
6 plt . subplot (2 , 2 , 2) ; plt . plot ( x_c , x_c **2 , ’m - - ’) ; plt . title ( ’y =
x * x ’) ; plt . grid ( True )
7 plt . subplot (2 , 2 , 3) ; plt . plot ( x_c , np . log ( x_c + 1) , ’c : ’) ;
plt . title ( ’y = log ( x +1) ’) ; plt . grid ( True )
8 plt . subplot (2 , 2 , 4) ; plt . plot ( x_c , np . exp ( x_c / 5) , ’k -. ’) ;
plt . title ( ’y = exp ( x /5) ’) ; plt . grid ( True )
9
10 plt . suptitle ( " Grade 2 x2 de Fun ç õ es " , fontsize =16)
11 plt . tight_layout ( rect =[0 , 0.03 , 1 , 0.95])
12 # plt . show () # Omitido

Código 19.15: Grade de subplots 2x2 com [Link]()

A Interface Orientada a Objetos: fig, ax = [Link]()


Para controle mais fino, la interface orientada a objetos é preferida.

• fig, ax = [Link]() para um único subplot.

• fig, axs = [Link](nrows, ncols, ...) para uma grade. axs é um array
NumPy de objetos Axes.

Em vez de [Link](), usa-se [Link](), ax.set_title(), ax.set_xlabel(), etc.


Exemplo: Dois subplots lado a lado com interface OO
1 # Supondo plt , np , x , y_sin , y_cos definidos
2 # x = np . linspace (0 , 2 * np . pi , 100)
3 # y_sin = np . sin ( x )
4 # y_cos = np . cos ( x )
5
6 fig , axs = plt . subplots (1 , 2 , figsize =(12 , 4.5) ) # 1 linha , 2
colunas
360 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

7
8 axs [0]. plot (x , y_sin , color = ’ darkviolet ’ , label = ’ sin ( x ) OO ’)
9 axs [0]. set_title ( " Fun ç ã o Seno ( Estilo OO ) " )
10 axs [0]. set_xlabel ( " Â ngulo " ) ; axs [0]. set_ylabel ( " Seno " )
11 axs [0]. grid ( True ) ; axs [0]. legend ()
12

13 axs [1]. plot (x , y_cos , color = ’ teal ’ , label = ’ cos ( x ) OO ’)


14 axs [1]. set_title ( " Fun ç ã o Cosseno ( Estilo OO ) " )
15 axs [1]. set_xlabel ( " Â ngulo " ) ; axs [1]. set_ylabel ( " Cosseno " )
16 axs [1]. grid ( True ) ; axs [1]. legend ()
17
18 fig . suptitle ( " Subplots com Interface OO " , fontsize =16 , y =1.02)
19 plt . tight_layout ( rect =[0 , 0 , 1 , 0.95])
20 plt . show () # Exibe os tr ê s conjuntos de gr á ficos desta subse ç ã o

Código 19.16: Subplots com interface orientada a objetos


A abordagem orientada a objetos é mais poderosa para gráficos complexos.
plt.tight_layout(): Ajusta automaticamente os parâmetros dos subplots para um
layout limpo, evitando sobreposições. É bom chamá-la antes de [Link]().
Subplots permitem apresentar múltiplas visualizações em uma única figura.

19.2.8 Salvando Gráficos em Arquivos: Persistindo Suas Visuali-


zações
Além de exibir gráficos na tela, frequentemente precisamos salvá-los em arquivos para
incluir em relatórios, apresentações ou publicações. Matplotlib facilita isso com a função
[Link]().
A Função [Link]()
Salva a figura atual em um arquivo. A sintaxe básica é:
[Link](’caminho/nome_arquivo.extensao’, ...) O Matplotlib infere o formato
pela extensão. Formatos comuns: .png, .jpg, .pdf, .svg, .eps.
Parâmetros Importantes:
• dpi (Dots Per Inch): Controla a resolução para formatos raster (ex: dpi=300
para boa qualidade de impressão).

• bbox_inches=’tight’: Tenta remover espaços em branco extras ao redor do gráfico.


Muito útil.

• transparent=True: Torna o fundo transparente (para formatos que suportam, como


PNG).
Importante: Ordem de [Link]() e [Link]()
A instrução [Link]() deve ser chamada ANTES de [Link](). Chamar [Link]()
primeiro pode limpar a figura, resultando em um arquivo salvo em branco.
1 import matplotlib . pyplot as plt
2 import numpy as np
3
4 x = np . linspace ( - np . pi , np . pi , 150)
5 y_sinc = np . sinc ( x ) # Fun ç ã o sinc ( x ) = sin ( pi * x ) /( pi * x )
6
7 plt . figure ( figsize =(8 , 4.5) )
8 plt . plot (x , y_sinc , label = ’ fun ç ã o sinc ( x ) ’ , color = ’ darkslateblue ’)
19.3. Seaborn: Gráficos Estatísticos Mais Elaborados e Atraentes 361

9 plt . title ( " Exemplo de Fun ç ã o Sinc ( x ) " )


10 plt . xlabel ( " x " ) ; plt . ylabel ( " sinc ( x ) " )
11 plt . legend () ; plt . grid ( True , linestyle = ’: ’)
12 plt . axhline (0 , color = ’ black ’ , lw =0.7)
13
14 # Salvando a figura ANTES de plt . show ()
15 try :
16 nome_base = " grafico_sinc "
17 plt . savefig ( f " { nome_base }. png " , dpi =300 , bbox_inches = ’ tight ’)
18 print ( f " Gr á fico salvo como ’{ nome_base }. png ’" )
19
20 plt . savefig ( f " { nome_base }. pdf " , bbox_inches = ’ tight ’)
21 print ( f " Gr á fico salvo como ’{ nome_base }. pdf ’" )
22 except Exception as e :
23 print ( f " Erro ao salvar o gr á fico : { e } " )
24
25 # Exibindo o gr á fico na tela
26 plt . show ()

Código 19.17: Criando um gráfico e salvando em PNG e PDF

Com [Link](), você pode exportar suas visualizações para diversos formatos,
adequados para diferentes usos.
Com isso, cobrimos os fundamentos do Matplotlib pyplot. Na próxima seção, veremos
como o Seaborn pode facilitar a criação de gráficos estatísticos.

19.3 Seaborn: Gráficos Estatísticos Mais Elaborados e


Atraentes
O Matplotlib é extremamente poderoso, mas para certos tipos de visualizações, especi-
almente aquelas focadas em análise estatística, pode exigir mais código. É aqui que entra o
Seaborn, uma biblioteca construída sobre o Matplotlib que visa fornecer uma interface de
alto nível para desenhar gráficos estatísticos atraentes e informativos com menos esforço.

19.3.1 Introdução ao Seaborn: Visualizações Estatísticas Simplifi-


cadas
Lançado por Michael Waskom, o Seaborn simplifica a criação de gráficos comuns em
análise de dados.

• Funções de plotagem especializadas: Para distribuições, relações, dados categó-


ricos, etc.

• Temas e Paletas de Cores Atraentes: Gráficos com estética aprimorada por


padrão.

• Forte Integração com Pandas DataFrames: Embora funcione com arrays


NumPy e listas, brilha com Pandas (Capítulo 20).

• Foco em Revelar a Estrutura dos Dados.


362 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

Relação com Matplotlib


O Seaborn “senta-se em cima” do Matplotlib. Gráficos Seaborn são objetos Matplotlib,
permitindo que você use comandos plt para customizações finas. Seaborn complementa,
não substitui, o Matplotlib.

Convenção de Importação

1 import seaborn as sns


2 import matplotlib . pyplot as plt
3 import numpy as np

Código 19.18: Importações comuns para Seaborn e Matplotlib


O alias sns é universalmente usado para Seaborn.

Preparando o Terreno: Temas e Estilos


Seaborn vem com temas que melhoram a aparência dos gráficos (incluindo os do
Matplotlib, se o tema for definido globalmente).
1 import matplotlib . pyplot as plt
2 import seaborn as sns
3 import numpy as np
4

5 # Definindo um tema Seaborn


6 sns . set_theme ( style = " whitegrid " , palette = " pastel " )
7
8 # Gr á fico Matplotlib simples para ver o efeito
9 x_demo = np . linspace (0 , 10 , 50)
10 y_demo_seno = np . sin ( x_demo )
11
12 plt . figure ( figsize =(7 , 4) )
13 plt . plot ( x_demo , y_demo_seno , marker = ’. ’)
14 plt . title ( " Gr á fico Matplotlib com Tema Seaborn " )
15 plt . xlabel ( " Eixo X " ) ; plt . ylabel ( " Eixo Y " )
16 plt . show ()

Código 19.19: Aplicando um tema Seaborn


Ao executar, o gráfico Matplotlib terá uma aparência influenciada pelo tema Seaborn.
Nas próximas subseções, exploraremos como criar diferentes tipos de gráficos estatísticos
usando as funções dedicadas do Seaborn.

19.3.2 Gráficos de Distribuição com Seaborn


Entender a distribuição de uma variável é crucial na análise de dados. Seaborn oferece
várias funções para visualizar distribuições univariadas de forma elaborada. Lembre-se
das importações:
1 import matplotlib . pyplot as plt
2 import seaborn as sns
3 import numpy as np
4 sns . set_theme ( style = " whitegrid " , palette = " muted " )

Código 19.20: Importações e tema para Seaborn


19.3. Seaborn: Gráficos Estatísticos Mais Elaborados e Atraentes 363

Histogramas Aprimorados com [Link]()

Similar ao [Link](), mas com funcionalidades adicionais como a sobreposição


fácil de uma Estimativa de Densidade do Kernel (KDE). Sintaxe: [Link](data,
x="coluna", bins="auto", kde=False, hue="categoria", ...)
1 # Dados de exemplo
2 np . random . seed (20)
3 alturas_amostra = np . random . normal ( loc =1.75 , scale =0.1 , size =300)
4 alturas_amostra = np . clip ( alturas_amostra , 1.4 , 2.1) # Garante
intervalo razo á vel
5
6 plt . figure ( figsize =(9 , 5.5) )
7 sns . histplot ( alturas_amostra , bins =20 , kde = True , color = ’ skyblue ’ ,
8 line_kws ={ ’ linewidth ’: 2 , ’ color ’: ’ red ’ })
9 plt . title ( " Histograma de Alturas com KDE ( Seaborn ) " )
10 plt . xlabel ( " Altura ( metros ) " ) ; plt . ylabel ( " Frequ ê ncia " )
11 # plt . show () % Omitido para exemplos sequenciais

Código 19.21: Histograma com KDE usando [Link]()

Gráficos de Densidade do Kernel (KDE) com [Link]()

O KDE estima a função de densidade de probabilidade, produzindo uma curva suave


da distribuição.
Sintaxe: [Link](data, x="coluna", fill=False, hue="categoria", ...)
1 # Usando alturas_amostra do exemplo anterior
2 plt . figure ( figsize =(9 , 5.5) )
3 sns . kdeplot ( alturas_amostra , fill = True , color = ’ salmon ’ ,
linewidth =2.5 , alpha =0.5)
4 plt . title ( " KDE das Alturas ( Seaborn ) " )
5 plt . xlabel ( " Altura ( metros ) " ) ; plt . ylabel ( " Densidade " )
6 plt . show ()

Código 19.22: Estimativa de Densidade do Kernel (KDE) com Seaborn

Figura de Nível Superior para Distribuições: [Link]()

Função mais geral (figure-level ) para criar vários tipos de gráficos de distribuição
(kind="hist", "kde", "ecdf").
1 # Usando alturas_amostra
2 # sns . displot () cria sua pr ó pria figura
3 plot_dist = sns . displot ( alturas_amostra , kind = " hist " , kde = True ,
4 color = ’ purple ’ , aspect =1.8 , height =4)
5 plot_dist . set_titles ( " Distribui ç ã o de Alturas ( sns . displot ) " )
6 plot_dist . set_xlabels ( " Altura ( m ) " ) ;
plot_dist . set_ylabels ( " Contagem " )
7 plt . show ()

Código 19.23: [Link]() para histograma com KDE


364 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

Box Plots (Diagramas de Caixa) com [Link]()


Mostram a distribuição através de quartis, mediana e outliers. Excelentes para compa-
rar grupos.
Sintaxe: [Link](data, x="categoria", y="numerica", hue="outra_cat", ...)
1 np . random . seed (101)
2 dados_A = np . random . normal ( loc =50 , scale =10 , size =80)
3 dados_B = np . random . normal ( loc =55 , scale =12 , size =80)
4 dados_A = np . clip ( dados_A , 20 , 80)
5 dados_B = np . clip ( dados_B , 20 , 90)
6
7 plt . figure ( figsize =(7 , 5) )
8 sns . boxplot ( data =[ dados_A , dados_B ] , palette = " pastel " )
9 plt . xticks ([0 , 1] , [ ’ Grupo Alfa ’ , ’ Grupo Beta ’ ])
10 plt . title ( " Compara ç ã o de Grupos ( Box Plot ) " )
11 plt . ylabel ( " Valores Observados " )
12 plt . grid ( axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
13 plt . show ()

Código 19.24: Comparação de Box Plots com Seaborn

Gráficos de Violino com [Link]()


Combinam box plot com uma estimativa de densidade do kernel (KDE) para mostrar
a forma da distribuição. Sintaxe similar ao [Link]().
1 # Usando dados_A e dados_B do exemplo anterior
2 plt . figure ( figsize =(7 , 5) )
3 sns . violinplot ( data =[ dados_A , dados_B ] , palette = " Set3 " ,
inner = " quartile " )
4 # ’ inner ’ pode ser " box " , " quartile " , " point " , " stick " , ou None
5 plt . xticks ([0 , 1] , [ ’ Grupo Alfa ’ , ’ Grupo Beta ’ ])
6 plt . title ( " Compara ç ã o de Grupos ( Gr á fico de Violino ) " )
7 plt . ylabel ( " Valores Observados " )
8 plt . grid ( axis = ’y ’ , linestyle = ’: ’ , alpha =0.7)
9 plt . show () # Mostra todos os gr á ficos desta subse ç ã o

Código 19.25: Comparação com Gráficos de Violino (Seaborn)


O Seaborn oferece ferramentas poderosas para visualizar rapidamente as distribuições em
seus dados.

19.3.3 Gráficos Categóricos com Seaborn


Frequentemente, precisamos comparar valores numéricos entre diferentes categorias
discretas ou visualizar a contagem de observações por categoria. Seaborn oferece funções
especializadas para isso, que funcionam muito bem com DataFrames Pandas (Capítulo
20).
1 import matplotlib . pyplot as plt
2 import seaborn as sns
3 import numpy as np
4 import pandas as pd # Seaborn integra bem com Pandas
5 sns . set_theme ( style = " whitegrid " , palette = " muted " )

Código 19.26: Importações e tema para exemplos Seaborn


19.3. Seaborn: Gráficos Estatísticos Mais Elaborados e Atraentes 365

Gráficos de Barras com Informação Estatística ([Link]())


Mostra uma estimativa de tendência central (padrão: média) para uma variável
numérica por categoria, com intervalos de confiança.
1 # Criando dados de exemplo com DataFrame Pandas ( Pandas ser á visto
no Cap . 20)
2 np . random . seed (42)
3 dados_exp = pd . DataFrame ({
4 ’ Tratamento ’: np . random . choice ([ ’ Controle ’ , ’ Droga A ’ , ’ Droga
B ’] , size =90) ,
5 ’ Resultado ’: np . random . normal ( loc =10 , scale =3 , size =90) +
6 np . repeat ([0 , 3 , 5] , 30) # Efeito do tratamento
7 })
8
9 plt . figure ( figsize =(8 ,5.5) )
10 sns . barplot ( data = dados_exp , x = ’ Tratamento ’ , y = ’ Resultado ’ ,
11 palette = ’ viridis ’ , errorbar =( ’ ci ’ , 95) , capsize =0.1)
12 plt . title ( " Resultado M é dio por Tratamento ( IC 95%) " )
13 plt . xlabel ( " Tratamento " ) ; plt . ylabel ( " M é dia do Resultado " )
14 # plt . show () % Omitido para exemplos sequenciais

Código 19.27: Gráfico de barras estatístico com [Link]()

Gráficos de Contagem ([Link]())


Mostra a contagem de observações em cada categoria. Como um histograma para dados
categóricos. Sintaxe: [Link](data=df, x="cat", hue="outra_cat", ...)
1 # Usando ’ dados_exp ’ do exemplo anterior
2 plt . figure ( figsize =(7 ,5) )
3 sns . countplot ( data = dados_exp , x = ’ Tratamento ’ , hue = ’ Tratamento ’ ,
4 palette = ’ Set2 ’ , legend = False )
5 plt . title ( " Contagem de Amostras por Tratamento " )
6 plt . xlabel ( " Tratamento " ) ; plt . ylabel ( " N ú mero de Amostras " )
7 # plt . show ()

Código 19.28: Gráfico de contagem com [Link]()

Visualizando a Distribuição de Pontos por Categoria


• [Link](): Gráfico de dispersão com um eixo categórico. Mostra todos os
pontos. jitter=True ajuda a evitar sobreposição.

• [Link](): Similar, mas ajusta os pontos para evitar sobreposição, mos-


trando a densidade.

1 # Usando ’ dados_exp ’
2 plt . figure ( figsize =(11 , 5) )
3 plt . subplot (1 , 2 , 1)
4 sns . stripplot ( data = dados_exp , x = ’ Tratamento ’ , y = ’ Resultado ’ ,
5 hue = ’ Tratamento ’ , jitter =0.2 , legend = False , palette = ’ flare ’ ,
alpha =0.7)
6 plt . title ( " Strip Plot : Resultado por Tratamento " )
7
8 plt . subplot (1 , 2 , 2)
366 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

9 sns . swarmplot ( data = dados_exp , x = ’ Tratamento ’ , y = ’ Resultado ’ ,


10 hue = ’ Tratamento ’ , legend = False , palette = ’ crest ’ , size =3.5)
11 plt . title ( " Swarm Plot : Resultado por Tratamento " )
12 plt . tight_layout ()
13 # plt . show ()

Código 19.29: Strip plot e Swarm plot com Seaborn

Gráficos de Ponto ([Link]())


Mostram estimativas pontuais (ex: média) e intervalos de confiança por categoria, conec-
tados por linhas. Sintaxe: [Link](data=df, x="cat", y="num", hue="outra_cat",
dodge=True, ...)
1 # Usando ’ dados_exp ’ e adicionando uma segunda categoria ’ Fase ’
2 np . random . seed (12)
3 dados_exp [ ’ Fase ’] = np . random . choice ([ ’ Inicial ’ , ’ Final ’] , size =90)
4
5 plt . figure ( figsize =(8 ,5) )
6 sns . pointplot ( data = dados_exp , x = ’ Tratamento ’ , y = ’ Resultado ’ ,
hue = ’ Fase ’ ,
7 dodge =0.1 , markers =[ ’o ’ , ’s ’] , linestyles =[ ’ - ’ , ’ -- ’] ,
8 errorbar =( ’ ci ’ , 95) , capsize =.08)
9 plt . title ( " Point Plot : Resultado M é dio por Tratamento e Fase " )
10 plt . xlabel ( " Tratamento " ) ; plt . ylabel ( " M é dia do Resultado " )
11 plt . legend ( title = ’ Fase ’)
12 plt . grid ( True , linestyle = ’: ’ , alpha =0.6)
13 plt . show () # Exibe todos os gr á ficos desta subse ç ã o

Código 19.30: Gráfico de ponto com Seaborn

Estes são alguns dos gráficos categóricos que o Seaborn oferece para comparações entre
grupos.

19.3.4 Gráficos de Relação com Seaborn


Entender como as variáveis se relacionam é crucial. Seaborn aprimora os gráficos de
dispersão e de linha, facilitando a incorporação de mais dimensões de dados e a exibição
de tendências com incertezas. Lembre-se das importações e de definir um tema:
1 import matplotlib . pyplot as plt
2 import seaborn as sns
3 import numpy as np
4 import pandas as pd # Seaborn e Pandas funcionam bem juntos
5 sns . set_theme ( style = " whitegrid " , palette = " bright " )

Código 19.31: Setup para exemplos Seaborn

Gráficos de Dispersão Aprimorados com [Link]()


[Link]() estende [Link]() com melhor integração com DataFrames
e mapeamento semântico de variáveis para cor (hue), tamanho (size) e estilo (style) dos
pontos.
19.3. Seaborn: Gráficos Estatísticos Mais Elaborados e Atraentes 367

1 # Criando dados de exemplo ( Pandas ser á visto no Cap . 20)


2 np . random . seed (50)
3 num_amostras = 80
4 dados_rel = pd . DataFrame ({
5 ’ Variavel_X ’: np . random . randn ( num_amostras ) * 5 ,
6 ’ Variavel_Y ’: 0.6 * np . random . randn ( num_amostras ) * 5 + 3 ,
7 ’ Tipo ’: np . random . choice ([ ’ T1 ’ , ’ T2 ’] , num_amostras ) ,
8 ’ Magnitude ’: np . random . rand ( num_amostras ) * 100 + 20
9 })
10 dados_rel . loc [ dados_rel [ ’ Tipo ’] == ’ T2 ’ , ’ Variavel_Y ’] +=
dados_rel [ ’ Variavel_X ’] * 0.4
11
12 plt . figure ( figsize =(10 , 6.5) )
13 sns . scatterplot ( data = dados_rel , x = ’ Variavel_X ’ , y = ’ Variavel_Y ’ ,
14 hue = ’ Tipo ’ , size = ’ Magnitude ’ , sizes =(25 , 250) ,
15 style = ’ Tipo ’ , palette = ’ Set1 ’ , alpha =0.7 , edgecolors = ’k ’ , lw =0.5)
16 plt . title ( " Dispers ã o Avan ç ada ( Seaborn ) " )
17 plt . xlabel ( " Vari á vel X " ) ; plt . ylabel ( " Vari á vel Y " )
18 plt . legend ( title = ’ Legenda ’ , bbox_to_anchor =(1.01 , 1) , loc = ’ upper
left ’)
19 plt . tight_layout ( rect =[0 , 0 , 0.83 , 1])
20 # plt . show () % Omitido para exemplos sequenciais

Código 19.32: Gráfico de dispersão avançado com [Link]()

Gráficos de Linha Aprimorados com [Link]()

[Link]() é forte para múltiplas observações de Y por X, mostrando tendência


central (ex: média) e intervalo de confiança (parâmetro errorbar).
1 # Dados de exemplo para lineplot
2 np . random . seed (80)
3 tempo = np . tile ( np . arange (1 , 8) , 2 * 10) # 7 pontos de tempo , 10
leituras por ponto para 2 grupos
4 grupos = np . repeat ([ ’ Sensor_A ’ , ’ Sensor_B ’] , 7 * 10)
5 leituras = []
6 for i in range (2) : # Para cada grupo
7 tend_base = ( i + 0.5) + np . arange (1 , 8) * (0.3 + i * 0.15)
8 for _ in range (10) :
9 leituras . extend ( tend_base + np . random . normal (0 , 0.3 + i *0.2 , 7) )
10
11 df_linhas = pd . DataFrame ({ ’ Tempo ’: tempo , ’ Sensor ’: grupos ,
’ Leitura ’: leituras })
12
13 plt . figure ( figsize =(9 , 5.5) )
14 sns . lineplot ( data = df_linhas , x = ’ Tempo ’ , y = ’ Leitura ’ , hue = ’ Sensor ’ ,
15 style = ’ Sensor ’ , markers = True , dashes = False , errorbar =( ’ ci ’ , 95) )
16 plt . title ( ’ Leituras de Sensores ao Longo do Tempo ( IC 95%) ’)
17 plt . xlabel ( ’ Ponto no Tempo ’) ; plt . ylabel ( ’ Valor da Leitura ’)
18 plt . legend ( title = ’ Sensor ’) ; plt . grid ( True , linestyle = ’: ’ , alpha =0.6)
19 # plt . show ()

Código 19.33: Gráfico de linha com Seaborn, hue e intervalo de confiança


368 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

[Link](): Figura de Nível Superior para Relações


[Link]() (relational plot) é uma função de nível de figura para scatterplot
(padrão) ou lineplot (kind="line"). Facilita subplots com col e row.
1 # Usando ’ dados_rel ’ do exemplo do scatterplot
2 # Adicionando uma coluna ’ Ambiente ’
3 np . random . seed (55)
4 dados_rel [ ’ Ambiente ’] = np . random . choice ([ ’ Lab X ’ , ’ Lab Y ’] ,
num_amostras )
5
6 g = sns . relplot (
7 data = dados_rel , x = ’ Variavel_X ’ , y = ’ Variavel_Y ’ ,
8 hue = ’ Tipo ’ , size = ’ Magnitude ’ , sizes =(20 , 200) , style = ’ Tipo ’ ,
9 col = ’ Ambiente ’ , # Colunas de subplots por Ambiente
10 kind = " scatter " , palette = ’ muted ’ , alpha =0.8 , height =3.5 , aspect =1.1
11 )
12 g . fig . suptitle ( " Rela ç ã o X e Y por Tipo e Ambiente ( sns . relplot ) " ,
y =1.04 , fontsize =14)
13 g . set_axis_labels ( " Eixo X ( Medida A ) " , " Eixo Y ( Medida B ) " )
14 plt . show () # Exibe todos os gr á ficos desta subse ç ã o

Código 19.34: Usando [Link]() para múltiplos subplots de dispersão


Seaborn simplifica a criação de gráficos de relação complexos e informativos.

19.3.5 Mapas de Calor (Heatmaps - [Link]())


Um mapa de calor (heatmap) é uma representação gráfica de dados matriciais onde
os valores são representados como cores. Células com valores maiores podem ter cores
mais intensas, e vice-versa. São úteis para identificar padrões em dados tabulares 2D,
como matrizes de correlação. A função principal no Seaborn é [Link]().
Criando um Mapa de Calor Básico com [Link]()
Sintaxe: [Link](data, annot=False, fmt=".2g", cmap=None, linewidths=0,
...)

• data: Array NumPy 2D ou DataFrame Pandas.

• annot=True: Escreve os valores dos dados em cada célula.

• fmt: String de formatação para as anotações (ex: ".2f").

• cmap: Mapa de cores (ex: "viridis", "coolwarm").

• linewidths, linecolor: Para linhas entre as células.

• cbar=True: Desenha uma barra de cores (legenda).

• Exemplo 1: Mapa de Calor de Dados Aleatórios


1 import matplotlib . pyplot as plt
2 import seaborn as sns
3 import numpy as np
4 import pandas as pd
5
6 sns . set_theme ( style = " white " )
7
19.3. Seaborn: Gráficos Estatísticos Mais Elaborados e Atraentes 369

8 np . random . seed (0)


9 dados_heatmap = np . random . rand (7 , 10) * 50
10 dias = [ " Seg " , " Ter " , " Qua " , " Qui " , " Sex " , " Sab " , " Dom " ]
11 horas = [ f " { h } h " for h in range (8 , 18) ]
12 df_heatmap = pd . DataFrame ( dados_heatmap , index = dias ,
columns = horas )
13
14 plt . figure ( figsize =(11 , 5.5) )
15 sns . heatmap ( df_heatmap , annot = True , fmt = " .0 f " ,
cmap = " YlGnBu " ,
16 linewidths =.5 , linecolor = ’ gray ’ , cbar_kws ={ ’ label ’:
’ Intensidade ’ })
17 plt . title ( " Mapa de Calor : Atividade por Dia e Hora " ,
fontsize =15)
18 plt . xlabel ( " Hora do Dia " ) ; plt . ylabel ( " Dia da Semana " )
19 plt . xticks ( rotation =30 , ha = ’ right ’) ; plt . yticks ( rotation =0)
20 plt . tight_layout ()
21 # plt . show () % Omitido para exemplos sequenciais

Código 19.35: Mapa de calor de dados aleatórios com Seaborn

• Exemplo 2: Visualizando uma Matriz de Correlação


1 # Supondo plt , sns , np , pd importados e tema definido
2 np . random . seed (42)
3 dados_corr = pd . DataFrame ( np . random . rand (50 , 4) ,
4 columns =[ ’ Temp_A ’ , ’ Pressao_B ’ , ’ Fluxo_C ’ , ’ Vibracao_D ’ ])
5 # Induzindo algumas correla ç õ es
6 dados_corr [ ’ Temp_A ’] = dados_corr [ ’ Temp_A ’] +
dados_corr [ ’ Pressao_B ’] * 0.5
7 dados_corr [ ’ Fluxo_C ’] = dados_corr [ ’ Fluxo_C ’] -
dados_corr [ ’ Temp_A ’] * 0.3
8
9 m a t r i z _d e _ c o r r e l a c a o = dados_corr . corr () # M é todo . corr ()
do Pandas
10
11 plt . figure ( figsize =(7 , 5.5) )
12 sns . heatmap ( matriz_de_correlacao , annot = True , fmt = " .2 f " ,
cmap = " coolwarm " ,
13 center =0 , linewidths =.5 , linecolor = ’ lightgray ’ ,
14 cbar_kws ={ ’ label ’: ’ Coeficiente de Correla ç ã o ’ })
15 plt . title ( " Mapa de Calor da Matriz de Correla ç ã o " ,
fontsize =15)
16 plt . xticks ( rotation =45 , ha = ’ right ’) ; plt . yticks ( rotation =0)
17 plt . tight_layout ()
18 plt . show () # Exibe os dois heatmaps

Código 19.36: Mapa de calor de uma matriz de correlação

Personalizando Mapas de Calor:


• Mapas de Cores (cmap): Escolha sequenciais (ex: "viridis") para dados de
baixo para alto, ou divergentes (ex: "coolwarm") para dados com um ponto central
(como correlações em torno de 0).
• Anotações (annot, fmt): Mostrar valores nas células pode ser útil, mas pode poluir
se houver muitas.
Mapas de calor são uma forma poderosa de representar dados matriciais visualmente.
370 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

19.3.6 Personalizando Estilos e Temas no Seaborn


Uma grande vantagem do Seaborn é a sua capacidade de produzir gráficos esteticamente
agradáveis com configurações padrão inteligentes, além de oferecer controle fácil sobre a
aparência geral através de temas, estilos e paletas de cores.

Definindo o Tema Geral com sns.set_theme()


Esta é a função principal para definir vários aspectos da aparência.
Sintaxe: sns.set_theme(style=None, palette=None, font_scale=1, ...)

• style: Estilo de fundo e eixos (ex: "whitegrid", "darkgrid", "ticks").

• palette: Paleta de cores padrão (ex: "pastel", "muted", "viridis").

• font_scale: Escala o tamanho de todos os elementos de texto.

sns.set_theme() afeta todos os gráficos Seaborn e Matplotlib subsequentes.


1 import matplotlib . pyplot as plt
2 import seaborn as sns
3 import numpy as np
4
5 x_ex = np . linspace (0 , 10 , 50)
6 y1_ex = np . sin ( x_ex )
7 y2_ex = np . cos ( x_ex )
8
9 # Aplicando um tema espec í fico
10 sns . set_theme ( style = " darkgrid " , palette = " Set2 " , font_scale =1.1)
11
12 plt . figure ( figsize =(8 ,4) )
13 plt . plot ( x_ex , y1_ex , marker = ’. ’ , label = ’ sin ( x ) ’)
14 plt . plot ( x_ex , y2_ex , marker = ’x ’ , label = ’ cos ( x ) ’)
15 plt . title ( " Gr á fico com Tema Seaborn Personalizado " )
16 plt . legend ()
17 # plt . show () % Omitido para exemplos sequenciais
18 # sns . set_theme () # Para resetar ao padr ã o Seaborn depois , se
necess á rio

Código 19.37: Aplicando um tema Seaborn com set_theme()

Controle Separado com sns.set_style(), sns.set_palette(), sns.set_context()


• sns.set_style(nome_estilo, ...): Controla o estilo dos eixos, grade, fundo.

• sns.set_palette(nome_paleta, ...): Controla a paleta de cores padrão.

• sns.set_context(nome_contexto, ...): Ajusta a escala dos elementos para dife-


rentes usos ("paper", "notebook", "talk", "poster").

1 # Supondo plt , sns , np e x_ex definidos


2 sns . set_style ( " ticks " )
3 sns . set_palette ( " husl " , 6)
4
5 plt . figure ( figsize =(7 ,4) )
6 for i in range (4) :
19.4. Dicas para Boas Visualizações: Comunicando com Clareza e Impacto 371

7 plt . plot ( x_ex , np . cos ( x_ex + i * np . pi / 4) , label = f ’ Curva


{ i +1} ’)
8 plt . title ( " Estilo ’ ticks ’ e Paleta ’ husl ’" )
9 plt . legend ( ncol =2)
10 plt . show ()
11 # sns . set_theme () # Resetar

Código 19.38: Usando set_style e set_palette separadamente

(O código acima é um exemplo conceitual de como seriam usados.)

Usando Estilos Temporariamente com Gerenciadores de Contexto


Para aplicar um estilo apenas a um gráfico específico, use with sns.axes_style(...)
ou with sns.color_palette(...).
1 # Supondo plt e sns importados
2 x_b = [ ’X ’ , ’Y ’ , ’Z ’ ]; y_b = [5 , 12 , 8]
3 sns . set_theme ( style = " whitegrid " , palette = " pastel " ) # Tema padr ã o
4
5 plt . figure ( figsize =(10 , 4) )
6 plt . subplot (1 , 2 , 1)
7 sns . barplot ( x = x_b , y = y_b )
8 plt . title ( " Estilo Padr ã o Ativo " )
9

10 with sns . axes_style ( " darkgrid " ) : # Estilo tempor á rio


11 with sns . color_palette ( " Set1 " , 3) : # Paleta tempor á ria
12 plt . subplot (1 , 2 , 2)
13 sns . barplot ( x = x_b , y = y_b )
14 plt . title ( " Estilo Tempor á rio " )
15

16 plt . tight_layout () ; plt . show ()

Código 19.39: Aplicando estilo e paleta temporariamente com with

Escolhendo Paletas de Cores (palette)


• Qualitativas: Para distinguir categorias discretas (ex: "Set1", "pastel").

• Sequenciais: Para dados numéricos de baixo para alto (ex: "Blues", "viridis").

• Divergentes: Para dados com um ponto central significativo (ex: "coolwarm",


"RdBu_r").

O Seaborn simplifica a criação de gráficos estatísticos atraentes e oferece controle sobre


sua aparência.

19.4 Dicas para Boas Visualizações: Comunicando com


Clareza e Impacto
Criar um gráfico funcional é apenas o primeiro passo. Para que uma visualização seja
eficaz, ela precisa ser clara, precisa e comunicar a mensagem desejada de forma eficiente.
Um gráfico mal projetado pode confundir ou distorcer a informação.
372 Capítulo 19. Matplotlib e Seaborn para Visualização de Dados

1. Escolha o Tipo de Gráfico Certo: Adeque o gráfico aos seus dados e à mensagem
que quer transmitir (linha para tendências, barra para comparações categóricas,
dispersão para relações, histograma para distribuições, etc.).

2. Clareza Acima de Tudo:

• Título Informativo: Descreva o que o gráfico representa.


• Rótulos de Eixos Claros e com Unidades: Eixos X e Y (e Z) devem ser
inequívocos, incluindo unidades de medida (ex: “Tempo (s)”, “Temperatura
(°C)”).
• Legendas Necessárias: Identifique múltiplas séries de dados.
• Fontes Legíveis: Tamanhos adequados para fácil leitura.

3. Simplicidade e Foco: Evite a Poluição Visual (“Chartjunk”):

• Remova elementos que não adicionam informação útil (grades excessivas, fundos
chamativos).
• Cuidado com gráficos 3D desnecessários, que podem distorcer a percepção.
• Use cores de forma significativa e consistente, não apenas para decorar.

4. Honestidade e Precisão na Representação dos Dados:

• Eixos de gráficos de barras devem, geralmente, começar em zero.


• Use escalas apropriadas e não distorça a percepção dos dados.
• Não oculte dados relevantes.

5. Considere a Acessibilidade:

• Use paletas de cores distinguíveis por daltônicos. Garanta contraste.


• Textos e marcadores devem ter tamanho suficiente.
• Considere texto alternativo para gráficos em contextos digitais.

6. Conheça seu Público e o Contexto: Adapte a complexidade e o estilo visual ao


público e ao meio de apresentação.

7. Iterar e Pedir Feedback: Revise e peça opiniões para refinar suas visualizações.

Boas visualizações combinam técnica, princípios de design e um entendimento claro dos


dados e da mensagem.

Concluindo o Capítulo sobre Matplotlib e Seaborn


Neste capítulo, mergulhamos no mundo da visualização de dados com Python, focando
em duas de suas bibliotecas mais importantes: Matplotlib e Seaborn.
Começamos com o Matplotlib, a biblioteca fundamental, aprendendo a usar sua
interface pyplot para criar e personalizar gráficos de linha, dispersão, barras, histogramas
e pizza, além de trabalhar com subplots e salvar nossas visualizações.
Em seguida, exploramos o Seaborn, construído sobre o Matplotlib, que simplifica a
criação de gráficos estatísticos mais elaborados e esteticamente agradáveis. Vimos como
19.4. Dicas para Boas Visualizações: Comunicando com Clareza e Impacto 373

visualizar distribuições (com histogramas aprimorados, KDEs, box plots, violin plots),
dados categóricos, relações entre variáveis e mapas de calor, além de personalizar temas e
estilos.
Finalmente, discutimos dicas essenciais para criar boas visualizações, enfatizando
clareza, simplicidade e honestidade na representação dos dados.
A visualização de dados é tanto uma ciência quanto uma arte. As ferramentas que o
Python oferece são incrivelmente poderosas. Com a prática, você desenvolverá a habilidade
de escolher as melhores formas de visualizar seus dados para extrair insights valiosos e
comunicar suas descobertas de forma eficaz.
No próximo capítulo, continuaremos nossa exploração de bibliotecas essenciais, mer-
gulhando no Pandas, uma biblioteca fundamental para manipulação e análise de dados
tabulares, com a qual o Seaborn se integra de maneira muito natural.
Capítulo 20

Pandas para Análise e Manipulação de


Dados

Nos capítulos anteriores desta parte, exploramos o NumPy para computação numérica e
o Matplotlib/Seaborn para visualização. No entanto, muitos dados do mundo real vêm em
formatos tabulares – como planilhas ou tabelas de banco de dados. Lidar com esses dados,
limpá-los, transformá-los e analisá-los pode ser complexo usando apenas as ferramentas
vistas até agora. É aqui que a biblioteca Pandas se torna essencial.

20.1 Introdução ao Pandas: Estruturas de Dados Pode-


rosas para Análise
O que é Pandas?
Pandas (de Panel Data e Python Data Analysis Library) é uma biblioteca de código
aberto, de alto desempenho e fácil de usar, construída sobre o NumPy. É a ferramenta
padrão em Python para manipulação e análise de dados estruturados ou tabulares.

Por que Usar Pandas?


Pandas oferece vantagens significativas para trabalhar com dados tabulares:

• Estruturas de Dados Flexíveis: Introduz Series (1D) e DataFrame (2D).

• Manuseio Eficiente de Dados Tabulares: Permite carregar, limpar, transformar,


filtrar e agrupar dados de diversos formatos.

• Tratamento Integrado de Dados Ausentes (NaN): Funcionalidades para encon-


trar, remover ou preencher dados faltantes.

• Indexação e Seleção Poderosas: Seleção flexível por rótulos de linha/coluna e


posições.

• Operações de Agrupamento e Agregação (Group By ).

• Funcionalidades para Séries Temporais.

• Boa Integração com Outras Bibliotecas: NumPy, Matplotlib, Seaborn.

374
20.2. Introdução ao Pandas Series: Arrays Unidimensionais Rotulados 375

As Duas Estruturas de Dados Principais: Series e DataFrame


1. Series (Série): Array unidimensional (1D) com um índice associado (rótulos para
cada elemento). Similar a uma coluna de uma planilha.
2. DataFrame (Quadro de Dados): Estrutura bidimensional (2D), tabular, com
linhas e colunas rotuladas. Similar a uma planilha ou tabela SQL. Cada coluna de
um DataFrame é uma Series.

Relação com NumPy


Pandas é construído sobre o NumPy. Os dados em Series e DataFrames são, em
grande parte, armazenados internamente como arrays NumPy, permitindo que Pandas
aproveite a performance do NumPy e adicione a flexibilidade dos rótulos.

Instalação e Convenção de Importação


Se não estiver instalado (comum em distribuições como Anaconda), instale via pip:
pip install pandas
A convenção universal para importar Pandas é:
1 import pandas as pd
2 import numpy as np # Frequentemente usado em conjunto

Código 20.1: Importando Pandas e NumPy


Neste capítulo, exploraremos como criar e usar Series e DataFrames, selecionar dados,
lidar com dados ausentes, realizar operações comuns, agrupar dados e interagir com
arquivos.

20.2 Introdução ao Pandas Series: Arrays Unidimensio-


nais Rotulados
A Series é uma das duas estruturas de dados fundamentais do Pandas. Pense nela
como um array unidimensional (1D), similar a um array NumPy, mas com **rótulos de
eixo**, que chamamos de índice (index).
Imagine uma única coluna em uma planilha: os dados nessa coluna formariam o
conteúdo da Series, e os rótulos das linhas (sejam eles números, nomes, datas, etc.)
formariam o índice dessa Series.

O que é uma Pandas Series?


Uma Series do Pandas é uma estrutura de dados que:
• Contém uma sequência de valores (os dados em si).
• Possui um índice associado que rotula cada um desses valores.
• Os dados podem ser de qualquer tipo Python (int, float, str, bool, objetos Python,
etc.), mas geralmente uma Series contém dados de um tipo homogêneo para melhor
performance e consistência.
376 Capítulo 20. Pandas para Análise e Manipulação de Dados

• O índice também é um objeto tipo array e pode conter números, strings, datas, etc.
Os rótulos do índice não precisam ser únicos, mas muitas funcionalidades do Pandas
se beneficiam de índices únicos.

Essa combinação de dados e um índice explícito torna a Series muito mais flexível e
poderosa para análise do que um simples array NumPy unidimensional.

Criando uma Series


Use o construtor [Link](data=None, index=None, dtype=None, name=None).
Lembre-se de import pandas as pd.

1. A partir de uma Lista Python: Se o índice não for especificado, um numérico


padrão (0, 1, 2,...) é criado.
1 import pandas as pd
2 import numpy as np # Para alguns exemplos e porque Pandas
usa NumPy
3

4 # Criando uma Series a partir de uma lista


5 d a d o s _ p o p u l a c a o _ l i s t a = [350000 , 12000000 , 2500000 ,
2900000] # Popula ç õ es aproximadas
6 serie _pop_sim ples = pd . Series ( d a d o s _ p o p u l a c a o _ l i s t a )
7

8 print ( " --- Series a partir de lista ( í ndice padr ã o ) ---" )


9 print ( serie_p op_simple s )
10 # Sa í da :
11 # 0 350000
12 # 1 12000000
13 # 2 2500000
14 # 3 2900000
15 # dtype : int64

Código 20.2: Criando uma Series a partir de uma lista

2. A partir de um Array NumPy: Funciona de forma similar a criar a partir de


uma lista.
1 array_np_temp = np . array ([22.5 , 23.1 , 21.9])
2 serie_temp_np = pd . Series ( array_np_temp )
3 print ( " \n - - - Series de array NumPy ---" )
4 print ( serie_temp_np )

Código 20.3: Criando Series de array NumPy

3. Especificando um Índice (index): Você pode fornecer explicitamente os rótulos


para o índice usando o parâmetro index. O index deve ter o mesmo número de
elementos que os dados.
1 dados_alturas_m = [1.75 , 1.80 , 1.68 , 1.92]
2 nomes_individuos = [ " Ana " , " Bruno " , " Carla " , " Daniel " ]
3
4 s e r i e _ a l t u r a s _ c o m _ i d x = pd . Series ( data = dados_alturas_m ,
index = nomes_individuos )
5 print ( " \n - - - Series com í ndice personalizado ( nomes ) ---" )
6 print ( s e r i e _ a l t u r a s _ c o m _ i d x )
20.2. Introdução ao Pandas Series: Arrays Unidimensionais Rotulados 377

7 # Sa í da :
8 # Ana 1.75
9 # Bruno 1.80
10 # Carla 1.68
11 # Daniel 1.92
12 # dtype : float64

Código 20.4: Series com índice personalizado

4. A partir de um Dicionário Python: Se você criar uma Series a partir de


um dicionário, as chaves do dicionário se tornarão o índice da Series, e os valores
do dicionário se tornarão os dados da Series. A ordem dos elementos na Series
corresponderá à ordem de inserção das chaves no dicionário (para Python 3.7+).
1 d a d o s _ d i c i o n a r i o _ n o t a s = { " C á lculo I " : 8.5 , " F í sica A " :
7.0 , " Algoritmos " : 9.2 , " Qu í mica Geral " : 6.5}
2 se r ie _ no t a s_ d e_ d i ct = pd . Series ( d a d o s _ d i c i o n a r i o _ n o t a s )
3 print ( " \n - - - Series a partir de dicion á rio ---" )
4 print ( s e ri e _n o t as _ de _ d ic t )
5
6 # Se voc ê passar um ’ index ’ expl í cito ao criar a Series de
um dicion á rio :
7 # - Pandas alinhar á os dados do dicion á rio com esse í ndice .
8 # - Para r ó tulos no ’ index ’ que n ã o s ã o chaves no
dicion á rio , o valor ser á NaN ( Not a Number - representa
dado ausente ) .
9 # - Chaves do dicion á rio que n ã o est ã o no ’ index ’
expl í cito ser ã o ignoradas .
10 i n d i c e _ d i s c i p l i n a s _ d e s e j a d o = [ " Algoritmos " , " C á lculo I " ,
" Mec â nica dos Fluidos " , " F í sica A " ]
11 serie_notas_com_indice_expl =
pd . Series ( dados_dicionario_notas ,
index = i n d i c e _ d i s c i p l i n a s _ d e s e j a d o )
12 print ( " \n - - - Series a partir de dicion á rio com í ndice
expl í cito ( e reordenado ) ---" )
13 print ( s e r i e _ n o t a s _ c o m _ i n d i c e _ e x p l )
14 # Sa í da ( Mec â nica dos Fluidos ter á NaN , Qu í mica Geral ser á
omitida ) :
15 # Algoritmos 9.2
16 # C á lculo I 8.5
17 # Mec â nica dos Fluidos NaN
18 # F í sica A 7.0
19 # dtype : float64

Código 20.5: Series a partir de dicionário

5. Especificando um Nome para a Series (name): Você pode dar um nome à


sua Series usando o parâmetro name. Isso é especialmente útil quando a Series
representa uma coluna que eventualmente fará parte de um DataFrame.
1 serie_nomeada = pd . Series ([1 ,2 ,3] , name = " Contador " )
2 print ( f " \n - - - Series com nome ’{ serie_nomeada . name } ’ ---" )
3 print ( serie_nomeada )

Código 20.6: Series com nome


378 Capítulo 20. Pandas para Análise e Manipulação de Dados

Acessando Dados em uma Series


• Por Rótulo do Índice: minha_serie[’rotulo’]. Gera KeyError se não existir.

• Por Posição Inteira (com .iloc[]): minha_serie.iloc[posicao]. Evita ambi-


guidade.

• Fatiamento:

– Com rótulos: minha_serie[’inicio’:’fim’] (fim é inclusivo).


– Com posições (.iloc[]): minha_serie.iloc[inicio:fim] (fim é exclusivo).

• Indexação Booleana: minha_serie[minha_serie > condicao].

1 # Usando serie _alturas_ idx : Ana :1.75 , Bruno :1.80 , Carla :1.68
2 serie_alt uras_idx = pd . Series ([1.75 , 1.80 , 1.68] , index =[ " Ana " ,
" Bruno " , " Carla " ])
3 print ( f " \ nAltura da Ana ( por r ó tulo ) : { ser ie_altur as_idx [ ’ Ana ’]} " )
4 print ( f " Primeira altura ( por . iloc [0]) :
{ serie_a lturas_id x . iloc [0]} " )
5 print ( f " Alturas de Ana a Bruno ( fatia por
r ó tulo ) :\ n { ser ie_altur as_idx [ ’ Ana ’: ’ Bruno ’]} " )
6 print ( f " Alturas nas posi ç õ es 0 e 1 ( fatia por
. iloc ) :\ n { ser ie_altur as_idx . iloc [0:2]} " )
7 print ( f " Alturas > 1.70:\ n { ser ie_altura s_idx [ se rie_altu ras_idx >
1.70]} " )

Código 20.7: Acessando dados em uma Pandas Series

Atributos Importantes de uma Series


Uma Series possui vários atributos úteis que fornecem informações sobre ela:

• [Link]: Retorna os dados da Series como um array NumPy.

• [Link]: Retorna o objeto índice ([Link]) da Series.

• [Link]: Retorna o tipo de dado (dtype NumPy) dos elementos.

• [Link]: Retorna o nome da Series (se ti er sido definido).

• [Link]: O número total de elementos na Series.

• [Link]: Uma tupla representando a forma da Series (ex.: (5,) para 5


elementos).

• [Link]: Retorna True se a Series contiver pelo menos um valor NaN (Not
a Number, dado ausente).

1 # Usando serie _alturas_ idx


2 print ( f " \n - - - Atributos da s e r i e _ a l t u r a s _ c o m _ i d x ---" )
3 print ( f " Valores ( como array NumPy ) : { s e r i e _ a l t u r a s _ c o m _ i d x . values } " )
4 print ( f " Í ndice : { s e r i e _ a l t u r a s _ c o m _ i n d i c e . index } " )
5 print ( f " Tipo de dado ( dtype ) : { s e r i e _ a l t u r a s _ c o m _ i d x . dtype } " )
20.3. Pandas DataFrame: Estruturas de Dados Tabulares Bidimensionais 379

6 print ( f " Nome da Series : { s e r i e _ a l t u r a s _ c o m _ i d x . name } " ) # N ã o


definimos , ser á None
7 print ( f " Tamanho ( size ) : { s e r i e _ a l t u r a s _ c o m _ i d x . size } " )
8 print ( f " Forma ( shape ) : { s e r i e _ a l t u r a s _ c o m _ i d x . shape } " )
9 print ( f " Cont é m NaN ? { s e r i e _ a l t u r a s _ c o m _ i d x . hasnans } " )

Código 20.8: Alguns atributos de uma Series

Operações Vetorizadas e Funções NumPy


Series suportam operações vetorizadas (elemento a elemento) e a maioria das ufuncs
NumPy, com alinhamento de índice e preservação do índice.
1 serie_num = pd . Series ([1 , 4 , 9] , index =[ ’a ’ , ’b ’ , ’c ’ ])
2 print ( f " \ nS é rie original :\ n { serie_num } " )
3 print ( f " Raiz quadrada da s é rie :\ n { np . sqrt ( serie_num ) } " )
4 print ( f " S é rie + 100:\ n { serie_num + 100} " )

Código 20.9: Operações vetorizadas em Series

Verificação de Valores Nulos (NaN)


[Link]() (ou isna()) retorna uma Series booleana; [Link]() é o
oposto. [Link]().sum() conta os nulos.
1 # Usando s e r i e _n o t a s _ i d x _ e x p l que tem NaN para ’ Mec â nica ’
2 idx_desejado = [ " Python " , " C á lculo " , " Mec â nica " , " F í sica " ]
3 dados_dict_notas = { " C á lculo " : 8.5 , " F í sica " : 7.0 , " Python " : 9.2}
4 s e r i e _ n o t a s _ i d x _ e x p l = pd . Series ( dados_dict_notas ,
index = idx_desejado )
5
6 print ( f " \n - - - Verificando nulos na s é rie de notas ---" )
7 print ( s e r i e _ n o t a s _ i dx _ e x p l )
8 print ( f " É nulo ?:\ n { s e r i e _ n o t a s _ i d x _ e x p l . isnull () } " )
9 print ( f " Contagem de nulos : { s e r i e _ no t a s _ i d x _ e x p l . isnull () . sum () } " )

Código 20.10: Verificando valores nulos em Series


As Series são os blocos de construção unidimensionais do Pandas, combinando arrays
com índices rotulados.

20.3 Pandas DataFrame: Estruturas de Dados Tabula-


res Bidimensionais
Se a Series do Pandas é como uma única coluna de uma planilha ou um vetor rotulado,
o DataFrame é a estrutura de dados que representa a planilha inteira, uma tabela de banco
de dados, ou qualquer tipo de dado tabular bidimensional. É a estrutura de dados mais
utilizada e central da biblioteca Pandas.

O que é um Pandas DataFrame?


Um DataFrame é uma estrutura de dados 2D, semelhante a uma tabela, que consiste
em:
380 Capítulo 20. Pandas para Análise e Manipulação de Dados

• Dados: Organizados em linhas e colunas.

• Índice de Linha (index): Rótulos para cada linha (similar ao índice de uma Series).

• Nomes de Coluna (columns): Rótulos para cada coluna.

Principais Características:

• Bidimensional: Possui linhas e colunas.

• Tamanho Mutável: Você pode adicionar ou remover linhas e colunas.

• Dados Heterogêneos: As colunas podem ter tipos de dados diferentes (ex: uma
coluna de nomes pode ser string, uma de idades pode ser int, uma de preços pode
ser float). No entanto, dentro de uma mesma coluna, os dados geralmente são de
um tipo homogêneo.

• Eixos Rotulados: Tanto as linhas quanto as colunas possuem rótulos, permitindo


um acesso e manipulação de dados mais semântico e flexível.

• Cada Coluna é uma Series: Você pode pensar em um DataFrame como um dicionário
de objetos Series, onde as chaves do dicionário são os nomes das colunas e os valores
são as Series correspondentes. Todas essas Series (colunas) compartilham o mesmo
índice de linha.

Criando um DataFrame
Existem várias maneiras de criar um DataFrame em Pandas. A função construtora
principal é [Link]().
Sintaxe geral:
[Link](data=None, index=None, columns=None, dtype=None, ...)

1. A partir de um Dicionário de Listas ou Arrays NumPy:


Esta é uma das formas mais comuns. As chaves do dicionário se tornam os nomes
das colunas, e as listas (ou arrays NumPy) associadas a cada chave se tornam os
dados das colunas. Todas as listas/arrays devem ter o mesmo comprimento.
1 import pandas as pd
2 import numpy as np # Para NaN e arrays
3
4 dados_df_dict = {
5 ’ Nome ’: [ ’ Mariana ’ , ’ Lucas ’ , ’ Beatriz ’ , ’ Tiago ’] ,
6 ’ Idade ’: [28 , 34 , 29 , 42] ,
7 ’ Cidade ’: [ ’S ã o Paulo ’ , ’ Recife ’ , ’ BH ’ , ’ POA ’] ,
8 ’ Pontos ’: [8.5 , 7.9 , 9.2 , np . nan ] # np . nan para valor
ausente
9 }
10 df_pessoas = pd . DataFrame ( dados_df_dict )
11 print ( " --- DataFrame de dicion á rio de listas ---" )
12 print ( df_pessoas )

Código 20.11: Criando DataFrame de dicionário de listas


20.3. Pandas DataFrame: Estruturas de Dados Tabulares Bidimensionais 381

2. A partir de uma Lista de Dicionários Python:


Cada dicionário na lista representa uma linha do DataFrame. As chaves dos di-
cionários se tornam os nomes das colunas. O Pandas alinhará os dados: se um
dicionário não tiver uma chave que outros têm, o valor para aquela célula na linha
correspondente será NaN.
1 lista_de_registros_sensores = [
2 { ’ sensor_id ’: ’ S001 ’ , ’ temperatura_C ’: 25.5 ,
’ umidade_perc ’: 60} ,
3 { ’ sensor_id ’: ’ S002 ’ , ’ temperatura_C ’: 26.1 ,
’ pressao_hPa ’: 1012} , # umidade_perc ausente
4 { ’ sensor_id ’: ’ S003 ’ , ’ temperatura_C ’: 24.9 ,
’ umidade_perc ’: 62 , ’ pressao_hPa ’: 1010}
5 ]
6 df_sensores = pd . DataFrame ( l i s t a _ d e _ r e g i s t r o s _ s e n s o r e s )
7
8 print ( " \n - - - DataFrame criado a partir de uma lista de
dicion á rios ---" )
9 print ( df_sensores )

Código 20.12: Criando DataFrame de lista de dicionários

Observe que a coluna umidade_perc terá NaN para o sensor S002, e a coluna
pressao_hPa terá NaN para o sensor S001.

3. A partir de um Array NumPy 2D:


Você pode passar um array NumPy 2D diretamente para o construtor. Opcionalmente,
pode fornecer os nomes das colunas com o parâmetro columns e os rótulos do índice
com o parâmetro index. Se não fornecidos, serão usados índices numéricos padrão
(0, 1, 2,...).
1 dados_matriz_np = np . array ([
2 [10 , 11 , 12] ,
3 [20 , 21 , 22] ,
4 [30 , 31 , 32] ,
5 [40 , 41 , 42]
6 ])
7 nomes_colunas = [ ’ ComponenteX ’ , ’ ComponenteY ’ ,
’ ComponenteZ ’]
8 rotulos_linhas = [ ’ Amostra1 ’ , ’ Amostra2 ’ , ’ Amostra3 ’ ,
’ Amostra4 ’]
9
10 df_de _array_nu mpy = pd . DataFrame ( data = dados_matriz_np ,
11 index = rotulos_linhas ,
12 columns = nomes_colunas )
13 print ( " \n - - - DataFrame criado a partir de um array NumPy
2 D ---" )
14 print ( df_de_ar ray_nump y )

Código 20.13: Criando DataFrame de array NumPy 2D

4. A partir de uma Series Pandas:


Se você passar uma Series para [Link](), ela se tornará uma coluna única
no DataFrame. O nome da series (se definido) será usado como coluna.
1 s e r i e _ v e n d a s _ p r o d u t o _ A = pd . Series ([100 , 120 , 90 , 110] ,
2 index =[ ’ Sem1 ’ , ’ Sem2 ’ , ’ Sem3 ’ , ’ Sem4 ’] ,
382 Capítulo 20. Pandas para Análise e Manipulação de Dados

3 name = ’ Vendas Produto A ’)


4 df _ ve n da s _ pr o du t o _A = pd . DataFrame ( s e r i e _ v e n d a s _ p r o d u t o _ A )
5 print ( " \n - - - DataFrame criado a partir de uma Series ---" )
6 print ( d f _v e nd a s _p r od u t o_ A )

Código 20.14: DataFrame de uma Series

5. Criando um DataFrame Vazio:


Você pode criar um DataFrame vazio e depois adicioanr colunas e dados a ele.
1 df_vazio_inicial = pd . DataFrame ()
2 print ( f " \ nDataFrame vazio inicial : \ n { df_vazio_inicial } " )
3
4 # Ou especificando colunas na cria ç ã o ( ainda sem dados )
5 d f _ v a z io _ c o m _ c o l u n a s =
pd . DataFrame ( columns =[ ’ Nome_Projeto ’ , ’ Responsavel ’ ,
’ Status ’ ])
6 print ( f " \ nDataFrame vazio com colunas definidas :
\n{ df_vazio_com_colunas }")

Código 20.15: Criano DataFrame vazio

Atributos Importantes de um DataFrame


Assim como as Series, os DataFrame possuem atributos que nos dão informações
sobre sua estrutura:

• [Link]: O objeto Index para os rótulos das linhas.

• [Link]: O objeto INdex para os nomes das colunas.

• [Link]: Retorna uma Series contendo o tipo de dado (dtype) de cada coluna.

• [Link]: Uma tupla representando as dimensões do DataFrame

• [Link]: O número total de elementos no DataFrame.

• [Link]: O número de dimensões (sempre 2 para um DataFrame).

• [Link]: Retorna a representação NumPy 2D dos dados do DataFrame.

1 df_pessoas = pd . DataFrame ({
2 ’ Nome ’: [ ’ Mariana ’ , ’ Lucas ’ , ’ Beatriz ’ , ’ Tiago ’ , ’ Sofia ’] ,
3 ’ Idade ’: [28 , 34 , 29 , 42 , 25] ,
4 ’ Cidade ’: [ ’S ã o Paulo ’ , ’ Recife ’ , ’ Belo Horizonte ’ , ’ Porto
Alegre ’ , ’ Manaus ’] ,
5 ’ PontuacaoTeste ’: [88.5 , 76.0 , 91.2 , np . nan , 85.0]
6 })
7 print ( " \n - - - Atributos do DataFrame ’ df_pessoas ’ ---" )
8 print ( f " Í ndice das linhas ( df . index ) : { df_pessoas . index } " )
9 print ( f " Nomes das colunas ( df . columns ) : { df_pessoas . columns } " )
10 print ( f " Tipos de dados por coluna
( df . dtypes ) :\ n { df_pessoas . dtypes } " )
11 print ( f " Forma ( linhas , colunas ) ( df . shape ) : { df_pessoas . shape } " )
12 print ( f " N ú mero total de elementos ( df . size ) : { df_pessoas . size } " )
13 print ( f " N ú mero de dimens õ es ( df . ndim ) : { df_pessoas . ndim } " )
20.3. Pandas DataFrame: Estruturas de Dados Tabulares Bidimensionais 383

14 # print ( f " Valores como array NumPy


( df . values ) :\ n { df_pessoas . values }") # Pode ser grande para
imprimir

Código 20.16: Atributos de um DataFrame

Visualizando e Inspecionando Dados de um DataFrame


Quando um DataFrame é grande, imprimir ele inteiro no console não é prático. Pandas
oferece métodos convenientes para uma rápida inspeção dos dados:

• [Link](n=5): Retorna as primeiras n linhas do DataFrame (o padrão é 5).

• [Link](n=5): Retorna as últimas n linhas do DataFrame (o padrão é 5).

• [Link](): Fornece um resumo conciso do DataFrame, incluindo o tipo de dado de


cada coluna, o número de valores não nulos em cada coluna, e o uso de memória. É
extremamente útil para ter uma visão geral rápida dos seus dados.

• [Link](percentiles=None, include=None, exclude=None):

– Gera estatísticas descritivas das colunas numéricas por padrão (contagem, média,
desvio padrão, mínimo, máximo, e os quartis 25
– Você pode usar include=’all’ para tentar incluir estatísticas para colunas não
numéricas também (como contagem de valores únicos, valor mais frequente,
frequência do mais frequente).
– include=[’object’] para descrever apenas colunas de strings/objetos, ou in-
clude=[’number’] para apenas numéricas.

• [Link](n=5): Retorna as primeiras n linhas.

• [Link](n=5): Retorna as últimas n linhas.

• [Link](): Resumo conciso (tipos, nulos, memória).

• [Link](): Estatísticas descritivas das colunas numéricas. Use include=’all’


para incluir não numéricas.

1 # Usando df_pessoas novamente


2 print ( " \n - - - Inspecionando ’ df_pessoas ’ ---" )
3 print ( " \ nPrimeiras 3 linhas ( df . head (3) ) : " )
4 print ( df_pessoas . head (3) )
5

6 print ( " \ n Ú ltimas 2 linhas ( df . tail (2) ) : " )


7 print ( df_pessoas . tail (2) )
8
9 print ( " \ nInforma ç õ es do DataFrame ( df . info () ) : " )
10 df_pessoas . info () # Imprime diretamente no console
11

12 print ( " \ nEstat í sticas descritivas ( df . describe () ) : " )


13 print ( df_pessoas . describe () ) # Para colunas num é ricas
14
384 Capítulo 20. Pandas para Análise e Manipulação de Dados

15 print ( " \ nEstat í sticas descritivas ( incluindo n ã o num é ricas com


include = ’ all ’) : " )
16 print ( df_pessoas . describe ( include = ’ all ’) )

Código 20.17: Inspecionando um DataFrame


O DataFrame é a estrutura de dados de trabalho principal no Pandas, oferecendo uma
maneira incrivelmente poderosa e flexível de organizar, manipular e analisar dados tabulares.
Entender como criá-los e como realizar uma inspeção inicial com head(), tail(), info()
e describe() é o primeiro passo essencial para qualquer análise de dados com Pandas.

20.4 Seleção e Indexação de Dados em DataFrames


Uma das tarefas mais frequentes e fundamentais ao trabalhar com DataFrames no
Pandas é a seleção e indexação de dados. Precisamos ser capazes de extrair colunas
específicas, linhas específicas, ou até mesmo células individuais e subconjuntos de dados
(fatias) com base em diferentes critérios, sejam eles os rótulos das linhas/colunas ou suas
posições numéricas.
O Pandas oferece um conjunto poderoso e flexível de ferramentas para indexação. Os
principais indexadores que exploraremos são:

• A notação de colchetes [] (usada principalmente para selecionar colunas ou para


filtragem booleana de linhas).

• O indexador .loc[] (para seleção baseada em rótulos de índice e coluna).

• O indexador .iloc[] (para seleção baseada em posição inteira).

Vamos criar um DataFrame de exemplo para nossos estudos. Este DataFrame conterá
informações sobre algumas pessoas, incluindo um índice de linha personalizado para
facilitar a demonstração da seleção por rótulo.
1 import pandas as pd
2 import numpy as np
3
4 # Criando dados de exemplo para um DataFrame
5 dados_para_df_selecao = {
6 ’ Nome ’: [ ’ Alice L . ’ , ’ Bruno M . ’ , ’ Carlos S . ’ , ’ Diana P . ’ ,
’ Eduardo F . ’] ,
7 ’ Idade ’: [25 , 30 , 22 , 35 , 28] ,
8 ’ Cidade ’: [ ’S ã o Paulo ’ , ’ Rio de Janeiro ’ , ’ Belo Horizonte ’ ,
’S ã o Paulo ’ , ’ Curitiba ’] ,
9 ’ Pontuacao ’: [8.5 , 7.0 , 9.8 , 6.5 , 7.7] ,
10 ’ Ativo ’: [ True , False , True , True , False ]
11 }
12 # Definindo um í ndice personalizado para as linhas
13 i n d i c e _ l i n h a s _ p e r s o n a l i z a d o = [ ’ ID001 ’ , ’ ID002 ’ , ’ ID003 ’ , ’ ID004 ’ ,
’ ID005 ’]
14
15 # Criando o DataFrame
16 df_exemplo = pd . DataFrame ( dados_para_df_selecao ,
index = i n d i c e _ l i n h a s _ p e r s o n a l i z a d o )
17
18 print ( " --- DataFrame de Exemplo para Sele ç ã o e Indexa ç ã o ---" )
19 print ( df_exemplo )
20.4. Seleção e Indexação de Dados em DataFrames 385

Código 20.18: DataFrame de exemplo para seleção e indexação


Com nosso df_exemplo pronto, vamos explorar as diferentes formas de selecionar dados.

20.4.1 Selecionando Colunas


Existem algumas maneiras de selecionar uma ou mais colunas de um DataFrame.

• Usando df[’nome_da_coluna’] (Notação de Dicionário):


Esta é a forma mais comum, robusta e recomendada para selecionar uma única
coluna. Ela retorna a coluna especificada como um objeto Series do Pandas. Se o
nome da coluna contiver espaços ou caracteres especiais, esta é a única forma que
funciona.
1 # Selecionando a coluna ’ Nome ’
2 coluna_nomes = df_exemplo [ ’ Nome ’]
3 print ( " \n - - - Selecionando a coluna ’ Nome ’ ( retorna uma
Series ) ---" )
4 print ( coluna_nomes )
5 print ( f " Tipo do resultado para coluna ’ Nome ’:
{ type ( coluna_nomes ) } " )
6
7 # Selecionando a coluna ’ Pontuacao ’
8 coluna_pontuacao = df_exemplo [ ’ Pontuacao ’]
9 print ( " \n - - - Coluna ’ Pontuacao ’ ---" )
10 print ( coluna_pontuacao )

Código 20.19: Selecionando uma coluna com []

• Usando df.nome_da_coluna (Notação de Atributo):


Se o nome da coluna for um identificador Python válido, você pode acessá-la como
um atributo. É mais conciso, mas menos geral.
1 # Selecionando a coluna ’ Idade ’ usando nota ç ã o de atributo
2 colun a_idade_ attr = df_exemplo . Idade
3 print ( " \n - - - Selecionando a coluna ’ Idade ’ como atributo
---" )
4 print ( coluna_ idade_att r )

Código 20.20: Selecionando coluna como atributo

Recomendação: Prefira df[’nome_da_coluna’] para consistência.

• Selecionando Múltiplas Colunas:


Passe uma lista de nomes de colunas dentro dos colchetes []. O resultado é um
novo DataFrame.
1 # Selecionando as colunas ’ Nome ’ e ’ Cidade ’
2 df_nome_e_cidade = df_exemplo [[ ’ Nome ’ , ’ Cidade ’ ]] #
Colchetes duplos !
3 print ( " \n - - - Selecionando ’ Nome ’ e ’ Cidade ’ ( retorna
DataFrame ) ---" )
4 print ( df_nome_e_cidade )
5
6 # Selecionando colunas em ordem diferente
386 Capítulo 20. Pandas para Análise e Manipulação de Dados

7 d f _ i d a d e _ p o n t u a c a o _ e _ n o m e = df_exemplo [[ ’ Idade ’ ,
’ Pontuacao ’ , ’ Nome ’ ]]
8 print ( " \n - - - Colunas ’ Idade ’, ’ Pontuacao ’, ’ Nome ’ ---" )
9 print ( d f _ i d a d e _ p o n t u a c a o _ e _ n o m e )

Código 20.21: Selecionando múltiplas colunas

20.4.2 Selecionando Linhas (e Colunas) com .loc[] e .iloc[]


Para selecionar linhas, ou uma combinação de linhas e colunas, os indexadores .loc[]
(baseado em rótulos) e .iloc[] (baseado em posição inteira) são as ferramentas principais.

• Usando .loc[] (Seleção Baseada em Rótulos - Labels):


Seleciona dados com base nos rótulos do índice e nos nomes das colunas.

– [Link][rotulo_linha]: Seleciona uma linha (retorna Series).


– [Link][[’rotulo1’, ’rotulo3’]]: Seleciona múltiplas linhas (retorna
DataFrame).
– [Link][’inicio’:’fim’]: Fatiamento por rótulos (ambos inclusivos).
– [Link][linhas, colunas]: Seleciona linhas e colunas. Use : para todas.
1 # Usando df_exemplo com í ndice [ ’ ID001 ’, ... , ’ ID005 ’]
2 print ( " \n - - - Usando . loc [] ( sele ç ã o por r ó tulos ) ---" )
3
4 linha_id002_loc = df_exemplo . loc [ ’ ID002 ’]
5 print ( f " Linha ’ ID002 ’ ( Series ) :\ n { linha_id002_loc } " )
6 print ( f " Nome da linha ’ ID002 ’: { linha_id002_loc [ ’ Nome ’]} " )
7
8 linhas_sel_loc = df_exemplo . loc [[ ’ ID001 ’ , ’ ID004 ’ ]]
9 print ( f " \ nLinhas ’ ID001 ’ e ’ ID004 ’
( DataFrame ) :\ n { linhas_sel_loc } " )
10
11 fatia_loc = df_exemplo . loc [ ’ ID002 ’: ’ ID004 ’]
12 print ( f " \ nFatia ’ ID002 ’: ’ ID004 ’ ( ambos
inclusos ) :\ n { fatia_loc } " )
13
14 s ub_ df _l oc _pa rc ia l = df_exemplo . loc [[ ’ ID001 ’ , ’ ID003 ’] ,
[ ’ Nome ’ , ’ Pontuacao ’ ]]
15 print ( f " \ nSub - DataFrame ( linhas e colunas espec í ficas com
. loc ) :\ n { sub _d f_ loc _p ar cia l } " )
16
17 t o d a s _ l i n h a s _ n o m e _ i d a d e _ l o c = df_exemplo . loc [: , [ ’ Nome ’ ,
’ Idade ’ ]]
18 print ( f " \ nTodas as linhas , colunas ’ Nome ’ e ’ Idade ’
(. loc ) :\ n { t o d a s _ l i n h a s _ n o m e _ i d a d e _ l o c . head (2) } " )

Código 20.22: Usando .loc[] para seleção por rótulo

• Usando .iloc[] (Seleção Baseada em Posição Inteira):


Seleciona dados com base em sua posição inteira (baseada em 0).

– [Link][pos_linha]: Seleciona uma linha (retorna Series).


– [Link][[pos1, pos3]]: Seleciona múltiplas linhas.
20.4. Seleção e Indexação de Dados em DataFrames 387

– [Link][inicio:fim]: Fatiamento por posições (fim é exclusivo).


– [Link][pos_linhas, pos_colunas]: Seleciona linhas e colunas por posição.

1 print ( " \n - - - Usando . iloc [] ( sele ç ã o por posi ç ã o inteira )


---" )
2
3 pr i me i ra _ l in h a_ i l oc = df_exemplo . iloc [0]
4 print ( f " Primeira linha ( iloc [0]) :\ n { pr i me i r a_ l in h a _i l oc } " )
5
6 ultim a_linha_ iloc = df_exemplo . iloc [ -1]
7 print ( f " \ n Ú ltima linha ( iloc [ -1]) :\ n { ulti ma_linha _iloc } " )
8

9 linhas_0_2_iloc = df_exemplo . iloc [[0 , 2]]


10 print ( f " \ nLinhas posi ç õ es 0 e 2 ( iloc [[0 ,
2]]) :\ n { linhas_0_2_iloc } " )
11
12 fatia_iloc = df_exemplo . iloc [0:2] # Posi ç õ es 0 e 1
13 print ( f " \ nFatia das duas primeiras linhas
( iloc [0:2]) :\ n { fatia_iloc } " )
14
15 v alo r_ es ca lar _i lo c = df_exemplo . iloc [1 , 3] # Linha pos 1 ,
Coluna pos 3
16 print ( f " \ nValor ( iloc [1 ,3]) : { va lor _e sc ala r_ il oc } " )
17

18 s u b _d f _i l o c_ p ar c ia l = df_exemplo . iloc [[0 ,1] , [0 ,2 ,3]]


19 print ( f " \ nSub - DataFrame ( linhas [0 ,1] , colunas [0 ,2 ,3] por
pos ) :\ n { su b _d f _ il o c_ p ar c i al } " )
20
21 bloco_iloc = df_exemplo . iloc [:3 , :2] # 3 primeiras linhas ,
2 primeiras colunas
22 print ( f " \ nBloco 3 x2 ( iloc [:3 , :2]) :\ n { bloco_iloc } " )

Código 20.23: Usando .iloc[] para seleção por posição

20.4.3 Indexação Booleana (Filtragem de Linhas por Condição)


Esta é uma forma poderosa de selecionar linhas que atendem a condições lógicas.

1. Crie uma Series booleana (“máscara”) aplicando uma condição a uma ou mais
colunas.

2. Use esta máscara dentro dos colchetes [] do DataFrame (ou com .loc[]) para
selecionar as linhas onde a máscara é True.

Para combinar múltiplas condições, use & (E), | (OU), ~ (NÃO), com parênteses () ao
redor de cada condição individual.
1 print ( " \n - - - Demonstra ç ã o da Indexa ç ã o Booleana ( Filtragem ) ---" )
2 # Relembrando nosso df_exemplo :
3 # Nome Idade Cidade Pontuacao Ativo
4 # ID001 Alice 25 S ã o Paulo 8.5 True
5 # ID002 Bruno 30 Rio de Janeiro 7.0 False
6 # ID003 Carlos 22 Belo Horizonte 9.8 True
7 # ID004 Diana 35 S ã o Paulo 6.5 True
8 # ID005 Eduardo 28 Curitiba 7.7 False
9
388 Capítulo 20. Pandas para Análise e Manipulação de Dados

10 # Condi ç ã o 1: Selecionar pessoas com idade maior que 28 anos


11 m a s c a r a _ i d a d e _ m a i o r _ q u e _ 2 8 = df_exemplo [ ’ Idade ’] > 28
12 print ( f " M á scara booleana ( Idade >
28) :\ n { m a s c a r a _ i d a d e _ m a i o r _ q u e _ 2 8 } " )
13
14 df_maiores_de_28 = df_exemplo [ m a s c a r a _ i d a d e _ m a i o r _ q u e _ 2 8 ]
15 # Alternativamente , df_exemplo . loc [ m a s c a r a _ i d a d e _ m a i o r _ q u e _ 2 8 ]
produziria o mesmo resultado
16 print ( f " \ nPessoas com idade > 28:\ n { df_maiores_de_28 } " )
17
18 # Condi ç ã o 2: Selecionar pessoas da cidade de " S ã o Paulo " E que
est ã o " Ativo " == True
19 # Para combinar m ú ltiplas condi ç õ es , usamos os operadores l ó gicos
bit a bit :
20 # & para E ( AND )
21 # | para OU ( OR )
22 # ~ para N Ã O ( NOT )
23 # É CRUCIAL usar par ê nteses () ao redor de cada condi ç ã o individual
devido à preced ê ncia de operadores .
24 m a s c a r a _ s a o _ p a u l o _ e _ a t i v o s = ( df_exemplo [ ’ Cidade ’] == ’S ã o Paulo ’)
& ( df_exemplo [ ’ Ativo ’] == True )
25 d f_ s ao_ p au l o _a t iv o s = df_exemplo [ m a s c a r a _ s a o _ p a u l o _ e _ a t i v o s ]
26 print ( f " \ nPessoas de S ã o Paulo que est ã o
ativas :\ n { d f _s a o _p a ul o _a t i vo s } " )
27
28 # Condi ç ã o 3: Selecionar pessoas com Pontua ç ã o maior que 9.0 OU
Idade menor que 25 ,
29 # e mostrar apenas as colunas ’ Nome ’, ’ Pontua ç ã o ’ e ’ Idade ’.
30 m a s c a r a _ p o n t u a c a o _ a l t a _ O U _ j o v e m = ( df_exemplo [ ’ Pontuacao ’] > 9.0) |
( df_exemplo [ ’ Idade ’] < 25)
31 df _r esu lt ad o_c on d3 =
df_exemplo . loc [ mascara_pontuacao_alta_OU_jovem , [ ’ Nome ’ ,
’ Pontuacao ’ , ’ Idade ’ ]]
32 print ( f " \ nPessoas com pontua ç ã o > 9 OU idade < 25 ( colunas
selecionadas ) :\ n { df _re su lt ado _c on d3 } " )

Código 20.24: Indexação Booleana em DataFrames

20.4.4 Uma Breve Nota sobre set_index() e reset_index()


• df.set_index(’coluna’, inplace=False): Define uma coluna como o novo ín-
dice.

• df.reset_index(drop=False, inplace=False): Converte o índice atual para co-


luna e usa um índice numérico padrão.

Às vezes, durante a análise, você pode querer que uma das colunas do seu DataFrame se
torne o índice principal das linhas (em vez do índice numérico padrão 0, 1, 2... ou de outro
que você tenha definido na criação).

• df.set_index(’nome_da_coluna’, inplace=False): Esta função define a coluna


especificada como o novo índice do DataFrame. O parâmetro inplace=False (pa-
drão) significa que um novo DataFrame é retornado; se inplace=True, o DataFrame
original é modificado.
20.5. Lidando com Dados Ausentes (Missing Data - NaN) 389

• df.reset_index(drop=False, inplace=False): Esta função faz o oposto: ela


converte o índice atual (ou níveis dele, se for um MultiIndex) de volta para uma ou
mais colunas regulares e redefine o índice do DataFrame para um índice numérico
padrão (0, 1, 2,...). Se drop=True, o índice antigo é descartado em vez de se tornar
uma coluna.

Essas funções são úteis para reestruturar seu DataFrame e podem afetar como você usa
.loc[] (que opera sobre os rótulos do índice atual). Não vamos nos aprofundar nelas
agora, mas é bom saber que existem para quando você precisar de mais controle sobre
qual coluna serve como índice.
A seleção e indexação de dados são operações absolutamente centrais no fluxo de
trabalho com Pandas. Dominar a notação de colchetes [] para seleção de colunas, os
poderosos indexadores .loc[] (baseado em rótulos) e .iloc[] (baseado em posição
inteira), e a flexível indexação booleana é fundamental para você conseguir filtrar, extrair
e preparar seus dados para as análises e visualizações subsequentes.

20.5 Lidando com Dados Ausentes (Missing Data - NaN)


No mundo real, é muito comum que os conjuntos de dados com os quais trabalhamos
estejam incompletos. O Pandas representa esses dados ausentes (ou missing data)
principalmente com um valor especial chamado NaN (que significa “Not a Number”), um
tipo float especial originário do NumPy. O valor None do Python também pode ser usado
e é frequentemente tratado de forma similar. Saber como identificar, tratar e, quando
necessário, remover ou preencher esses dados ausentes é uma etapa crucial na limpeza e
preparação de dados.

Identificando Dados Ausentes


Os dois métodos principais para identificar dados ausentes são:

• objeto_pandas.isnull() (ou isna()): Retorna um objeto booleano com True


para cada elemento ausente.

• objeto_pandas.notnull() (ou notna()): O oposto de isnull().

Você pode combinar isnull() com .sum() para contar quantos valores ausentes existem
por coluna.
1 import pandas as pd
2 import numpy as np
3
4 # Criando um DataFrame com alguns dados ausentes ( NaN )
5 d ad os _co m_ au sen te s = {
6 ’ Nome ’: [ ’ Alice ’ , ’ Bruno ’ , ’ Carlos ’ , ’ Diana ’ , ’ Eduardo ’ ,
’ Fernanda ’] ,
7 ’ Idade ’: [25 , 30 , np . nan , 35 , 28 , np . nan ] , # Duas idades ausentes
8 ’ PontuacaoA ’: [8.5 , 7.0 , 9.8 , np . nan , 7.7 , 8.0] ,
9 ’ PontuacaoB ’: [9.1 , np . nan , 8.0 , 6.5 , np . nan , 9.5]
10 }
11 df_ausentes = pd . DataFrame ( dados_com_ausentes ,
12 index =[ ’ Est1 ’ , ’ Est2 ’ , ’ Est3 ’ , ’ Est4 ’ , ’ Est5 ’ , ’ Est6 ’ ])
13
390 Capítulo 20. Pandas para Análise e Manipulação de Dados

14 print ( " --- DataFrame com Dados Ausentes ---" )


15 print ( df_ausentes )
16
17 print ( " \n - - - Verificando valores nulos ( isnull () ) ---" )
18 print ( df_ausentes . isnull () )
19

20 print ( " \n - - - Contagem de valores nulos por coluna ( isnull () . sum () ) ---" )
21 print ( df_ausentes . isnull () . sum () )
22 # Sa í da :
23 # Nome 0
24 # Idade 2
25 # PontuacaoA 1
26 # PontuacaoB 2
27 # dtype : int64
28
29 print ( " \n - - - Verificando valores n ã o nulos ( notnull () ) ---" )
30 print ( df_ausentes . notnull () )

Código 20.25: Identificando dados ausentes com Pandas

Removendo Dados Ausentes com dropna()


O método dropna() permite remover linhas ou colunas com dados ausentes. Sintaxe:
[Link](axis=0, how=’any’, thresh=None, subset=None, inplace=False)

• axis: 0 para linhas (padrão), 1 para colunas.

• how: ’any’ (remove se houver algum NaN, padrão), ’all’ (remove se todos forem
NaN).

• thresh: Mantém linhas/colunas com pelo menos thresh valores não-NaN.

• subset: Lista de colunas/linhas a considerar para a verificação de NaN.

• inplace=False (padrão): Retorna um novo DataFrame. Se True, modifica o original.

1 # Usando df_ausentes do exemplo anterior


2
3 print ( " \n - - - Removendo linhas com QUALQUER valor ausente
( how = ’ any ’) ---" )
4 d f _s em_ a us e n te s _a n y = df_ausentes . dropna () # Padr ã o é axis =0 ,
how = ’ any ’
5 print ( d f_ s em _ au s e nt e s_ a n y )
6 # Apenas a linha ’ Est1 ’ ( Alice ) sobraria , pois é a ú nica sem NaN
7
8 print ( " \n - - - Removendo linhas onde TODOS os valores s ã o ausentes
( how = ’ all ’) ---" )
9 # Para este exemplo , n ã o temos nenhuma linha assim , ent ã o o
resultado seria o mesmo que o original
10 d f_ s em_ a us e n te s _a l l = df_ausentes . dropna ( how = ’ all ’)
11 print ( d f_ s em _ au s e nt e s_ a l l ) # Mostrar á o df_ausentes original
12
13 print ( " \n - - - Removendo colunas com QUALQUER valor ausente ( axis =1)
---" )
14 d f _ s e m _ c o l u n a s _ a u s e n t e s = df_ausentes . dropna ( axis =1)
15 print ( d f _ s e m _ c o l u n a s _ a u s e n t e s )
20.5. Lidando com Dados Ausentes (Missing Data - NaN) 391

16 # Apenas a coluna ’ Nome ’ sobraria


17
18 print ( " \n - - - Removendo linhas que n ã o t ê m pelo menos 3 valores n ã o
ausentes ( thresh =3) ---" )
19 df_thresh_3 = df_ausentes . dropna ( thresh =3)
20 print ( df_thresh_3 )
21 # Est1 : 4 n ão - nulos ( Nome , Idade , PontuacaoA , PontuacaoB ) -> Mant é m
22 # Est2 : 2 n ão - nulos ( Nome , PontuacaoA ) -> Remove
23 # Est3 : 3 n ão - nulos ( Nome , PontuacaoA , PontuacaoB ) -> Mant é m
24 # Est4 : 2 n ão - nulos ( Nome , Idade ) -> Remove
25 # Est5 : 2 n ão - nulos ( Nome , Idade ) -> Remove
26 # Est6 : 3 n ão - nulos ( Nome , PontuacaoA , PontuacaoB ) -> Mant é m

Código 20.26: Removendo dados ausentes com dropna()


Remover dados pode levar à perda de informação significativa.

Preenchendo Dados Ausentes com fillna()


Em vez de remover, muitas vezes é preferível preencher os valores ausentes. Sintaxe:
[Link](value, method=None, axis=None, inplace=False, limit=None, ...)
• value: O valor a ser usado (escalar, dicionário, Series).
• method: Método de preenchimento (’ffill’ para propagar o último valor válido
para frente; ’bfill’ para usar o próximo valor válido para trás).
• inplace=False (padrão): Retorna um novo DataFrame.

1 # Usando df_ausentes do exemplo anterior


2 print ( " \n - - - DataFrame Original para fillna () ---" )
3 print ( df_ausentes )
4
5 print ( " \n - - - Preenchendo todos os NaN com 0 ---" )
6 df_preenchido_0 = df_ausentes . fillna (0)
7 print ( df_preenchido_0 )
8
9 print ( " \n - - - Preenchendo NaN na ’ Idade ’ com a m é dia das idades ---" )
10 media_idades = df_ausentes [ ’ Idade ’ ]. mean ()
11 df_idade_media = df_ausentes . copy ()
12 df_idade_media [ ’ Idade ’ ]. fillna ( media_idades , inplace = True )
13 print ( df_idade_media )
14

15 print ( " \n - - - Preenchendo ’ PontuacaoB ’ usando forward - fill ( ’ ffill ’)


---" )
16 df_ffill_pont_b = df_ausentes . copy ()
17 df_ffill_pont_b [ ’ PontuacaoB ’ ]. fillna ( method = ’ ffill ’ , inplace = True )
18 print ( df_ffill_pont_b )
19

20 print ( " \n - - - Preenchendo com valores diferentes por coluna ---" )


21 v a l o r e s _ p a r a _ p r e e n c h e r = { ’ Idade ’: df_ausentes [ ’ Idade ’ ]. median () ,
22 ’ PontuacaoA ’: 0 ,
23 ’ PontuacaoB ’: df_ausentes [ ’ PontuacaoB ’ ]. mean () }
24 df_preenchido_especifico =
df_ausentes . fillna ( value = v a l o r e s _ p a r a _ p r e e n c h e r )
25 print ( d f _ p r e e n c h i d o _ e s p e c i f i c o )

Código 20.27: Preenchendo dados ausentes com fillna()


392 Capítulo 20. Pandas para Análise e Manipulação de Dados

A escolha do método de preenchimento (valor fixo, média, mediana, ffill, bfill, ou


técnicas mais avançadas como interpolação ou modelagem) depende muito do contexto
dos dados e do impacto que o preenchimento terá nas análises subsequentes.
Lidar com dados ausentes é uma etapa essencial e muitas vezes desafiadora no pré-
processamento de dados. O Pandas fornece as ferramentas para identificá-los e tratá-los
de várias maneiras, permitindo que você prepare seus dados para análises mais confiáveis.

20.6 Operações Comuns e Funções em DataFrames


Os DataFrames do Pandas vêm com uma vasta gama de funcionalidades para realizar
operações, aplicar funções e obter estatísticas descritivas. Muitas dessas operações são
vetorizadas e lidam automaticamente com o alinhamento de índices.

Operações Aritméticas e Alinhamento de Dados


Operações aritméticas (+, -, *, /) entre DataFrames ou com Series/escalares são
aplicadas elemento a elemento, com alinhamento de dados pelo índice antes da
operação. Se um rótulo de linha ou coluna não existir em um dos DataFrames, o resultado
para aquela combinação será NaN.
1 import pandas as pd
2 import numpy as np
3
4 df1 = pd . DataFrame ( np . arange (1 ,7) . reshape ((2 ,3) ) ,
columns =[ ’A ’ , ’B ’ , ’C ’] , index =[ ’ L1 ’ , ’ L2 ’ ])
5 df2 = pd . DataFrame ( np . arange (5 ,11) . reshape ((2 ,3) ) ,
columns =[ ’B ’ , ’C ’ , ’D ’] , index =[ ’ L2 ’ , ’ L3 ’ ])
6
7 print ( f " --- df1 - - -\ n { df1 }\ n - - - df2 - - -\ n { df2 } " )
8 soma_dfs_ alinhada = df1 + df2
9 print ( f " \n - - - df1 + df2 ( com alinhamento ) - - -\ n { soma_df s_alinha da } " )
10
11 df_mais_10 = df1 + 10
12 print ( f " \n - - - df1 + 10 - - -\ n { df_mais_10 } " )
13
14 serie_sub = pd . Series ([1 ,0 ,10] , index =[ ’A ’ , ’B ’ , ’C ’ ])
15 df_sub_serie = df1 - serie_sub # Broadcast nas linhas
16 print ( f " \n - - - df1 - Serie - - -\ n { df_sub_serie } " )

Código 20.28: Operações aritméticas com DataFrames e alinhamento

Aplicando Funções NumPy e Outras Funções Universais (ufuncs)


A maioria das ufuncs NumPy (ex: [Link](), [Link](), [Link]()) funciona direta-
mente em DataFrames e Series, aplicando a função elemento a elemento e preservando os
rótulos.
1 # Usando df1 do exemplo anterior
2 print ( f " \n - - - Aplicando np . sqrt () em df1 - - -\ n { np . sqrt ( df1 ) } " )
3

4 serie_ex = pd . Series ([0 , 1 , np . log (5) ] , index =[ ’x ’ , ’y ’ , ’z ’ ])


5 print ( f " \n - - - Exponencial de uma Series ( np . exp )
- - -\ n { np . exp ( serie_ex ) } " )
20.6. Operações Comuns e Funções em DataFrames 393

Código 20.29: Aplicando ufuncs NumPy em DataFrames e Series

Estatísticas Descritivas
Pandas oferece métodos para calcular estatísticas descritivas. NaNs são ignorados por
padrão (skipna=True).
• [Link](): Resumo de várias estatísticas (contagem, média, std, min, max,
quartis).
• Agregações comuns: [Link](), [Link](), [Link](), [Link](), [Link](),
[Link](), [Link](), [Link](). Podem usar axis=0 (padrão, por coluna) ou
axis=1 (por linha).
• [Link](): Matriz de correlação entre colunas numéricas.

1 # DataFrame de exemplo com NaN


2 dados_estats = {
3 ’ Turma ’: [ ’A ’ , ’B ’ , ’A ’ , ’B ’ , ’A ’ , ’B ’] ,
4 ’ Nota1 ’: [7 , 8 , 6 , 9 , 7 , 8] ,
5 ’ Nota2 ’: [8 , 7 , np . nan , 8 , 9 , 7] ,
6 ’ Faltas ’: [2 , 1 , 3 , 0 , 1 , np . nan ]
7 }
8 df_stats = pd . DataFrame ( dados_estats )
9 print ( f " \n - - - DataFrame para Estat í sticas - - -\ n { df_stats } " )
10
11 print ( f " \ nM é dia das Nota1 : { df_stats [ ’ Nota1 ’]. mean () :.2 f } " )
12 print ( f " Soma das Faltas ( NaNs ignorados ) :
{ df_stats [ ’ Faltas ’]. sum () } " )
13 print ( f " Contagem de valores em Nota2 : { df_stats [ ’ Nota2 ’]. count () } " )
14
15 # Estat í sticas por coluna ( axis =0 é o padr ã o )
16 print ( f " \ nM é dias por coluna
( num é ricas ) :\ n { df_stats [[ ’ Nota1 ’,’ Nota2 ’,’ Faltas ’]]. mean () } " )

Código 20.30: Estatísticas descritivas com Pandas

Aplicando Funções Arbitrárias com apply()


O método [Link](funcao, axis=0) aplica uma funcao ao longo de um eixo (0
para colunas, 1 para linhas). A função recebe cada coluna/linha como uma Series.
1 # Usando df_stats ( apenas colunas num é ricas para este exemplo de
apply )
2 df_numerico = df_stats [[ ’ Nota1 ’ , ’ Nota2 ’ , ’ Faltas ’ ]]. copy ()
3 df_numerico . fillna (0 , inplace = True ) # Preenchendo NaNs para a
fun ç ã o funcionar
4
5 def amplitude_serie ( s ) : # Recebe uma Series ( coluna ou linha )
6 return s . max () - s . min ()
7

8 print ( f " \n - - - DataFrame Num é rico para Apply - - -\ n { df_numerico } " )


9 amplitude _por_col = df_numerico . apply ( amplitude_serie ) # axis =0 por
padr ã o
394 Capítulo 20. Pandas para Análise e Manipulação de Dados

10 print ( f " \ nAmplitude por coluna :\ n { amplitu de_por_c ol } " )


11
12 # Normalizando cada coluna com lambda : ( x - min ( x ) ) / ( max ( x ) -
min ( x ) )
13 df_norm_apply = df_numerico . apply ( lambda s_col : ( s_col -
s_col . min () ) / ( s_col . max () - s_col . min () ) )
14 print ( f " \ nDataFrame Normalizado com apply e
lambda :\ n { df_norm_apply } " )

Código 20.31: Usando apply() para funções customizadas

Ordenação
• df.sort_values(by, ascending=True, ...): Ordena pelos valores de uma ou
mais colunas.

• df.sort_index(ascending=True, ...): Ordena pelos rótulos do índice.

1 # Usando df_stats
2 print ( " \n - - - Ordenando DataFrames ---" )
3 df_ord_nota1 = df_stats . sort_values ( by = ’ Nota1 ’ , ascending = False )
4 print ( f " Ordenado por Nota1 ( decrescente ) :\ n { df_ord_nota1 . head () } " )
5
6 df _o rd_ tu rm a_n ot a2 = df_stats . sort_values ( by =[ ’ Turma ’ , ’ Nota2 ’] ,
ascending =[ True , False ])
7 print ( f " \ nOrdenado por Turma ( asc ) e Nota2
( desc ) :\ n { df_ or d_ tur ma _n ota 2 } " )

Código 20.32: Ordenando DataFrames

Contagem de Valores Únicos em uma Series


O método series.value_counts() é útil para obter a frequência de cada valor único.
1 # Usando df_stats
2 print ( " \n - - - Contagem de Valores Ú nicos ---" )
3 cont_turmas = df_stats [ ’ Turma ’ ]. value_counts ()
4 print ( f " Contagem por Turma :\ n { cont_turmas } " )

Código 20.33: Contagem de valores únicos com value_counts()


Essas são algumas das operações e funções mais comuns para trabalhar com DataFrames
no Pandas.

20.7 Agrupamento de Dados (Group By): Analisando


Dados por Categorias
Uma das funcionalidades mais poderosas do Pandas para análise de dados é a capacidade
de agrupar dados com base nos valores de uma ou mais colunas e, em seguida, aplicar
funções de agregação ou transformações a esses grupos. Essa operação é comumente
conhecida como “Group By” e segue um paradigma chamado “Split-Apply-Combine”
(Dividir-Aplicar-Combinar).
20.7. Agrupamento de Dados (Group By): Analisando Dados por Categorias 395

O Paradigma “Split-Apply-Combine”
A ideia é:

1. Split (Dividir): O DataFrame é dividido em grupos menores com base nos valores
únicos de colunas especificadas (chaves de agrupamento).

2. Apply (Aplicar): Uma função é aplicada a cada um desses grupos independente-


mente (agregação, transformação, filtragem).

3. Combine (Combinar): Os resultados são combinados de volta em uma nova


estrutura de dados (Series ou DataFrame).

Agrupando com [Link]()


A principal ferramenta para o passo de “Split” é o método groupby() de um DataFrame.

• Sintaxe: objeto_groupby = [Link](by, axis=0, ...)

– by: Nome de coluna (string), lista de nomes de colunas, ou uma Series.


– axis=0 (padrão): Agrupa pelas linhas.

• Retorna um objeto DataFrameGroupBy (ou SeriesGroupBy). Nenhuma computação


real é feita até que uma função de agregação/transformação seja aplicada.

Aplicando Funções de Agregação aos Grupos


Após criar um objeto DataFrameGroupBy, aplique funções de agregação para calcular
estatísticas para cada grupo:

• .sum(), .mean(), .median(), .min(), .max()

• .count() (valores não nulos), .size() (total de linhas no grupo)

• .std(), .var()

• .first(), .last()

• .agg(funcao_ou_lista_ou_dict): Para múltiplas agregações ou funções customi-


zadas.

Exemplo Prático: Analisando Vendas por Categoria de Produto


1 import pandas as pd
2 import numpy as np
3

4 # Criando dados de exemplo


5 dados_vendas = {
6 ’ ProdutoID ’: range (1 , 11) ,
7 ’ Categoria ’: [ ’ Eletr ô nicos ’ , ’ Livros ’ , ’ Eletr ô nicos ’ , ’ Casa ’ ,
’ Livros ’ ,
8 ’ Eletr ô nicos ’ , ’ Casa ’ , ’ Livros ’ , ’ Eletr ô nicos ’ , ’ Casa ’] ,
9 ’ ValorVenda ’: [1200 , 45 , 850 , 230 , 60 , 1500 , 150 , 30 , 950 , 80] ,
10 ’ Quantidade ’: [1 , 2 , 1 , 3 , 1 , 1 , 2 , 1 , 1 , 1]
11 }
396 Capítulo 20. Pandas para Análise e Manipulação de Dados

12 df _v end as _p rod ut os = pd . DataFrame ( dados_vendas )


13
14 print ( " --- DataFrame de Vendas Original ---" )
15 print ( df_ ve nd as_ pr od uto s )
16
17 # Agrupando pela coluna ’ Categoria ’
18 g r u p o s _ p o r _ c a t e g o r ia = df_ ve nd as_ pr od uto s . groupby ( ’ Categoria ’)
19
20 print ( " \n - - - Soma do Valor de Venda por Categoria ---" )
21 s o m a _ v a l o r _ p o r _ c a t e g o r i a = g r u p o s _ p o r_ c a t e g o r i a [ ’ ValorVenda ’ ]. sum ()
22 print ( s o m a _ v a l o r _ p o r _ c a t e g o r i a )
23

24 print ( " \n - - - M é dia da Quantidade Vendida por Categoria ---" )


25 media_quantidade_por_categoria =
g r u p o s _ p o r _ c at e g o r i a [ ’ Quantidade ’ ]. mean ()
26 print ( m e d i a _ q u a n t i d a d e _ p o r _ c a t e g o r i a )
27
28 print ( " \n - - - Contagem de Produtos por Categoria (. size () ) ---" )
29 c o n t a g e m _ p r o d u t o s _ p o r _ c a t e g o r i a = g ru p o s _ p o r _ c a t e g o r i a . size ()
30 print ( c o n t a g e m _ p r o d u t o s _ p o r _ c a t e g o r i a )
31
32 print ( " \n - - - M ú ltiplas Agrega ç õ es para ’ ValorVenda ’ (. agg () ) ---" )
33 a g r e g a c o e s _ v a l o r _ v e n d a = g r u p o s _ p o r _ ca t e g o r i a [ ’ ValorVenda ’ ]. agg (
34 [ ’ sum ’ , ’ mean ’ , ’ max ’ , ’ count ’]
35 )
36 print ( a g r e g a c o e s _ v a l o r _ v e n d a )
37
38 print ( " \n - - - Agrega ç õ es em M ú ltiplas Colunas ---" )
39 a g r e g a c o e s _ m u l t i p l a s _ c o l u n a s = g r u p os _ p o r _ c a t e g o r i a . agg ({
40 ’ ValorVenda ’: ’ sum ’ , # Soma para ValorVenda
41 ’ Quantidade ’: [ ’ mean ’ , ’ sum ’] # M é dia e Soma para Quantidade
42 })
43 print ( a g r e g a c o e s _ m u l t i p l a s _ c o l u n a s )

Código 20.34: Agrupando dados de vendas por categoria com Pandas

Agrupamento por Múltiplas Colunas


Passe uma lista de nomes de colunas para groupby() para criar um índice hierárquico
(MultiIndex).
1 # Adicionando uma coluna ’ Regiao ’
2 np . random . seed (10)
3 df _v end as _p rod ut os [ ’ Regiao ’] = np . random . choice ([ ’ Norte ’ , ’ Sul ’] ,
4 size = len ( d f_ ve nda s_ pr odu to s ) )
5 print ( " \n - - - DataFrame de Vendas com Regi ã o ---" )
6 print ( df_ ve nd as_ pr od uto s . head () ) # Mostra as primeiras linhas
7
8 # Agrupando por ’ Regiao ’ e depois por ’ Categoria ’
9 g r u p o s _ r e g i a o _ c a t e g o r i a = d f_ ve nda s_ pr odu to s . groupby ([ ’ Regiao ’ ,
’ Categoria ’ ])
10
11 print ( " \n - - - Valor M é dio de Venda por Regi ã o e Categoria ---" )
12 m ed i a_v a lo r _ re g _c a t = g r u p o s _ r e g i a o _ c a t e g o r i a [ ’ ValorVenda ’ ]. mean ()
13 print ( m ed i a_ v al o r _r e g_ c a t )
14
15 # Para visualizar melhor um MultiIndex , voc ê pode usar . unstack ()
( opcional )
20.8. Mesclando, Juntando e Concatenando DataFrames (Merge, Join, Concat) 397

16 # print ("\ n - - - M é dia de Valor ( desempilhando ’ Categoria ’) - - -")


17 # print ( me d ia _ v al o r_ r eg _ c at . unstack () )

Código 20.35: Agrupando por múltiplas colunas


A operação groupby é uma das pedras angulares da análise de dados com Pandas, per-
mitindo dividir dados em subconjuntos significativos, aplicar cálculos e combinar os
resultados.

20.8 Mesclando, Juntando e Concatenando DataFrames


(Merge, Join, Concat)
Na análise de dados, raramente os dados vêm em uma única tabela perfeita. Frequen-
temente, precisamos combinar dados de diferentes fontes ou DataFrames. Pandas oferece
várias funções poderosas para isso: [Link](), [Link](), e o método .join().

20.8.1 Concatenando DataFrames com [Link]()


A função [Link]() é usada para “empilhar” DataFrames verticalmente (axis=0,
padrão) ou juntá-los lado a lado horizontalmente (axis=1).
Sintaxe: [Link](objs, axis=0, join=’outer’, ignore_index=False, ...)

• objs: Lista de DataFrames a serem concatenados.

• axis=0: Empilha linhas. axis=1: Junta colunas.

• join=’outer’: Mantém todos os rótulos do outro eixo, preenchendo com NaN.


’inner’: Mantém apenas rótulos comuns.

• ignore_index=True: Cria um novo índice numérico (0, 1, 2,...).

Exemplo 1: Empilhando Linhas (axis=0)


1 import pandas as pd
2 import numpy as np
3
4 df1 = pd . DataFrame ({ ’A ’: [ ’ A0 ’ , ’ A1 ’] , ’B ’: [ ’ B0 ’ , ’ B1 ’ ]})
5 df2 = pd . DataFrame ({ ’A ’: [ ’ A2 ’ , ’ A3 ’] , ’B ’: [ ’ B2 ’ , ’ B3 ’ ]})
6 df3 = pd . DataFrame ({ ’B ’: [ ’ B4 ’ , ’ B5 ’] , ’C ’: [ ’ C4 ’ , ’ C5 ’]} ,
index =[0 ,1]) # Coluna C nova
7
8 print ( f " --- df1 - - -\ n { df1 } " )
9 print ( f " \n - - - df2 - - -\ n { df2 } " )
10 print ( f " \n - - - df3 - - -\ n { df3 } " )
11

12 v e n d a s _ j a n _ f e v _ i d x _ n o v o = pd . concat ([ df1 , df2 ] , ignore_index = True )


13 print ( f " \n - - - df1 + df2 ( ignore_index = True )
- - -\ n { v e n d a s _ j a n _ f e v _ i d x _ n o v o } " )
14
15 ve nd as_ to do s_o ut er = pd . concat ([ df1 , df3 ] , sort = False ) # sort = False
para manter ordem original
16 print ( f " \n - - - df1 + df3 ( join = ’ outer ’ por padr ã o )
- - -\ n { v en das _t od os_ ou te r } " )
17
398 Capítulo 20. Pandas para Análise e Manipulação de Dados

18 ve nd as_ to do s_i nn er = pd . concat ([ df1 , df3 ] , join = ’ inner ’)


19 print ( f " \n - - - df1 + df3 ( join = ’ inner ’) - - -\ n { v en das _t od os_ in ne r } " )

Código 20.36: Concatenando linhas de DataFrames com [Link]()

Exemplo 2: Juntando Colunas (axis=1)


Ao concatenar colunas, Pandas alinha pelos índices de linha.
1 # df_info_pessoal e df_info_contato definidos como no texto da se ç ã o
2 df_pessoal = pd . DataFrame ({ ’ Nome ’: [ ’ Ana ’ , ’ Bruno ’] , ’ Idade ’:
[28 ,34]} , index =[ ’ id1 ’ , ’ id2 ’ ])
3 df_contato = pd . DataFrame ({ ’ Email ’:
[ ’ ana@e . com ’ , ’ bruno@e . com ’ , ’ eva@e . com ’]} ,
4 index =[ ’ id1 ’ , ’ id2 ’ , ’ id4 ’ ])
5
6 print ( f " \n - - - df_pessoal - - -\ n { df_pessoal } " )
7 print ( f " \n - - - df_contato - - -\ n { df_contato } " )
8
9 df_col_outer = pd . concat ([ df_pessoal , df_contato ] , axis =1) #
join = ’ outer ’ é padr ã o
10 print ( f " \n - - - Concatena ç ã o de Colunas ( axis =1 , join = ’ outer ’)
- - -\ n { df_col_outer } " )
11

12 df_col_inner = pd . concat ([ df_pessoal , df_contato ] , axis =1 ,


join = ’ inner ’)
13 print ( f " \n - - - Concatena ç ã o de Colunas ( axis =1 , join = ’ inner ’)
- - -\ n { df_col_inner } " )

Código 20.37: Concatenando colunas de DataFrames com [Link]()

20.8.2 Combinando DataFrames com [Link]() (Estilo SQL JOIN)


[Link]() é a principal ferramenta para junções no estilo de banco de dados, baseadas
em colunas comuns (chaves) ou índices.
Sintaxe:
[Link](left, right, how=’inner’, on=None, left_on=None,
right_on=None, left_index=False, right_index=False, suffixes=(’_x’, ’_y’))

• how: Tipo de junção (’inner’, ’outer’, ’left’, ’right’).

• on: Nome da(s) coluna(s) chave de junção (comum a ambos).

• left_on, right_on: Nomes das colunas chave se forem diferentes.

• left_index=True, right_index=True: Usar índice como chave.

• suffixes: Sufixos para colunas de mesmo nome não usadas na junção.

Exemplo: Mesclando dados de clientes e seus pedidos


1 df_clientes = pd . DataFrame ({
2 ’ ID_Cliente ’: [1 , 2 , 3 , 4] , ’ NomeCliente ’: [ ’ Ana ’ , ’ Bruno ’ ,
’ Carla ’ , ’ Daniel ’]
3 })
4 df_pedidos = pd . DataFrame ({
5 ’ ID_Pedido ’: [ ’ P101 ’ , ’ P102 ’ , ’ P103 ’ , ’ P104 ’ , ’ P105 ’] ,
20.8. Mesclando, Juntando e Concatenando DataFrames (Merge, Join, Concat) 399

6 ’ ID_Cl iente_Ped ido ’: [1 , 2 , 1 , 3 , 5] , # Cliente 5 n ã o est á em


df_clientes
7 ’ Produto ’: [ ’A ’ , ’B ’ , ’C ’ , ’A ’ , ’D ’]
8 })
9 print ( f " \n - - - Clientes - - -\ n { df_clientes }\ n - - - Pedidos
- - -\ n { df_pedidos } " )
10
11 df_inner = pd . merge ( df_clientes , df_pedidos ,
12 left_on = ’ ID_Cliente ’ , right_on = ’ ID_Cl iente_Pe dido ’ , how = ’ inner ’)
13 print ( f " \n - - - Inner Merge - - -\ n { df_inner } " )
14
15 df_left = pd . merge ( df_clientes , df_pedidos ,
16 left_on = ’ ID_Cliente ’ , right_on = ’ ID_Cl iente_Pe dido ’ , how = ’ left ’)
17 print ( f " \n - - - Left Merge - - -\ n { df_left } " )
18
19 df_outer = pd . merge ( df_clientes , df_pedidos ,
20 left_on = ’ ID_Cliente ’ , right_on = ’ ID_Cl iente_Pe dido ’ , how = ’ outer ’ ,
21 indicator = True ) # Adiciona coluna ’ _merge ’
22 print ( f " \n - - - Outer Merge com indicator --- \ n { df_outer } " )

Código 20.38: Mesclando DataFrames de clientes e pedidos com [Link]()

20.8.3 Juntando DataFrames com o Método .join()


O método [Link](df2, on=None, how=’left’, ...) é conveniente para combinar
colunas, principalmente com base nos índices.

• Por padrão, junta pelos índices de ambos.

• Se on for especificado, junta o índice de df2 com a(s) coluna(s) em on de df1.

1 df_func_main = pd . DataFrame ({
2 ’ Nome ’: [ ’ Carlos ’ , ’ Diana ’] , ’ Depto ’: [ ’ Vendas ’ , ’ Marketing ’]
3 } , index =[ ’ F101 ’ , ’ F102 ’ ])
4

5 df_func_sal = pd . DataFrame ({
6 ’ Salario ’: [5000 , 6200] , ’ Bonus ’: [500 , 700]
7 } , index =[ ’ F101 ’ , ’ F102 ’ ]) # Mesmo í ndice
8
9 print ( f " \n - - - Funcion á rios - - -\ n { df_func_main } " )
10 print ( f " \n - - - Sal á rios - - -\ n { df_func_sal } " )
11
12 df_completo_j1 = df_func_main . join ( df_func_sal ) # Left join por í
ndice é o padr ã o
13 print ( f " \n - - - Funcion á rios + Sal á rios ( join ) - - -\ n { df_completo_j1 } " )

Código 20.39: Usando o método .join() para combinar DataFrames

Escolhendo o Método Certo para Combinar


• [Link](): Para empilhar ou juntar lado a lado de forma simples, baseado em
alinhamento de eixos.

• [Link](): Para operações de junção complexas, estilo banco de dados, baseadas


em colunas comuns ou índices. Mais flexível.
400 Capítulo 20. Pandas para Análise e Manipulação de Dados

• [Link](): Para junções mais simples, geralmente baseadas em índice.

Combinar DataFrames de diferentes fontes ou com diferentes estruturas é uma parte


essencial da preparação de dados para análise. O Pandas, com concat, merge e join,
oferece um conjunto completo e poderoso de ferramentas para realizar essas combinações
de maneira eficiente e flexível.

20.9 Leitura e Escrita de Arquivos com Pandas


Uma das funcionalidades mais poderosas do Pandas é sua capacidade de ler dados de
uma variedade de formatos de arquivo para dentro de DataFrames, e de salvar DataFrames
de volta para arquivos. Focaremos nos formatos mais comuns: CSV e Excel. Lembre-se de
import pandas as pd e import numpy as np.

20.9.1 Lendo e Escrevendo Arquivos CSV (Comma-Separated


Values)
Arquivos CSV são um formato de texto muito comum para armazenar dados tabulares.
Cada linha do arquivo representa uma linha da tabela, e os valores dentro de cada linha
são separados por um delimitador, geralmente uma vírgula (,).

Lendo Arquivos CSV com pd.read_csv()


Sintaxe: pd.read_csv(caminho, sep=’,’, header=’infer’, index_col=None,
usecols=None, encoding=’utf-8’, ...)
• caminho_do_arquivo: Caminho ou URL para o arquivo.

• sep: Delimitador (padrão ’,’).

• header: Linha a ser usada como cabeçalho (padrão 0 ou ’infer’).

• index_col: Coluna a ser usada como índice das linhas.

• usecols: Lista de colunas a serem lidas.

• na_values: Strings a serem interpretadas como NaN.

• encoding: Codificação do arquivo (ex: "utf-8", "latin1").

1 import pandas as pd
2 import numpy as np
3 import io # Para simular arquivo em string
4

5 # Conte ú do para exemplo_dados . csv ( simulado )


6 c o n t e u d o _ c s v _ e x e m p l o = " " " ID , Nome , Idade , Cidade
7 1 , Ana Silva ,28 , Sao Paulo
8 2 , Bruno Costa ,34 , Rio de Janeiro
9 3 , Carlos Dias , , Belo Horizonte " " " # Idade de Carlos est á vazia
10

11 try :
12 # Em um caso real : df_lido_csv =
pd . read_csv (" caminho / real / arquivo . csv ")
20.9. Leitura e Escrita de Arquivos com Pandas 401

13 df_lido_csv = pd . read_csv ( io . StringIO ( c o n t e u d o_ c s v _ e x e m p l o ) ,


14 sep = ’ , ’ , encoding = ’utf -8 ’ , na_values =[ ’ ’ ])
15 print ( " --- DataFrame lido do CSV ( simulado ) ---" )
16 print ( df_lido_csv )
17 print ( " \ nInforma ç õ es do DataFrame : " )
18 df_lido_csv . info ()
19 except Exception as e :
20 print ( f " Erro ao ler CSV simulado : { e } " )

Código 20.40: Lendo um arquivo CSV (simulado) com Pandas

Escrevendo DataFrames para Arquivos CSV com df.to_csv()


Sintaxe:
df.to_csv(caminho, sep=’,’, index=True, header=True, na_rep=’NaN’,
encoding=’utf-8’, ...)

• index=True (padrão): Escreve o índice do DataFrame. Use False para omitir.

• header=True (padrão): Escreve os nomes das colunas.

• na_rep: String para representar valores NaN.

• float_format: Formato para números float (ex: ’%.2f’).

1 dados_salvar = { ’ ID ’: [101 , 102] , ’ Produto ’: [ ’A ’ , ’B ’] , ’ Preco ’:


[19.99 , np . nan ]}
2 df _p ara _s al var _c sv = pd . DataFrame ( dados_salvar )
3 no me _ar q_ cs v_s ai da = " meu_df_salvo . csv "
4
5 try :
6 d f_ par a_ sa lva r_ cs v . to_csv ( nome_arq_csv_saida , index = False ,
7 sep = ’; ’ , na_rep = ’N / A ’ , float_format = ’ %.2 f ’)
8 print ( f " \ nDataFrame salvo em ’{ nom e_ ar q_c sv _s aid a } ’ " )
9 except Exception as e :
10 print ( f " Erro ao salvar CSV : { e } " )

Código 20.41: Salvando um DataFrame em arquivo CSV

20.9.2 Lendo e Escrevendo Arquivos Excel (.xls, .xlsx)


Pandas pode ler e escrever arquivos Excel. Dependências: Requer bibliotecas
adicionais como xlrd (para .xls) e openpyxl (para .xlsx). Instale com pip install
openpyxl xlrd.

Lendo Arquivos Excel com pd.read_excel()


Sintaxe:
pd.read_excel(caminho, sheet_name=0, header=0, index_col=None, ...)

• sheet_name=0: Lê a primeira planilha (pode ser nome ou índice). None lê todas.


402 Capítulo 20. Pandas para Análise e Manipulação de Dados

1 # Exemplo conceitual ( requer arquivo ’ dados . xlsx ’ e openpyxl )


2 nome_arq_excel = " dados . xlsx "
3 try :
4 df_excel_lido = pd . read_excel ( nome_arq_excel ,
sheet_name = " PlanilhaDeVendas " )
5 print ( f " \n - - - DataFrame lido do Excel
- - -\ n { df_excel_lido . head () } " )
6 except File NotFound Error : print ( f " Erro : Arquivo ’{ nome_arq_excel } ’
n ã o encontrado . " )
7 except ImportError : print ( " Erro : Instale ’ openpyxl ’ ( pip install
openpyxl ) . " )
8 except Exception as e : print ( f " Erro ao ler Excel : { e } " )
9 print ( " Exemplo de leitura de Excel " )

Código 20.42: Lendo de arquivo Excel (exemplo conceitual)

Escrevendo DataFrames para Arquivos Excel com df.to_excel()


Sintaxe:
df.to_excel(caminho, sheet_name=’Plan1’, index=True, header=True,
engine=’openpyxl’, ...)
• sheet_name: Nome da planilha no arquivo Excel.

• engine: Biblioteca a ser usada (ex: ’openpyxl’).

1 # Supondo um df_relatorio
2 d f _ r e l a t o r i o _ e x e m p l o = pd . DataFrame ({ ’ Dados ’: [1 ,2 ,3] ,
’ Resultados ’: [10 ,20 ,30]})
3 n o m e _ a r q _ e x c e l _ s a i d a = " relatorio_excel . xlsx "
4 try :
5 df _ r e l at o r i o _ e x e m p l o . to_excel ( nome_arq_excel_saida ,
6 sheet_name = " Resumo " , index = False )
7 print ( f " \ nDataFrame salvo em ’{ n o m e _ a r q _ e x c el _ s a i d a } ’ " )
8 except ImportError : print ( " Erro : Instale ’ openpyxl ’. " )
9 except Exception as e : print ( f " Erro ao salvar Excel : { e } " )
10 print ( " Exemplo de escrita em Excel . " )

Código 20.43: Salvando DataFrame em arquivo Excel (exemplo conceitual)

20.9.3 Breve Menção a Outros Formatos


Pandas suporta muitos outros formatos, como:
• JSON: pd.read_json(), df.to_json().

• SQL (Bancos de Dados): pd.read_sql_query(), df.to_sql() (requer SQLAl-


chemy ou conector).

• HTML: pd.read_html() (extrai tabelas de páginas web), df.to_html().

• HDF5, Parquet, Feather: Formatos binários eficientes para grandes datasets.


A capacidade de I/O (Entrada/Saída) do Pandas é uma de suas funcionalidades mais
importantes.
20.9. Leitura e Escrita de Arquivos com Pandas 403

Concluindo o Capítulo sobre Pandas


Neste capítulo, mergulhamos no universo da biblioteca Pandas, uma ferramenta indis-
pensável para qualquer pessoa que trabalhe com análise e manipulação de dados em Python.
Vimos como o Pandas simplifica e potencializa o trabalho com dados tabulares e séries
temporais através de suas duas estruturas de dados principais: a Series (unidimensional)
e, sobretudo, o DataFrame (bidimensional).
Começamos aprendendo a criar Series e DataFrames a partir de diversas fontes.
Em seguida, exploramos as poderosas formas de selecionar e indexar dados usando a
notação de colchetes [], os indexadores .loc[] (baseado em rótulos) e .iloc[] (baseado
em posição inteira), além da crucial indexação booleana.
Discutimos a importância de lidar com dados ausentes (NaN) e vimos como o
Pandas nos ajuda a identificar, remover (dropna()) ou preencher (fillna()) esses valores.
Exploramos uma gama de operações comuns e funções em DataFrames, incluindo
operações aritméticas com alinhamento de índices, aplicação de funções NumPy, cálculo
de estatísticas descritivas, o método apply(), ordenação e contagem de valores únicos.
Uma das funcionalidades mais poderosas, o agrupamento de dados (Group By),
nos mostrou como dividir dados em grupos, aplicar funções de agregação e combinar
os resultados. Também vimos como combinar múltiplos DataFrames através de
concatenação, mesclagem (merge) e junção (join).
Finalmente, cobrimos as funcionalidades essenciais do Pandas para leitura e escrita
de arquivos, com foco nos formatos CSV e Excel.
O Pandas, construído sobre o NumPy, não apenas oferece estruturas de dados eficientes,
mas também se integra com outras bibliotecas do ecossistema científico Python, como
Matplotlib e Seaborn. Dominar o Pandas abre um vasto leque de possibilidades para
a limpeza, transformação, análise e preparação de dados. É uma das bibliotecas mais
importantes para quem trabalha com dados em Python.
No próximo capítulo, continuaremos nossa exploração de bibliotecas essenciais, dando
uma introdução à biblioteca SciPy, que complementa o NumPy com uma grande coleção
de algoritmos científicos e de engenharia.
Capítulo 21

SciPy para Computação Científica


(Introdução)

Nos capítulos anteriores desta parte, mergulhamos no NumPy, aprendendo como ele
fornece a estrutura de ndarray para computação numérica eficiente e operações vetorizadas.
Vimos o Pandas para manipulação e análise de dados tabulares, e o Matplotlib junto com
o Seaborn para visualização de dados. Essas bibliotecas formam um núcleo poderoso para
quem trabalha com dados e computação técnica em Python.
Agora, vamos introduzir outra biblioteca fundamental no ecossistema científico do
Python: a SciPy (pronuncia-se “Sái-Pai”, de Scientific Python). Enquanto o NumPy
fornece a base de arrays e operações matemáticas fundamentais, a SciPy constrói sobre
essa base para oferecer uma vasta coleção de algoritmos e ferramentas de nível mais alto,
essenciais para diversas áreas da ciência e da engenharia.

21.1 Introdução ao SciPy: Ferramentas Científicas Avan-


çadas sobre NumPy
21.1.1 O que é SciPy?
A SciPy é uma biblioteca de código aberto em Python, parte do mesmo ecossistema
do NumPy, que fornece muitas rotinas numéricas amigáveis ao usuário e eficientes, como
otimização numérica, integração, interpolação, processamento de sinais, álgebra linear
avançada, estatísticas, processamento de imagens e muito mais.

21.1.2 Como a SciPy Complementa o NumPy?


É importante entender a relação entre NumPy e SciPy:

• NumPy: Fornece a estrutura de dados fundamental (ndarray) e as operações básicas


sobre esses arrays (matemática elemento a elemento, broadcasting, manipulação de
forma, etc.). Ele é o alicerce.

• SciPy: Utiliza os arrays NumPy como sua estrutura de dados base e implementa
sobre eles uma grande variedade de algoritmos científicos e de engenharia de alto
nível. Você pode pensar na SciPy como uma caixa de ferramentas mais especializada
que opera eficientemente sobre os “materiais de construção” fornecidos pelo NumPy.

404
21.1. Introdução ao SciPy: Ferramentas Científicas Avançadas sobre NumPy 405

Muitas vezes, funcionalidades que parecem pertencer ao domínio da computação numérica


(e que poderiam estar no NumPy) são, na verdade, implementadas no SciPy porque são
algoritmos mais complexos ou especializados. Por exemplo, enquanto o [Link]
oferece funções básicas de álgebra linear, o [Link] oferece uma gama ainda mais
ampla e, em alguns casos, implementações mais robustas.

21.1.3 Estrutura da SciPy: Uma Coleção de Submódulos


A biblioteca SciPy é organizada como uma coleção de submódulos, cada um focado
em um domínio específico da computação científica e técnica. Alguns dos submódulos
mais importantes e frequentemente utilizados incluem:

• [Link]: Algoritmos de agrupamento (clustering).

• [Link]: Constantes físicas e matemáticas.

• [Link] ou [Link]: Algoritmos de Transformada Rápida de Fourier


(FFT).

• [Link]: Rotinas para integração numérica (cálculo de integrais).

• [Link]: Ferramentas para interpolação de dados.

• [Link]: Leitura e escrita de dados em diversos formatos (ex: arquivos MATLAB,


NetCDF).

• [Link]: Funções de álgebra linear avançadas (complementando [Link]).

• [Link]: Funções para processamento de imagens N-dimensionais.

• [Link]: Algoritmos de otimização (minimização/maximização de funções)


e busca de raízes.

• [Link]: Ferramentas para processamento de sinais (filtros, análise espectral).

• [Link]: Suporte para matrizes esparsas e suas rotinas de álgebra linear.

• [Link]: Algoritmos para estruturas espaciais e geometria computacional.

• [Link]: Um grande conjunto de distribuições de probabilidade e funções


estatísticas.

21.1.4 Convenção de Importação


Diferentemente do NumPy (importado como np) ou Pandas (como pd), não existe um
alias padrão único para importar a biblioteca SciPy inteira, pois ela é muito grande e
geralmente você só precisa de funcionalidades de submódulos específicos.
A prática comum é importar apenas os submódulos que você necessita. Por exemplo:
1 from scipy import stats # Para usar funcionalidades estat í sticas
2 from scipy . optimize import minimize # Para usar a fun ç ã o de
minimiza ç ã o
3 import scipy . linalg as spla # Dando um alias para o subm ó dulo
linalg do SciPy
406 Capítulo 21. SciPy para Computação Científica (Introdução)

4 # Ou , se for usar v á rias fun ç õ es de um subm ó dulo :


5 # import scipy . interpolate
6 # resultado = scipy . interpolate . interp1d (...)

Código 21.1: Exemplos de importação de submódulos SciPy

21.1.5 O que Esperar Deste Capítulo (Uma Introdução)


Dado o vasto escopo da SciPy, este capítulo servirá como uma introdução a algumas
das suas funcionalidades mais relevantes para um público geral de engenharia e ciências.
Não poderemos cobrir todos os submódulos, nem nos aprofundaremos exaustivamente em
cada um. O objetivo é:

• Mostrar o tipo de problema que a SciPy ajuda a resolver.

• Apresentar exemplos básicos de uso de alguns dos seus submódulos mais populares.

• Incentivar você a explorar a documentação oficial da SciPy ([Link]


org/doc/scipy/) para encontrar as ferramentas específicas para suas necessidades
futuras.

Vamos começar explorando algumas das funções matemáticas especiais que a SciPy oferece.

21.2 Funções Especiais ([Link]): Indo Além da


Matemática Básica
Enquanto o módulo math da biblioteca padrão do Python e as funções universais do
NumPy nos fornecem um conjunto robusto de operações matemáticas comuns (trigono-
métricas, exponenciais, logarítmicas, etc.), existem muitas outras funções matemáticas,
conhecidas como funções especiais, que são cruciais em diversas áreas da ciência, enge-
nharia e estatística.
O submódulo [Link] da biblioteca SciPy é dedicado a fornecer implementa-
ções eficientes e precisas de um grande número dessas funções especiais. Essas funções
frequentemente aparecem em soluções de equações diferenciais, problemas de física, teoria
das probabilidades, processamento de sinais, entre outros campos.
Não é nosso objetivo aqui detalhar a teoria matemática por trás de cada uma dessas
funções – muitas delas requerem um conhecimento matemático mais aprofundado. Em vez
disso, queremos que você saiba que elas existem, que estão facilmente acessíveis através do
[Link], e mostrar como usar algumas delas com exemplos simples.

21.2.1 Importando o Submódulo special


Para usar as funções deste submódulo, você geralmente o importa da seguinte maneira:
1 from scipy import special
2 # Ou , se preferir um alias mais curto ( menos comum para este
subm ó dulo ) :
3 # import scipy . special as sps

Código 21.2: Importando [Link]


21.2. Funções Especiais ([Link]): Indo Além da Matemática Básica 407

21.2.2 Alguns Exemplos de Funções Especiais Comuns


Vamos dar uma olhada em algumas funções especiais que podem ser úteis:

1. Função Gama ([Link](z)):


A função Gama, denotada por Γ(z), é uma generalização da função fatorial para
números reais e complexos. Para um inteiro positivo n, temos Γ(n) = (n − 1)!. Ela
aparece em muitas áreas, incluindo estatística (por exemplo, na distribuição Gama)
e teoria das probabilidades.
1 import numpy as np
2 from scipy import special
3 import matplotlib . pyplot as plt
4
5 # Calculando alguns valores da fun ç ã o Gama
6 gamma_de_5 = special . gamma (5) # Deve ser 4! = 24
7 print ( f " Gamma (5) = (5 -1) ! = 4! = { gamma_de_5 } " )
8
9 gamma_de_3_5 = special . gamma (3.5)
10 print ( f " Gamma (3.5) = { gamma_de_3_5 :.4 f } " ) #
Aproximadamente 3.3234
11
12 # A fun ç ã o fatorial tamb é m est á dispon í vel diretamente
13 f a t or i al _ d e_ 4 _s c ip y = special . factorial (4)
14 print ( f " Fatorial de 4 ( via scipy . special ) :
{ f a to r ia l _ de _ 4_ s c ip y } " )

Código 21.3: Usando a função Gama e Fatorial de [Link]

2. Função de Erro e Função de Erro Complementar:


A função de erro (erf(z)) está intimamente relacionada com a função de distribuição
cumulativa da distribuição normal (Gaussiana). Ela é definida como:
Z z
2 2
erf(z) = √ e−t dt
π 0

A função de erro complementar é erfc(z) = 1 − erf(z). Essas funções aparecem em


probabilidade, estatística, equações diferenciais parciais (como a equação do calor) e
em problemas de difusão.
1 # ( Supondo ’ from scipy import special ’ j á executado )
2 valor_erf_1 = special . erf (1.0)
3 print ( f " \ nerf (1.0) = { valor_erf_1 :.5 f } " ) # Aproximadamente
0.84270
4
5 valor_erfc_1 = special . erfc (1.0)
6 print ( f " erfc (1.0) = { valor_erfc_1 :.5 f } " ) # Aproximadamente
0.15730
7 print ( f " Verifica ç ã o : erf (1.0) + erfc (1.0) = { valor_erf_1 +
valor_erfc_1 } " )
8 # Deve ser pr ó ximo de 1.0

Código 21.4: Usando as funções de erro erf e erfc

3. Funções de Bessel ([Link](ordem, z), [Link](ordem, z), etc.):


As funções de Bessel são soluções para a equação diferencial de Bessel e surgem em
408 Capítulo 21. SciPy para Computação Científica (Introdução)

uma enorme variedade de problemas físicos e de engenharia que envolvem ondas,


como propagação de ondas eletromagnéticas em guias de onda cilíndricos, modos de
vibração de uma membrana circular (como um tambor), problemas de condução de
calor em cilindros, entre outros.

• [Link](v, z): Função de Bessel de primeira espécie de ordem v.


• [Link](v, z): Função de Bessel de segunda espécie de ordem v.

Vamos plotar as primeiras funções de Bessel de primeira espécie para ter uma ideia
de sua forma:
1 # ( Supondo imports de np , special , e plt j á executados )
2 x_bessel = np . linspace (0 , 15 , 200) # Valores de x de 0 a 15
3
4 plt . figure ( figsize =(10 , 6) )
5 for ordem_v_bessel in range (4) : # Plotar para ordens v =
0, 1, 2, 3
6 plt . plot ( x_bessel , special . jn ( ordem_v_bessel , x_bessel ) ,
7 label = f ’ $J_ {{{ ordem_v_bessel }}}( x ) $ ’) # Usando f - string
com chaves duplas para LaTeX
8
9 plt . title ( " Fun ç õ es de Bessel de Primeira Esp é cie
$J_v ( x ) $ " , fontsize =15)
10 plt . xlabel ( " $x$ " , fontsize =12)
11 plt . ylabel ( " $J_v ( x ) $ " , fontsize =12)
12 plt . legend ()
13 plt . grid ( True , linestyle = ’: ’ , alpha =0.7)
14 plt . axhline (0 , color = ’ black ’ , linewidth =0.5)
15 plt . ylim ( -0.5 , 1.1)
16 plt . show ()

Código 21.5: Plotando Funções de Bessel de Primeira Espécie

O gráfico mostrará curvas oscilatórias que decaem em amplitude, características das


funções de Bessel.

4. Polinômios Ortogonais (Breve Menção):


O submódulo [Link] também contém funções para gerar e avaliar diversos ti-
pos de polinômios ortogonais, como os polinômios de Legendre ([Link](n)
retorna um objeto polinomial), Hermite ([Link](n)), Laguerre
([Link](n)) e Chebyshev ([Link](n)). Esses polinômios são
fundamentais em aproximação de funções, soluções de equações diferenciais e em
métodos numéricos.
1 # ( Supondo ’ from scipy import special ’ j á executado )
2 grau_ do_polin omio = 3
3 p ol ino mi o_ leg en dr e = special . legendre ( grau _do_polin omio )
4 print ( f " \ nPolin ô mio de Legendre de grau
{ grau_do_ polinomi o }: " )
5 print ( po li no mio _l eg end re )
6 # Sa í da ( os coeficientes ) : poly1d ([ 2.5 , 0. , -1.5 , 0.
]) ( significa 2.5 x ^3 - 1.5 x )
7 print ( f " Ra í zes do polin ô mio de Legendre :
{ po li no mio _l eg end re . roots } " )

Código 21.6: Exemplo com Polinômio de Legendre


21.3. Álgebra Linear com [Link] (Complementando NumPy) 409

21.2.3 Explorando Mais Funções Especiais


A lista de funções especiais disponíveis em [Link] é vasta e cobre muitas
necessidades de diferentes campos da matemática aplicada, física e engenharia. Algumas
outras categorias incluem funções elípticas, funções de Airy, funções hipergeométricas,
funções de Struve, e muitas outras.
Para a maioria dos usos do dia a dia em Python básico, você pode não precisar dessas
funções com tanta frequência. No entanto, quando você se deparar com problemas que
envolvem equações diferenciais específicas, teoria de ondas, certas integrais que não têm
soluções em termos de funções elementares, ou distribuições estatísticas mais complexas, é
muito provável que a função de que você precisa já esteja implementada e otimizada no
[Link].
A melhor abordagem é sempre consultar a documentação oficial do [Link]
([Link] quando você precisar
de uma função matemática que não é encontrada nos módulos math ou NumPy.
O submódulo [Link] é um exemplo de como a SciPy estende o NumPy para
fornecer ferramentas de nível mais alto, prontas para uso em domínios científicos e de
engenharia.

21.3 Álgebra Linear com [Link] (Complemen-


tando NumPy)
No Capítulo 18, ao explorarmos o NumPy, tivemos nosso primeiro contato com opera-
ções de álgebra linear através do submódulo [Link]. Vimos como realizar tarefas
essenciais como multiplicação de matrizes, cálculo de inversas, determinantes e solução de
sistemas lineares. Essas são, de fato, algumas das operações mais comuns e fundamentais.
A biblioteca SciPy, no entanto, leva a capacidade de realizar álgebra linear em Python
um passo adiante com seu próprio submódulo [Link]. Este submódulo não
apenas contém todas as funcionalidades presentes em [Link] (muitas vezes, as
funções do SciPy são invólucros ou versões aprimoradas das do NumPy), mas também as
expande significativamente. [Link] oferece uma gama mais ampla de algoritmos,
decomposições de matrizes mais especializadas e, em alguns casos, implementações que
podem ser mais robustas numericamente ou otimizadas, especialmente se a sua instalação
do SciPy estiver vinculada a bibliotecas de Álgebra Linear de baixo nível altamente
eficientes como BLAS (Basic Linear Algebra Subprograms) e LAPACK (Linear Algebra
Package).

21.3.1 Relação com [Link]


Para muitas das operações fundamentais, as funções em [Link] e [Link]
podem parecer intercambiáveis, e de fato, [Link] é construído sobre a base sólida
dos arrays NumPy. Se você já está usando NumPy e precisa de uma operação básica como
calcular um determinante ou uma inversa simples, [Link] pode ser suficiente.
No entanto, a documentação oficial do SciPy geralmente recomenda o uso das funções
de [Link] em vez das de [Link] para aplicações que exigem o máximo de
precisão ou que podem se beneficiar das funcionalidades mais avançadas ou das otimizações
específicas que o SciPy pode oferecer. Em resumo, pense no [Link] como um
superconjunto mais completo e especializado.
410 Capítulo 21. SciPy para Computação Científica (Introdução)

A convenção de importação, se você for usar extensivamente este submódulo, pode ser:
1 import numpy as np
2 import scipy . linalg as spla # spla é um alias comum para
scipy . linalg
3
4 # Ou , para fun ç õ es espec í ficas , o que pode ser mais claro :
5 # from scipy . linalg import inv , det , svd , lu , expm

Código 21.7: Importando [Link]

21.3.2 Funcionalidades Adicionais ou Aprimoradas em [Link]


Vamos explorar algumas áreas onde [Link] realmente brilha e oferece mais do
que o [Link] básico.

Decomposições (ou Fatorações) de Matrizes


As decomposições de matrizes são técnicas fundamentais na álgebra linear que envolvem
expressar uma matriz como um produto de outras matrizes que são mais simples ou que
possuem propriedades especiais (por exemplo, serem triangulares, diagonais ou ortogonais).
Essas fatorações são a base para muitos algoritmos importantes, incluindo a solução
eficiente de sistemas lineares, o cálculo de determinantes e inversas, e têm aplicações vastas
em áreas como estatística (ex: mínimos quadrados), aprendizado de máquina (ex: redução
de dimensionalidade) e engenharia (ex: análise de sistemas).

• Decomposição LU ([Link](A)): Fatora uma matriz quadrada A no produto


A = P LU .

– P é uma matriz de permutação que representa as trocas de linha realizadas


durante o processo de eliminação gaussiana para garantir estabilidade numérica
(pivotamento).
– L é uma matriz triangular inferior com 1s na sua diagonal principal.
– U é uma matriz triangular superior.

Esta decomposição é uma forma robusta e eficiente de resolver sistemas lineares


Ax = b. Primeiro, fatora-se A em P LU , então o sistema se torna P LU x = b.
Multiplicando por P T (que é P −1 para matrizes de permutação), temos LU x = P T b.
Resolve-se então Ly = P T b para y (usando substituição para frente, pois L é
triangular inferior) e, em seguida, U x = y para x (usando substituição para trás,
pois U é triangular superior). A função spla.lu_factor e spla.lu_solve no SciPy
são otimizadas para este processo.

• Decomposição QR ([Link](A)): Fatora uma matriz A (não necessariamente


quadrada) no produto A = QR.

– Q é uma matriz ortogonal (ou unitária, se A for complexa), o que significa


que suas colunas são vetores ortonormais, e QT Q = I (ou QH Q = I).
– R é uma matriz triangular superior.
21.3. Álgebra Linear com [Link] (Complementando NumPy) 411

A decomposição QR é fundamental em algoritmos de mínimos quadrados, para


resolver sistemas lineares sobredeterminados e para o cálculo de autovalores (algoritmo
QR).

• Decomposição de Cholesky ([Link](A, lower=False)): Se a matriz A


é simétrica (ou Hermitiana, se complexa) e positiva definida, esta decomposição
a fatora em A = U T U (onde U é triangular superior, se lower=False, que é o
padrão) ou A = LLT (onde L é triangular inferior, se lower=True). A decomposição
de Cholesky é numericamente estável e cerca de duas vezes mais eficiente que a
decomposição LU para resolver sistemas lineares com matrizes simétricas e positivas
definidas.

• Decomposição em Valores Singulares (SVD - [Link](A))


A SVD é, possivelmente, uma das decomposições matriciais mais importantes e
versáteis. Ela fatora qualquer matriz A (mesmo que seja retangular ou singular) na
forma A = U ΣV H . (Para matrizes reais, V H é simplesmente a transposta V T ).

– U : Uma matriz unitária (ou ortogonal para A real) cujas colunas são os vetores
singulares à esquerda.
– Σ: Uma matriz retangular diagonal (com a mesma forma de A após preenchi-
mento com zeros, se necessário) cujos elementos da diagonal são os valores
singulares de A. Estes são reais, não negativos e geralmente listados em ordem
decrescente.
– V H : A transposta Hermitiana (ou apenas transposta para A real) de uma
matriz unitária/ortogonal V , cujas colunas são os vetores singulares à direita.

A SVD tem uma vasta gama de aplicações, incluindo:

– Cálculo da pseudo-inversa de Moore-Penrose (para matrizes não quadradas ou


singulares).
– Solução de problemas de mínimos quadrados.
– Análise de Componentes Principais (PCA) e redução de dimensionalidade.
– Compressão de imagens e dados.
– Sistemas de recomendação.
– Determinação do posto (rank) e do espaço nulo de uma matriz.

Vamos ver um exemplo prático com LU e SVD:


1 import numpy as np
2 import scipy . linalg as spla
3

4 # Matriz de exemplo para decomposi ç õ es


5 matriz_M_ex = np . array ([[2. , 1. , 1.] ,
6 [4. , -6. , 0.] ,
7 [ -2. , 7. , 2.]])
8 print ( f " --- Matriz Original M_ex : - - -\ n { matriz_M_ex } " )
9

10 # Decomposi ç ã o LU
11 # P é a matriz de permuta ç ão , L triangular inferior , U triangular
superior
412 Capítulo 21. SciPy para Computação Científica (Introdução)

12 # tal que P @ matriz_M_ex = L @ U ( esta é a conven ç ã o do output de


spla . lu )
13 # ou A = P_transpose @ L @ U
14 P_lu , L_lu , U_lu = spla . lu ( matriz_M_ex )
15 print ( f " \n - - - Decomposi ç ã o LU de M_ex ---" )
16 print ( f " P ( Permuta ç ã o ) tal que P@A = L@U :\ n { P_lu } " )
17 print ( f " L ( Triangular Inferior ) :\ n { np . round ( L_lu ,3) } " )
18 print ( f " U ( Triangular Superior ) :\ n { np . round ( U_lu ,3) } " )
19 # Verifica ç ã o : P @ matriz_M_ex deve ser pr ó ximo de L @ U
20 print ( f " P@M_ex :\ n { np . round ( P_lu @ matriz_M_ex , 3) } " )
21 print ( f " L@U :\ n { np . round ( L_lu @ U_lu , 3) } " )
22 print ( f " S ã o pr ó ximos ? { np . allclose ( P_lu @ matriz_M_ex , L_lu @
U_lu ) } " )
23
24
25 # Decomposi ç ã o SVD ( Singular Value Decomposition )
26 # U e Vh s ã o unit á rias / ortogonais . s_vetor_singular cont é m os
valores singulares .
27 U_svd , s_vetor_singular , Vh_svd = spla . svd ( matriz_M_ex )
28 print ( f " \n - - - Decomposi ç ã o SVD de M_ex ---" )
29 print ( f " U ( Matriz Unit á ria / Ortogonal ) :\ n { np . round ( U_svd , 3) } " )
30 print ( f " s ( Valores Singulares - vetor
1 D ) :\ n { np . round ( s_vetor_singular , 3) } " )
31 print ( f " Vh ( Matriz Unit á ria / Ortogonal
Transposta / Hermitiana ) :\ n { np . round ( Vh_svd , 3) } " )
32
33 # Para reconstruir M_ex , precisamos transformar ’ s_vetor_singular ’
em uma matriz diagonal Sigma
34 Sigma_matriz_svd = np . zeros ( matriz_M_ex . shape )
35 # Preenche a diagonal de Sigma com os valores singulares
36 # O n ú mero de valores singulares é min ( linhas , colunas )
37 min_dim_svd = min ( matriz_M_ex . shape )
38 Sigma_matriz_svd [: min_dim_svd , : min_dim_svd ] =
np . diag ( s_vetor_singular [: min_dim_svd ])
39
40 M_ re con st ru ida _s vd = U_svd @ Sigma_matriz_svd @ Vh_svd
41 print ( f " \ nM_ex reconstru í da a partir de U @ Sigma @
Vh :\ n { np . round ( M_reconstruida_svd , 3) } " )
42 print ( f " As matrizes M_ex e M _re co ns tru id a_ svd s ã o pr ó ximas ?
{ np . allclose ( matriz_M_ex , M_ rec on st rui da _s vd ) } " )

Código 21.8: Exemplo de Decomposições LU e SVD com [Link]

Funções de Matrizes
São funções matemáticas aplicadas à matriz como um todo, e não elemento a elemento
(como as ufuncs do NumPy). Seu cálculo geralmente envolve conceitos como séries de
Taylor para matrizes ou decomposições espectrais.
2 3
• Exponencial de Matriz ([Link](A)): Calcula eA = I + A + A2! + A3! + . . . . É
crucial em sistemas de equações diferenciais lineares (y ′ = Ay ⇒ y(t) = eAt y(0)).
• Logaritmo de Matriz ([Link](A)): A função inversa do exponencial de matriz.
• Raiz Quadrada de Matriz ([Link](A)): Encontra uma matriz B tal que
B 2 = A. (Note que esta é diferente de [Link](A), que calcula a raiz quadrada de
cada elemento).
21.3. Álgebra Linear com [Link] (Complementando NumPy) 413

• Outras como [Link](A) (seno de matriz), [Link](A) (cosseno de matriz),


etc.

1 # ( Supondo import numpy as np e import scipy . linalg as spla )


2 m a t r i z _ p a r a _ e x p o n e n c i a l = np . array ([[1. , 4.] , # Usando floats
3 [1. , 1.]])
4 e x p o n e n c i a l _ d a _ m a t r i z = spla . expm ( m a t r i z _ p a r a _ e x p o n e n c i a l )
5 print ( f " \ nMatriz A para exponencial :\ n { m a t r i z _ p a r a _ e x p o n e n c i a l } " )
6 print ( f " Exponencial da Matriz e ^ A ( calculada por
scipy . linalg . expm ) :\ n { np . round ( exponencial_da_matriz , 3) } " )
7
8 # Para compara ç ão , o np . exp () elemento a elemento :
9 e x p _ e l e m e n t o _ a _ e l e m e n t o = np . exp ( m a t r i z _ p a r a _ e x p o n e n c i a l )
10 print ( f " np . exp ( A ) ( elemento a elemento , diferente de
expm ( A ) ) :\ n { np . round ( exp_elemento_a_elemento , 3) } " )

Código 21.9: Exponencial de matriz com [Link]

Outras Funções Úteis em [Link]


O submódulo [Link] é vasto e inclui muitas outras funções, como:

• spla.solve_triangular(a, b, lower=False, ...): Resolve sistemas lineares


Ax = b de forma muito eficiente quando A é uma matriz triangular (inferior se
lower=True, superior se lower=False).

• Funções para calcular diferentes normas de matrizes e vetores (ex: [Link](a,


ord=None, axis=None, ...)), que medem o "tamanho"ou "magnitude".

• Funções para construir matrizes especiais (Hankel, Toeplitz, Hilbert, etc.).

• Solucionadores de mínimos quadrados mais gerais ([Link]).

21.3.3 Quando Usar [Link] vs. [Link]?


• Para operações muito básicas e comuns como determinante (det), inversa (inv),
solução de sistemas lineares simples (solve), cálculo de autovalores/autovetores
(eig), e SVD (svd), as funções correspondentes em [Link] são geralmente
suficientes e podem já estar no seu escopo de importação se você usa NumPy
extensivamente.

• O [Link] é preferível ou necessário quando:

– Você precisa de algoritmos de decomposição mais especializados (LU, QR,


Cholesky, etc.) ou mais opções e controle sobre eles.
– Você precisa de funções de matrizes (como expm, logm, sqrtm).
– Você quer ter a certeza de que está usando implementações que podem ser
ligadas a bibliotecas BLAS/LAPACK altamente otimizadas, pois o SciPy tende
a gerenciar melhor essas ligações em sua compilação e distribuição.
– A documentação do [Link] para essas funções mais avançadas é frequen-
temente mais detalhada e inclui mais referências aos algoritmos subjacentes.
414 Capítulo 21. SciPy para Computação Científica (Introdução)

Em muitos casos comuns, as funcionalidades se sobrepõem. No entanto, é bom saber que


[Link] oferece um conjunto de ferramentas mais completo e, para certos problemas,
mais especializado e potencialmente mais otimizado para álgebra linear robusta em Python.

21.4 Otimização e Busca de Raízes ([Link]):


Encontrando Soluções e Pontos Ótimos
Muitos problemas em engenharia e ciências envolvem encontrar as “melhores” soluções
(otimização) ou resolver equações para as quais não temos uma fórmula direta (busca de
raízes). O submódulo [Link] da SciPy oferece algoritmos para essas tarefas.

• Otimização: Encontrar o valor mínimo (ou máximo) de uma função, possivelmente


sujeito a restrições.

• Busca de Raízes: Encontrar os valores de x para os quais uma função f (x) = 0.

Para usá-las, importe as funções específicas:


1 import numpy as np
2 from scipy . optimize import root_scalar , minimize_scalar , minimize
3 import matplotlib . pyplot as plt # Para visualiza ç ã o opcional

Código 21.10: Importações comuns para [Link]

21.4.1 Busca de Raízes de Funções Escalares (Uma Variável)


O problema é encontrar x tal que f (x) = 0. A função [Link].root_scalar(f,
bracket=None, x0=None, method=None, ...) é uma interface geral.

• f: A função (callable).

• bracket: Um intervalo [a,b] onde se espera a raiz (para métodos como ’brentq’,
’bisect’).

• method: String do método (ex: ’brentq’).

Retorna um objeto RootResults com .root (a raiz) e .converged (booleano).


Exemplo: Encontrando a raiz de f (x) = x3 − x − 2
1 # Fun ç ã o para a qual queremos encontrar a raiz f ( x ) = 0
2 def f u n c a o _ c u b i c a _ e x e m p l o ( x ) :
3 return x **3 - x - 2 # Raiz real pr ó xima a 1.52
4
5 print ( " --- Buscando Raiz para f ( x ) = x ^3 - x - 2 ---" )
6 try :
7 so l uc a o_ r a iz _ cu b i ca = root_scalar ( funcao_cubica_exemplo ,
8 bracket =[1 , 2] , method = ’ brentq ’)
9 if s o lu c a o_ r ai z _c u b ic a . converged :
10 raiz_encontrada = s ol u ca o _ ra i z_ c ub i c a . root
11 print ( f " Raiz encontrada : x = { raiz_encontrada :.6 f } " )
12 print ( f " f ( x ) na raiz :
{ f u n c a o _ c u b i c a _ e x e m p l o ( raiz_encontrada ) :.2 e } " )
13 print ( f " Itera ç õ es : { s ol u ca o _ ra i z_ c ub i c a . iterations } " )
14 else :
21.4. Otimização e Busca de Raízes ([Link]): Encontrando Soluções e Pontos
Ótimos 415
15 print ( " Busca de raiz n ã o convergiu . " )
16 except ValueError as e_raiz :
17 print ( f " Erro ao buscar raiz ( verifique o intervalo ) : { e_raiz } " )
18
19 # Exemplo : cos ( x ) - x = 0
20 def f u n c a o _ c o s _ m e n o s _ x _ e x e m p l o ( x ) :
21 return np . cos ( x ) - x
22
23 print ( " \n - - - Buscando Raiz para f ( x ) = cos ( x ) - x ---" )
24 try :
25 sol_cos_x = root_scalar ( funcao_cos_menos_x_exemplo ,
26 bracket =[0 , np . pi /2] , method = ’ brentq ’)
27 if sol_cos_x . converged :
28 print ( f " Raiz de cos ( x ) - x = 0 em : x =
{ sol_cos_x . root :.6 f } " )
29 except ValueError as e_cos_raiz :
30 print ( f " Erro ao buscar raiz para cos ( x ) -x =0: { e_cos_raiz } " )
31

32 # ( C ó digo de plot opcional omitido no LaTeX para brevidade ,


33 # mas presente no " Conte ú do da Se ç ã o " original )

Código 21.11: Buscando raízes com [Link].root_scalar

21.4.2 Otimização (Minimização) de Funções Escalares (Uma


Variável)
Para encontrar x que minimiza f (x), use [Link].minimize_scalar
(f, bracket=None, bounds=None, method=’brent’, ...).
• f: Função a ser minimizada.

• bracket: Trio (a,b,c) com f (b) < f (a), f (c) ou intervalo (a,b).

• bounds: Par (min,max) para restringir a busca.

• method: Ex: ’brent’ (padrão), ’bounded’.


Retorna um objeto OptimizeResult com .x (mínimo) e .fun (valor da função no mínimo).
Exemplo: Minimizando funções
1 # ( Supondo ’ from scipy . optimize import minimize_scalar ’ e ’ import
numpy as np ’)
2 def f u n c a o _ p a r a b o l a _ e x e m p l o ( x ) :
3 return ( x - 3) **2 + 1
4
5 print ( " \n - - - Minimiza ç ã o de f ( x ) = (x -3) ^2 + 1 ---" )
6 r e s u l t a d o _ m i n _ p a r a b o l a = minimize_scalar ( f u n c a o _ p a r a b o l a _ e x e m p l o )
7 if r e s u l t a d o _ m i n _ p a r a b o l a . success :
8 print ( f " M í nimo em x = { r e s u l t a d o _ m i n _ p a r a b o l a . x :.6 f } , f ( x ) =
{ r e s u l t a d o _ m i n _ p a r a b o l a . fun :.6 f } " )
9 else :
10 print ( " Minimiza ç ã o da par á bola falhou . " )
11
12 def f u n c a o _ x _ s i n _ x _ e x e m p l o ( x ) :
13 return x * np . sin ( x )
14
15 print ( " \n - - - Minimiza ç ã o de f ( x ) = x * sin ( x ) em [0 , 10] ---" )
416 Capítulo 21. SciPy para Computação Científica (Introdução)

16 r e s u l t a d o _ m i n _ x _ s i n _ x = minimize_scalar ( funcao_x_sin_x_exemplo ,
17 bounds =(0 , 10) , method = ’ bounded ’)
18 if r e s u l t a d o _ m i n _ x _ s i n _ x . success :
19 print ( f " M í nimo em x = { r e s u l t a d o _ m i n _ x _ s i n _ x . x :.6 f } , f ( x ) =
{ r e s u l t a d o _ m i n _ x _ s i n _ x . fun :.6 f } " )
20 else :
21 print ( " Minimiza ç ã o de x * sin ( x ) falhou . " )
22 # ( C ó digo de plot opcional omitido no LaTeX )

Código 21.12: Minimizando funções escalares com minimize_scalar

21.4.3 Otimização (Minimização) de Funções Multivariáveis (Breve


Introdução)
Para encontrar o vetor x = (x1 , ..., xn ) que minimiza f (x), use
[Link](fun, x0_inicial, method=None, ...).
• fun: Função objetivo (aceita array 1D, retorna escalar).
• x0_inicial: Estimativa inicial (array 1D) para x.
• method: Algoritmo (ex: ’Nelder-Mead’ (não requer gradientes), ’BFGS’, ’L-BFGS-B’,
’SLSQP’ (para restrições)).
Exemplo: Minimizando a função de Rosenbrock
1 # ( Supondo ’ from scipy . optimize import minimize ’ e ’ import numpy as
np ’)
2 def rosenbrock_func ( x_vetor ) : # x_vetor é [x , y ]
3 return (1 - x_vetor [0]) **2 + 100 * ( x_vetor [1] -
x_vetor [0]**2) **2
4
5 p o n t o _ d e _ p a r t i d a _ r o s e n = np . array ([2.0 , -1.0])
6 r e su lta d o_ m i n_ r os e n = minimize ( rosenbrock_func ,
ponto_de_partida_rosen ,
7 method = ’ Nelder - Mead ’ , tol =1 e -7)
8
9 print ( " \n - - - Minimiza ç ã o da Fun ç ã o de Rosenbrock ---" )
10 if r es u lt a do _ m in _ ro s e n . success :
11 print ( f " M í nimo em : x = { np . round ( re s ul t a do _ mi n _r o s en .x , 4) } " )
12 print ( f " Valor da f ( x ) no m í nimo : { r e s ul t ad o _m i n _r o se n . fun :.3 e } " )
13 print ( f " Mensagem : { re s ul t ad o _ mi n _r o s en . message } " )
14 else :
15 print ( f " Minimiza ç ã o falhou : { r es u l ta d o_ m in _ r os e n . message } " )

Código 21.13: Minimizando a função de Rosenbrock com minimize


A otimização multivariável pode ser complexa. A escolha do método e boas estimativas
iniciais são cruciais. O submódulo [Link] é uma ferramenta poderosa para essas
tarefas.

21.5 Interpolação ([Link]): Estimando Va-


lores Entre Pontos de Dados
Em muitas situações em engenharia e ciências, obtemos dados experimentais ou
resultados de simulações em um conjunto discreto de pontos. No entanto, frequentemente
21.5. Interpolação ([Link]): Estimando Valores Entre Pontos de Dados417

precisamos estimar qual seria o valor da nossa variável de interesse em pontos entre aqueles
que foram medidos ou calculados. Esse processo de estimar valores intermediários com
base em um conjunto de pontos de dados conhecidos é chamado de interpolação.
O submódulo [Link] da biblioteca SciPy oferece uma variedade de ferra-
mentas para realizar diferentes tipos de interpolação. Para este nosso livro “descomplicado”,
focaremos na interpolação unidimensional (1D).

O que é Interpolação?
Se temos pontos de dados (x0 , y0 ), (x1 , y1 ), . . . , (xn , yn ) de uma função desconhecida
y = f (x), a interpolação busca estimar y para um x que esteja entre os xi conhecidos. A
ideia é encontrar uma função que passe através (ou próximo) dos pontos conhecidos e
usá-la para calcular valores em pontos intermediários.

Interpolação Unidimensional com [Link].interp1d


A principal ferramenta para interpolação 1D no [Link] é a classe interp1d.
Sintaxe de Criação: funcao_interpoladora = interp1d(x_conhecidos,
y_conhecidos, kind=’linear’, bounds_error=True, fill_value=[Link], ...)
• x_conhecidos: Array 1D com as coordenadas x dos pontos conhecidos (devem ser
crescentes).

• y_conhecidos: Array 1D com as coordenadas y correspondentes.

• kind (opcional): Tipo de interpolação. Comuns:

– ’linear’ (padrão): Interpolação linear (segmentos de reta).


– ’nearest’: Valor do vizinho mais próximo.
– ’zero’, ’slinear’, ’quadratic’, ’cubic’: Splines de ordem 0, 1, 2, 3 (curvas
mais suaves).

• bounds_error=True (padrão): Levanta ValueError se tentar interpolar fora do


intervalo de x_conhecidos.

• fill_value (opcional): Se bounds_error=False, valor usado para pontos fora do


intervalo (padrão NaN, ou pode ser "extrapolate").
A chamada a interp1d retorna um objeto função que você pode usar para estimar y
para novos valores de x.
Exemplo Prático: Interpolando Dados de um Sensor
1 import numpy as np
2 from scipy . interpolate import interp1d # Importa a fun ç ã o
3 import matplotlib . pyplot as plt
4
5 # Dados conhecidos ( tempo em segundos , temperatura em Celsius )
6 tempo_conhecido = np . array ([0 , 5 , 10 , 15 , 20 , 25 , 30])
7 temp_conhecida = np . array ([22 , 24 , 23 , 25 , 26 , 24 , 23])
8

9 # 1. Interpola ç ã o Linear
10 interp_linear = interp1d ( tempo_conhecido , temp_conhecida ,
kind = ’ linear ’)
418 Capítulo 21. SciPy para Computação Científica (Introdução)

11
12 # 2. Interpola ç ã o C ú bica ( pode precisar de mais pontos ou dados
suaves )
13 try :
14 i nterp_cubica = interp1d ( tempo_conhecido , temp_conhecida ,
kind = ’ cubic ’)
15 except ValueError as e_interp_cub :
16 print ( f " Aviso : N ã o foi poss í vel criar interpola ç ã o c ú bica
({ e_interp_cub }) . Usando linear para ’c ú bica ’. " )
17 interp_cubica = interp_linear # Fallback para linear
18
19 # Novos pontos de tempo para estimativa
20 te mp o_n ov o_ est im ar = np . linspace ( min ( tempo_conhecido ) ,
max ( tempo_conhecido ) , 200)
21
22 t e m p _ e s t i m a d a _ l i n e ar = interp_linear ( te mp o_ nov o_ es tim ar )
23 t e m p _ e s t i m a d a _ c u b i ca = interp_cubica ( te mp o_ nov o_ es tim ar )
24

25 # Tentando interpolar fora do intervalo ( extrapola ç ã o )


26 i n t e r p _ c o m _ e x t r a p o l a c a o = interp1d ( tempo_conhecido , temp_conhecida ,
27 kind = ’ linear ’ ,
28 bounds_error = False ,
29 fill_value = " extrapolate " )
30 temp_aos_35s = i n t e r p _ c o m _ e x t r a p o l a c a o (35)
31 t emp_aos_ menos_5s = i n t e r p _ c o m _ e x t r a p o l a c a o ( -5)
32 print ( f " \ nTemperatura extrapolada para t =35 s : { temp_aos_35s :.2 f } " )
33 print ( f " Temperatura extrapolada para t = -5 s :
{ temp_ao s_menos_5 s :.2 f } " )
34
35 # Plotando os resultados
36 plt . figure ( figsize =(11 , 6.5) )
37 plt . plot ( tempo_conhecido , temp_conhecida , ’o ’ , color = ’ red ’ ,
markersize =8 , label = ’ Dados Originais ’)
38 plt . plot ( tempo_novo_estimar , temp_estimada_linear , ’ - ’ ,
color = ’ blue ’ , label = ’ Interpola ç ã o Linear ’)
39 plt . plot ( tempo_novo_estimar , temp_estimada_cubica , ’ -- ’ ,
color = ’ green ’ , label = ’ Interp . C ú bica ( ou Linear ) ’)
40 plt . plot ([35 , -5] , [ temp_aos_35s , temp_ao s_menos_5 s ] , ’s ’ ,
color = ’ purple ’ ,
41 markersize =7 , linestyle = ’ none ’ , label = ’ Pontos Extrapolados ’)
42
43 plt . title ( " Interpola ç ã o de Dados de Temperatura " , fontsize =16)
44 plt . xlabel ( " Tempo ( segundos ) " , fontsize =12)
45 plt . ylabel ( " Temperatura ( ° C ) " , fontsize =12)
46 plt . legend () ; plt . grid ( True , linestyle = ’: ’ , alpha =0.7)
47 plt . show ()

Código 21.14: Interpolação linear e cúbica de dados de temperatura

Considerações ao Usar Interpolação


• Qualidade dos Dados: A interpolação depende da qualidade e densidade dos
dados originais.

• Escolha do Método (kind): Linear é um bom ponto de partida. Splines cúbicas


podem ser mais suaves mas exigem mais cuidado.
21.6. Integração Numérica ([Link]): Calculando Áreas e Acumulações 419

• Extrapolação vs. Interpolação: Interpolar é estimar valores dentro do intervalo


conhecido. Extrapolar (estimar fora) é geralmente muito menos confiável. Use
bounds_error=False e fill_value com cautela para extrapolação.

O submódulo [Link] oferece muitas outras funcionalidades, incluindo inter-


polação multivariada. interp1d é a ferramenta fundamental para o caso unidimensional.

21.6 Integração Numérica ([Link]): Calcu-


lando Áreas e Acumulações
Em cálculo, a integral definida de uma função f (x) sobre um intervalo [a, b] (repre-
Rb
sentada como a f (x)dx) nos dá a “área líquida” sob a curva da função entre esses dois
pontos. De forma mais geral, a integração está relacionada à acumulação de quantidades.
Por exemplo, a integral da velocidade em relação ao tempo nos dá o deslocamento; a
integral da potência em relação ao tempo nos dá a energia.
Embora para muitas funções simples possamos encontrar a integral analiticamente
(ou seja, encontrar uma fórmula exata para ela), existem muitas funções para as quais
uma solução analítica é muito complexa ou simplesmente não existe em termos de funções
elementares. Além disso, frequentemente em cenários práticos de engenharia e ciências,
não temos a função f (x) explicitamente, mas sim um conjunto de pontos de dados (xi , yi )
obtidos de medições experimentais ou simulações.
Nesses casos, recorremos à integração numérica, que são métodos para aproximar
o valor da integral definida. O submódulo [Link] da biblioteca SciPy oferece
um conjunto de ferramentas poderosas e robustas para realizar integração numérica.
Para usar as funcionalidades, geralmente importamos as funções específicas que preci-
samos:
1 import numpy as np
2 from scipy . integrate import quad , trapezoid , simpson # Principais
3 import matplotlib . pyplot as plt # Para visualiza ç ã o opcional

Código 21.15: Importações comuns para [Link]

21.6.1 Integrando Funções Definidas Analiticamente com quad()


Quando você tem a função f (x) definida como uma função Python e quer calcular
Rb
sua integral definida a f (x)dx, a principal ferramenta no [Link] é a função
quad() (abreviação de quadratura, um termo para integração numérica).

• Sintaxe: resultado_integral, erro_estimado =


quad(funcao, limite_inferior_a, limite_superior_b, args=())

– funcao: O objeto função Python que define f (x). Deve aceitar um float como
argumento e retornar um float.
– limite_inferior_a, limite_superior_b: Os limites de integração.
– args (opcional): Uma tupla de argumentos extras a serem passados para funcao
(além do x).

• Retorno: A função quad() retorna uma tupla contendo dois valores:


420 Capítulo 21. SciPy para Computação Científica (Introdução)

1. O valor estimado da integral definida.


2. Uma estimativa do erro absoluto no resultado.
A função quad() é muito robusta, baseada em técnicas adaptativas da biblioteca QUAD-
PACK (Fortran), e geralmente fornece resultados precisos.
Exemplo 1: Integrando uma função polinomial simples f (x) = x2 de 0 a 1.
R1 2 h 3 i1 3 3
A integral analítica é 0 x dx = x3 = 13 − 03 = 13 ≈ 0.33333333.
0

1 # ( Supondo imports de np e quad j á feitos )


2 def f u n c a o _ q u a d r a t i c a _ p a r a _ i n t e g r a r ( x ) :
3 return x **2
4
5 limite_a = 0.0
6 limite_b = 1.0
7

8 resultado_integral_q , erro_estimado_q =
quad ( funcao_quadratica_para_integrar , limite_a , limite_b )
9
10 print ( f " --- Integra ç ã o de f ( x ) = x ^2 de { limite_a } a { limite_b }
---" )
11 print ( f " Resultado num é rico ( quad ) : { r e s u l t a d o _ i n t e g r a l _ q :.8 f } " )
12 print ( f " Resultado anal í tico esperado : {1/3:.8 f } " )
13 print ( f " Estimativa do erro absoluto da integra ç ã o :
{ erro_estimado_q :.2 e } " )

Código 21.16: Integrando f (x) = x2 com quad()


Exemplo 2: Integrando uma função mais complexa, como f (x) = e−ax sin(x)
de 0 a π.
Vamos usar um parâmetro adicional a na nossa função.
1 # ( Supondo imports de np , quad e plt j á feitos )
2 def funcao_exp_seno (x , a =1) : # Par â metro ’a ’ adicional
3 return np . exp ( - a * x ) * np . sin ( x )
4
5 lim_inf_exp = 0
6 lim_sup_exp = np . pi
7 param_a = 1 # Valor de ’a ’ para este c á lculo
8
9 resultado_int_exp_sen , err_est_exp_sen = quad ( funcao_exp_seno ,
10 lim_inf_exp , lim_sup_exp ,
11 args =( param_a ,) ) # Passando ’a ’ via tupla args
12
13 print ( f " \n - - - Integra ç ã o de f ( x ) = e ^( - ax ) sin ( x ) com a ={ param_a } ,
de { lim_inf_exp } a { lim_sup_exp :.4 f } ---" )
14 print ( f " Resultado num é rico ( quad ) : { res_int_exp_sen :.8 f } " )
15 print ( f " Estimativa do erro absoluto : { err_est_exp_sen :.2 e } " )
16 # O resultado anal í tico para a =1 é (1 + e ^ - pi ) /2 que é
aproximadamente 0.52160304
17
18 # Opcional : Plot para visualizar a á rea sob a curva
19 x_plot_vals = np . linspace ( lim_inf_exp , lim_sup_exp , 200)
20 y_plot_vals = funcao_exp_seno ( x_plot_vals , a = param_a ) # Usa o mesmo
’a ’
21 plt . figure ( figsize =(8 , 5) )
22 plt . plot ( x_plot_vals , y_plot_vals , label = f ’ $e ^{{{ - param_a } x }}
\ sin ( x ) $ ’ , color = ’ purple ’)
21.6. Integração Numérica ([Link]): Calculando Áreas e Acumulações 421

23 plt . fill_between ( x_plot_vals , y_plot_vals , alpha =0.3 ,


color = ’ orchid ’ ,
24 label = f ’Á rea da Integral $ \\ approx$ { res_int_exp_sen :.4 f } ’)
25 plt . title ( f " Visualiza ç ã o da Integral de $e ^{{{ - param_a } x }} \ sin ( x ) $
de { lim_inf_exp } a $ \ pi$ " )
26 plt . xlabel ( " $x$ " ) ; plt . ylabel ( " $f ( x ) $ " )
27 plt . legend () ; plt . grid ( True ) ; plt . axhline (0 , color = ’ black ’ , lw =0.5)
28 plt . show ()

Código 21.17: Integrando f (x) = e−ax sin(x) com quad() e args

21.6.2 Integrando Dados Amostrados (Discretos)


Frequentemente, não temos a função f (x) explicitamente, mas sim um conjunto de
pontos de dados (xi , yi ). Para calcular a integral, usamos regras de quadratura compostas.

• Regra do Trapézio ([Link](y, x=None, dx=1.0, ...)):


(Anteriormente trapz). Aproxima a área sob a curva entre cada par de pontos (xi , yi )
e (xi+1 , yi+1 ) como a área de um trapézio e soma todas essas áreas.

– y: Array com os valores yi (ordenadas).


– x (opcional): Array com os valores xi (abscissas).
– dx (opcional, float, padrão=1.0): Espaçamento entre os xi se x não for fornecido
e o espaçamento for constante.

• Regra de Simpson
([Link](y, x=None, dx=1.0, ..., even=’avg’)):
(Anteriormente simps). Usa interpolação parabólica sobre conjuntos de três pontos
adjacentes. Geralmente mais precisa para funções suaves.

– Parâmetros similares ao trapezoid.


– Tradicionalmente requer um número ímpar de pontos (par de intervalos). O
parâmetro even define como lidar com um número par de pontos.

Exemplo: Calculando o deslocamento a partir de dados de velocidade


amostrados
Se temos medições de velocidade em diferentes instantes, a integral da velocidade em
relação ao tempo nos dá o deslocamento.
1 # ( Supondo imports de np , trapezoid , simpson , e plt j á feitos )
2 t e m p o _ a m o s t r a s _ s e n s o r = np . array ([0.0 , 0.2 , 0.4 , 0.6 , 0.8 , 1.0 ,
1.2])
3 v e l o c i d a d e _ a m o s t r a s _ s e n s o r = np . array ([0.0 , 5.5 , 10.2 , 13.5 , 15.1 ,
14.0 , 10.5]) # m / s
4
5 print ( " \n - - - Integra ç ã o de Dados Amostrados ( Velocidade x Tempo )
---" )
6 print ( f " Tempos ( s ) : { t e m p o _ a m o s t r a s _ s e n s o r } " )
7 print ( f " Velocidades ( m / s ) : { v e l o c i d a d e _ a m o s t r a s _ s e n s o r } " )
8

9 # Calculando o deslocamento usando a Regra do Trap é zio


10 deslocamento_estimado_trapezio =
trapezoid ( velocidade_amostras_sensor ,
422 Capítulo 21. SciPy para Computação Científica (Introdução)

11 x = t e mp o _ a m o s t r a s _ s e n s o r )
12 print ( f " Deslocamento estimado ( Regra do Trap é zio ) :
{ d e s l o c a m e n t o _ e s t i m a d o _ t r a p e z i o :.3 f } metros " )
13
14 # Calculando o deslocamento usando a Regra de Simpson
15 # Temos 7 pontos , que significam 6 intervalos ( par ) , ideal para
Simpson .
16 d e s l o c a m e n t o _ e s t i m a d o _ s i m p s o n = simpson ( velocidade_amostras_sensor ,
17 x = t e mp o _ a m o s t r a s _ s e n s o r )
18 print ( f " Deslocamento estimado ( Regra de Simpson ) :
{ d e s l o c a m e n t o _ e s t i m a d o _ s i m p s o n :.3 f } metros " )
19

20 # Opcional : Plot para visualizar os dados e a á rea


21 plt . figure ( figsize =(8 , 5) )
22 plt . plot ( tempo_amostras_sensor , velocidade_amostras_sensor , ’o - ’ ,
23 color = ’ teal ’ , label = ’ Velocidade Amostrada ’)
24 plt . fill_between ( tempo_amostras_sensor , velocidade_amostras_sensor ,
alpha =0.2 ,
25 color = ’ turquoise ’ ,
26 label = f ’Á rea ( Desloc .) $ \\ approx$
{ d e s l o c a m e n t o _ e s t i m a d o _ s i m p s o n :.2 f } m ( Simpson ) ’)
27 plt . title ( " Integra ç ã o Num é rica de Dados de Velocidade Amostrados " )
28 plt . xlabel ( " Tempo ( s ) " ) ; plt . ylabel ( " Velocidade ( m / s ) " )
29 plt . legend () ; plt . grid ( True )
30 plt . show ()

Código 21.18: Integrando dados de velocidade com trapezoid e simpson

21.6.3 Outras Funções de Integração (Breve Menção)


O submódulo [Link] é bastante extenso e inclui muitas outras funcionali-
dades, como:
• Solucionadores para equações diferenciais ordinárias (ODEs):
– solve_ivp: Para resolver problemas de valor inicial (IVP).
– solve_bvp: Para resolver problemas de valor de contorno (BVP).
• Funções para integrais duplas (dblquad), triplas (tplquad) e N-dimensionais (nquad).
Esses são tópicos mais avançados, mas é bom saber que o SciPy oferece ferramentas
robustas para eles caso suas necessidades em engenharia ou ciências os exijam.
O submódulo [Link] fornece, portanto, ferramentas essenciais e robustas
para realizar integração numérica, seja a partir de uma função definida analiticamente (com
quad) ou a partir de um conjunto de dados amostrados discretamente (com trapezoid
e simpson). Essas técnicas são amplamente aplicadas em simulações, análises de sinais,
cálculo de propriedades físicas a partir de dados experimentais e muitas outras áreas da
engenharia e das ciências.

21.7 Processamento de Sinais ([Link]) (Breve


Menção)
O processamento de sinais é uma área fundamental em diversas disciplinas da
engenharia (como elétrica, eletrônica, mecânica, de controle, biomédica e de telecomu-
21.7. Processamento de Sinais ([Link]) (Breve Menção) 423

nicações) e em muitas ciências aplicadas (como física, geofísica, acústica). Ele se ocupa
da análise, modificação, interpretação e síntese de sinais, que são funções que carregam
informação sobre o comportamento ou atributos de algum fenômeno. Esses sinais podem
ser de natureza variada, como sinais de áudio, sinais de rádio, imagens (que podem ser
tratadas como sinais bidimensionais), leituras de sensores ao longo do tempo, dados de
eletrocardiogramas (ECG), entre muitos outros.
A biblioteca SciPy possui um submódulo muito poderoso e extenso dedicado a essas
tarefas: o [Link]. Este módulo oferece uma vasta gama de ferramentas para
realizar operações comuns e avançadas de processamento de sinais.

Principais Funcionalidades do [Link] (Visão Geral)


Devido à amplitude do campo de processamento de sinais, não poderemos nos apro-
fundar nos detalhes de cada funcionalidade neste capítulo introdutório. No entanto, é
importante que você saiba da existência dessas ferramentas e do tipo de problema que
elas podem ajudar a resolver:

• Filtragem de Sinais: Esta é, talvez, uma das aplicações mais comuns. A filtragem
envolve a remoção de componentes indesejados de um sinal (como ruído), a extração
de componentes de frequência específicos, ou a suavização dos dados.

– [Link] oferece funções para projetar e aplicar diversos tipos de filtros


digitais, como filtros FIR (Finite Impulse Response) e IIR (Infinite Impulse
Response).
– Funções como lfilter() (para aplicação de filtros IIR ou FIR), filtfilt()
(para filtragem de fase zero, que evita atrasos no sinal filtrado), medfilt()
(filtro de mediana, bom para remover ruído impulsivo) e wiener() (filtro de
Wiener para redução de ruído) são exemplos.
– Funções para projetar filtros incluem butter() (Butterworth), cheby1() e
cheby2() (Chebyshev), ellip() (elíptico), firwin() (para filtros FIR baseados
em janelamento).

• Análise Espectral (Análise de Fourier): Permite analisar o conteúdo de frequên-


cia de um sinal, ou seja, decompor um sinal do domínio do tempo para o domínio
da frequência. Isso revela quais frequências estão presentes no sinal e com qual
intensidade.

– A Transformada Rápida de Fourier (FFT) é o algoritmo fundamental para isso.


O SciPy oferece funcionalidades de FFT através do submódulo [Link] (mais
novo e recomendado) ou do mais antigo [Link]. O NumPy ([Link])
também possui um módulo FFT, mas o da SciPy é geralmente mais completo.
– Funções incluem fft() (FFT), ifft() (FFT Inversa), fftfreq() (para obter
as frequências correspondentes), rfft() (FFT para sinais reais).
– Funções para estimar a densidade espectral de potência, como periodogram()
e welch(), também são muito úteis.

• Convolução e Correlação: São operações matemáticas fundamentais no processa-


mento de sinais, usadas em filtragem, reconhecimento de padrões, análise de sistemas,
etc.
424 Capítulo 21. SciPy para Computação Científica (Introdução)

– [Link]() e [Link]() (e suas versões 2D).

• Janelamento (Windowing): Envolve a aplicação de funções de janela (como


Hanning, Hamming, Blackman, Bartlett, etc.) a um segmento de um sinal. Isso
é frequentemente usado antes de realizar uma análise de Fourier para reduzir o
fenômeno de “vazamento espectral” (spectral leakage).

– O módulo [Link] (ou diretamente


[Link].get_window()) fornece acesso a diversas funções de janela.

• Outras Funcionalidades Relevantes:

– Análise de sistemas lineares invariantes no tempo (LTI): representação de


sistemas, simulação de resposta (ex: lsim(), step(), impulse()).
– Geração de formas de onda periódicas (ex: square(), sawtooth(),
gausspulse()).
– Detecção de picos em sinais (find_peaks()).
– Reamostragem de sinais (resample()).

Importando e Usando o Módulo


Para utilizar essas funcionalidades, você geralmente importaria o submódulo signal:
1 from scipy import signal
2 # ou
3 import scipy . signal as sig
4 # ( usando ’ sig ’ como um alias comum )

Código 21.19: Importando [Link]


Dado o escopo introdutório deste capítulo e a complexidade inerente ao processamento
de sinais (que muitas vezes requer um entendimento mais profundo da teoria subjacente
e a preparação de dados de sinal específicos), não apresentaremos exemplos de código
complexos aqui. O objetivo desta breve menção é que você, como estudante de engenharia
ou ciências, saiba que o [Link] é uma ferramenta extremamente poderosa e bem
estabelecida no Python para quando você precisar realizar essas tarefas.
Se o processamento de sinais for uma área de seu interesse ou necessidade, a docu-
mentação oficial do [Link] é o recurso definitivo para explorar a vasta gama de
funções disponíveis e aprender como aplicá-las corretamente aos seus problemas.
O submódulo [Link] é um testemunho da profundidade da SciPy como uma
biblioteca para computação científica, oferecendo ferramentas especializadas que são
essenciais para muitas aplicações práticas em engenharia.

21.8 Estatísticas com [Link] (Complementando


NumPy)
Embora o NumPy (Capítulo 18) já nos forneça um bom conjunto de funções estatísticas
básicas como média ([Link]()), mediana ([Link]()), desvio padrão ([Link]()),
mínimo ([Link]()) e máximo ([Link]()), o submódulo [Link] da biblioteca SciPy
expande enormemente essas capacidades. Ele oferece um arsenal muito mais rico e
21.8. Estatísticas com [Link] (Complementando NumPy) 425

detalhado de ferramentas para análise estatística, sendo um recurso indispensável para


quem precisa ir além das estatísticas descritivas mais simples.

Visão Geral do [Link]


O [Link] é um submódulo extenso que contém:

• Uma vasta coleção de distribuições de probabilidade contínuas e discretas


(Normal, Binomial, Poisson, Exponencial, t de Student, Qui-quadrado, Gama, Beta,
e muitas outras).

• Funções para calcular propriedades dessas distribuições, como a Função Densidade de


Probabilidade (PDF), Função Massa de Probabilidade (PMF), Função de Distribuição
Cumulativa (CDF), Função Ponto Percentual (PPF, a inversa da CDF), momentos
estatísticos (média, variância, assimetria, curtose).

• Métodos para gerar amostras aleatórias (variáveis aleatórias) dessas distribuições.

• Uma grande variedade de testes estatísticos (testes de hipóteses).

• Funções para estatísticas descritivas mais detalhadas.

A convenção comum para importação é:


1 from scipy import stats
2 # Ou , para um alias mais curto , embora ’ stats ’ j á seja bem conciso :
3 # import scipy . stats as st

Código 21.20: Importando [Link]

21.8.1 Trabalhando com Distribuições de Probabilidade


Uma das grandes forças do [Link] é a forma como ele permite trabalhar com
distribuições de probabilidade. Para cada distribuição, o SciPy geralmente fornece um
objeto que representa essa distribuição e que possui vários métodos úteis.
Distribuição Normal ([Link]) como Exemplo Principal:
A distribuição Normal (ou Gaussiana) é uma das mais importantes em estatística. Em
[Link], podemos criar um objeto que representa uma distribuição normal específica
usando: [Link](loc=media, scale=desvio_padrao)

• loc: Especifica a média (µ) da distribuição.

• scale: Especifica o desvio padrão (σ) da distribuição.

• Se loc=0 e scale=1, temos a distribuição Normal Padrão.

Uma vez que temos um objeto de distribuição, podemos usar seus métodos:

• .rvs(size=N, random_state=None): Gera N variáveis aleatórias (Random Variates)


dessa distribuição. random_state pode ser usado para reprodutibilidade.

• .pdf(x): Calcula a Função Densidade de Probabilidade (PDF) no ponto (ou pontos)


x. Para distribuições contínuas.
426 Capítulo 21. SciPy para Computação Científica (Introdução)

• .pmf(k): Calcula a Função Massa de Probabilidade (PMF) no ponto (ou pontos) k.


Para distribuições discretas (ex: [Link](k, n, p)).

• .cdf(x): Calcula a Função de Distribuição Cumulativa (CDF) no ponto (ou pontos)


x, ou seja, P (X ≤ x).

• .ppf(q): Função Ponto Percentual (Percent Point Function), que é a inversa da CDF.
Dado uma probabilidade q (entre 0 e 1), encontra o valor x tal que P (X ≤ x) = q.
É usada para encontrar quantis.

• .mean(), .median(), .var(), .std(): Retornam os momentos teóricos (média,


mediana, variância, desvio padrão) da distribuição.

• .interval(alpha, loc=0, scale=1): Calcula o intervalo de confiança (endpoint)


com nível de confiança alpha (ex: 0.95 para 95%).

• .fit(dados): Tenta estimar os parâmetros da distribuição (ex: média e desvio


padrão para a normal) a partir de um conjunto de dados observados.

1 import numpy as np
2 from scipy import stats
3 import matplotlib . pyplot as plt # Para plotagem opcional
4
5 # Criando um objeto para a distribui ç ã o normal com m é dia 70 e
desvio padr ã o 10
6 media_pop = 70
7 dp_pop = 10
8 d i st _no r ma l _ ex e mp l o = stats . norm ( loc = media_pop , scale = dp_pop )
9
10 # Gerando 1000 amostras aleat ó rias desta distribui ç ã o
11 am os tra s_ da _no rm al = d is t _n o r ma l _e x e mp l o . rvs ( size =1000 ,
random_state =42)
12
13 # Calculando a PDF em alguns pontos
14 x_pdf_teste = [60 , 70 , 80]
15 pdf_nos_pontos = d i st _ no r ma l _ ex e mp l o . pdf ( x_pdf_teste )
16 print ( f " --- Trabalhando com stats . norm ( M é dia ={ media_pop } ,
DP ={ dp_pop }) ---" )
17 for x_val , pdf_val in zip ( x_pdf_teste , pdf_nos_pontos ) :
18 print ( f " PDF em x ={ x_val }: { pdf_val :.4 f } " )
19

20 # Calculando a CDF em x =65 ( P ( X <= 65) )


21 cdf_em_65 = d i st _ n or m al _ e xe m pl o . cdf (65)
22 print ( f " CDF em x =65 ( P (X <=65) ) : { cdf_em_65 :.4 f } " )
23
24 # Calculando o valor x para o qual 97.5% dos dados est ã o abaixo
( percentil 97.5)
25 percentil_97_5 = d i st _ no r ma l _ ex e mp l o . ppf (0.975)
26 print ( f " Percentil 97.5 ( valor de x tal que CDF ( x ) =0.975) :
{ percentil_97_5 :.4 f } " ) # Aprox . 89.6
27
28 # Ajustando uma distribui ç ã o normal a um conjunto de dados de
exemplo
29 d ad o s_r e ai s _ ex e mp l o = np . array ([155 , 160 , 162 , 165 , 168 , 170 , 171 ,
172 , 175 , 180 , 185 , 190])
30 media_estimada , dp_estimado = stats . norm . fit ( da d o s_ r ea i s _e x em p lo )
21.8. Estatísticas com [Link] (Complementando NumPy) 427

31 print ( f " \ nPar â metros ajustados para ’ d a do s _r e ai s _ ex e mp l o ’ ( fit ) : " )


32 print ( f " M é dia estimada : { media_estimada :.2 f } " )
33 print ( f " Desvio Padr ã o estimado : { dp_estimado :.2 f } " )
34
35 # Opcional : Visualizando a PDF e a CDF da distribui ç ã o criada
36 # x_teorico_plot = np . linspace ( media_pop - 4* dp_pop , media_pop +
4* dp_pop , 200)
37 # pdf_teorica_plot = d is t _n o rm a l _e x em p l o . pdf ( x_teorico_plot )
38 # cdf_teorica_plot = d is t _n o rm a l _e x em p l o . cdf ( x_teorico_plot )
39
40 # plt . figure ( figsize =(14 , 5.5) )
41 # plt . subplot (1 , 2 , 1)
42 # plt . plot ( x_teorico_plot , pdf_teorica_plot , ’b - ’ , lw =2 , label = ’ PDF
Te ó rica N (70 ,100) ’)
43 # plt . hist ( amostras_da_normal , bins =30 , density = True , alpha =0.6 ,
label = ’ Hist . Amostras ’)
44 # plt . title (" PDF Te ó rica e Histograma de Amostras ")
45 # plt . xlabel (" Valores ") ; plt . ylabel (" Densidade ") ; plt . legend () ;
plt . grid ( True )
46
47 # plt . subplot (1 , 2 , 2)
48 # plt . plot ( x_teorico_plot , cdf_teorica_plot , ’r - ’ , lw =2 , label = ’ CDF
Te ó rica N (70 ,100) ’)
49 # plt . title (" Fun ç ã o de Distribui ç ã o Cumulativa ( CDF ) ")
50 # plt . xlabel (" Valores ") ; plt . ylabel (" Probabilidade Cumulativa
P (X <= x ) ") ; plt . legend () ; plt . grid ( True )
51 # fig_titulo = f " Distribui ç ã o Normal ( $ \\ mu ={ media_pop } ,
\\ sigma ={ dp_pop } $ ) "
52 # plt . suptitle ( fig_titulo , fontsize =16 , y =1.0) # y para ajuste
vertical
53 # plt . tight_layout ( rect =[0 ,0 ,1 ,0.93]) # Ajusta para o suptitle
54 # plt . show ()

Código 21.21: Trabalhando com a Distribuição Normal em [Link]

Outras distribuições contínuas importantes incluem stats.t (t de Student), stats.chi2


(Qui-quadrado), stats.f (F de Snedecor), [Link] (Exponencial), [Link]
(Uniforme), [Link], [Link], etc. Para distribuições discretas, temos [Link]
(Binomial), [Link] (Poisson), [Link] (Geométrica), [Link] (Hi-
pergeométrica), entre outras, que usam .pmf() em vez de .pdf().

21.8.2 Testes de Hipóteses Estatísticas (Introdução Muito Básica)


Um teste de hipótese é um procedimento estatístico para tomar uma decisão sobre
uma afirmação (a hipótese nula, H0 ) a respeito de uma população, com base em dados
de uma amostra. A decisão é geralmente entre não rejeitar H0 ou rejeitá-la em favor de
uma hipótese alternativa (H1 ).
Um conceito central é o p-valor (valor-p):

• É a probabilidade de observar os dados da amostra (ou dados ainda mais extremos)


se a hipótese nula (H0 ) for verdadeira.

• Se o p-valor é pequeno (tipicamente menor que um nível de significância α predefinido,


como 0.05), isso sugere que os dados observados são improváveis sob H0 . Nesse caso,
rejeitamos H0 em favor de H1 .
428 Capítulo 21. SciPy para Computação Científica (Introdução)

• Se o p-valor é grande (maior ou igual a α), não temos evidência suficiente para
rejeitar H0 .

O [Link] implementa uma grande variedade de testes. Vamos ver um exemplo


muito comum:
Teste t para uma Amostra (stats.ttest_1samp(amostra, media_pop_hipotetica)):
Testa se a média de uma única amostra de dados é significativamente diferente de uma
média populacional hipotética conhecida ou de referência.
1 # ( Supondo imports de np e stats j á feitos )
2 # Exemplo : Medi ç õ es do tempo de rea ç ã o de 20 participantes a um
est í mulo .
3 # Queremos testar se a m é dia do tempo de rea ç ã o é diferente de
250 ms .
4 np . random . seed (123)
5 t e mp os_ d e_ r e ac a o_ m s = stats . norm . rvs ( loc =265 , scale =20 , size =20 ,
random_state = np . random . default_rng ( seed =123) )
6 # Gerando dados com m é dia um pouco acima de 250 ms
7
8 m e d i a _ h i p o t e t i c a _ r e a c a o = 250.0 # ms
9
10 print ( f " \n - - - Teste t para uma Amostra ( Tempos de Rea ç ã o ) ---" )
11 print ( f " M é dia amostral dos tempos de rea ç ã o :
{ np . mean ( t em p os _ d e_ r ea c ao _ m s ) :.2 f } ms " )
12
13 # Hip ó tese Nula ( H0 ) : A m é dia da popula ç ã o da qual a amostra foi
extra í da é 250 ms .
14 # Hip ó tese Alternativa ( H1 ) : A m é dia da popula ç ã o é diferente de
250 ms .
15 estatistica_t_calculada , p_ valor_cal culado = stats . ttest_1samp (
16 a = tempos_de_reacao_ms ,
17 popmean = m e d i a _ h i p o t e t i c a _ r e a c a o
18 )
19

20 print ( f " Estat í stica t calculada : { e s t a t i s t i c a _ t _ c a l c u l a d a :.3 f } " )


21 print ( f "P - valor : { p_valor_ calculad o :.4 f } " )
22
23 n i v e l _ d e _ s i g n i f i c a n c i a _ a l p h a = 0.05
24 if p_v alor_cal culado < n i v e l _ d e _ s i g n i f i c a n c i a _ a l p h a :
25 print ( f " Com p - valor = { p_valor_ calculad o :.4 f } <
{ n i v e l _ d e _ s i g n i f i c a n c i a _ a l p h a } , rejeitamos H0 . " )
26 print ( f " Conclus ã o : H á evid ê ncia estat í stica de que a m é dia do
tempo de rea ç ã o é diferente de { m e d i a _ h i p o t e t i c a _ r e a c a o } ms . " )
27 else :
28 print ( f " Com p - valor = { p_valor_ calculad o :.4 f } >=
{ n i v e l _ d e _ s i g n i f i c a n c i a _ a l p h a } , n ã o rejeitamos H0 . " )
29 print ( f " Conclus ã o : N ã o h á evid ê ncia suficiente para dizer que a
m é dia do tempo de rea ç ã o é diferente de
{ m e d i a _ h i p o t e t i c a _ r e a c a o } ms . " )

Código 21.22: Exemplo de Teste t para uma amostra com [Link]

Outro teste t comum é o stats.ttest_ind(amostra1, amostra2) para comparar as mé-


dias de duas amostras independentes. O [Link] também inclui testes de normalidade
(ex: [Link](), [Link]()), testes Qui-quadrado (stats.chi2_contingency()),
Análise de Variância (ANOVA, ex: stats.f_oneway()), entre muitos outros. A interpre-
tação correta desses testes requer um bom entendimento de estatística.
21.8. Estatísticas com [Link] (Complementando NumPy) 429

21.8.3 Estatísticas Descritivas Adicionais


O [Link] também oferece funções para estatísticas descritivas mais detalhadas:
• [Link](array, axis=0, nan_policy=’propagate’, ...): Retorna um
resumo estatístico completo, incluindo número de observações, mínimo/máximo,
média, variância, assimetria (skewness) e curtose (kurtosis).

• [Link](array, ...): Calcula a assimetria da distribuição (mede a falta de


simetria).

• [Link](array, fisher=True, ...): Calcula a curtose (mede o quão


“pesadas” são as caudas da distribuição em relação a uma normal). Com fisher=True
(padrão), a curtose da normal é 0.

1 # Usando ’ a mos tr as _da _n or mal ’ do exemplo anterior (1000 amostras de


N (70 ,10) )
2 # d is t _n o r ma l _e x e mp l o = stats . norm ( loc =70 , scale =10)
3 # amost ra s_ da_ no rm al = di s t _n o rm a l _e x em p lo . rvs ( size =1000 ,
random_state =42)
4
5 print ( " \n - - - Estat í sticas Descritivas Detalhadas com
scipy . stats . describe ---" )
6 # Gerando novamente para este bloco de c ó digo , para garantir que
existe
7 np . random . seed (42) # Para repr odutibili dade
8 a m o s t r a s _ p a r a _ d e s c r i b e = stats . norm . rvs ( loc =70 , scale =10 , size =1000)
9

10 d esc_das_ amostras = stats . describe ( a m o s t r a s _ p a r a _ d e s c r i b e )


11
12 print ( f " N ú mero de observa ç õ es ( nobs ) : { de sc_das_a mostras . nobs } " )
13 print ( f " Valores M í nimo e M á ximo ( minmax ) :
{ desc_da s_amostra s . minmax } " )
14 print ( f " M é dia ( mean ) : { des c_das_am ostras . mean :.3 f } " )
15 print ( f " Vari â ncia ( variance ) : { desc_d as_amost ras . variance :.3 f } " )
16 print ( f " Assimetria ( skewness ) : { desc_d as_amost ras . skewness :.4 f } " )
17 # Para uma distribui ç ã o normal , esperamos assimetria pr ó xima de 0.
18 print ( f " Curtose ( kurtosis , Fisher ) :
{ desc_da s_amostra s . kurtosis :.4 f } " )
19 # Para uma distribui ç ã o normal , esperamos curtose ( de Fisher )
pr ó xima de 0.

Código 21.23: Usando [Link]() para estatísticas detalhadas


O submódulo [Link] é uma ferramenta incrivelmente rica e essencial para quem
precisa realizar análises estatísticas mais aprofundadas em Python, indo muito além das
funcionalidades básicas oferecidas pelo NumPy. Ele fornece a base para trabalhar com
uma vasta gama de distribuições de probabilidade, realizar testes de hipóteses e obter
estatísticas descritivas detalhadas.

Concluindo o Capítulo sobre SciPy


Neste capítulo, tivemos uma introdução à vasta e poderosa biblioteca SciPy, um pilar
fundamental no ecossistema de computação científica do Python. Vimos que a SciPy é
construída sobre a base sólida dos arrays NumPy, estendendo suas capacidades com uma
430 Capítulo 21. SciPy para Computação Científica (Introdução)

rica coleção de algoritmos e ferramentas de nível mais alto, organizados em submódulos


especializados.
Exploramos brevemente alguns desses submódulos, incluindo:

• [Link]: Para funções matemáticas especiais que vão além do básico.

• [Link]: Oferecendo funcionalidades de álgebra linear mais avançadas e ro-


bustas, complementando o [Link], com decomposições de matrizes e funções
de matrizes.

• [Link]: Fornecendo algoritmos para otimização (encontrar mínimos ou


máximos de funções) e para busca de raízes de equações.

• [Link]: Com ferramentas para interpolação, permitindo estimar valores


entre pontos de dados conhecidos.

• [Link]: Oferecendo métodos para realizar integração numérica, tanto de


funções definidas analiticamente quanto de dados amostrados.

• E mencionamos a existência de outros submódulos importantes como [Link]


(para processamento de sinais) e [Link] (para estatísticas avançadas e distri-
buições de probabilidade).

É crucial lembrar que este capítulo serviu apenas como uma introdução superficial à
SciPy. Cada um de seus submódulos é um universo em si, com uma profundidade que
pode preencher livros inteiros. O objetivo aqui foi apresentar a você a existência dessas
ferramentas e dar uma ideia do tipo de problemas que elas podem ajudar a resolver,
incentivando a exploração da documentação oficial da SciPy para necessidades mais
específicas.
Com a conclusão deste capítulo sobre a SciPy, finalizamos também a Parte VI:
Bibliotecas Essenciais para Engenharia (ou Aplicações Práticas). Ao longo desta
parte, cobrimos:

• NumPy (Capítulo 18): A base para toda a computação numérica em Python,


com seus eficientes arrays N-dimensionais (ndarray), operações vetorizadas e funcio-
nalidades de álgebra linear.

• Matplotlib e Seaborn (Capítulo 19): Nossas principais ferramentas para visua-


lização de dados, permitindo a criação desde gráficos simples e customizáveis com
Matplotlib até visualizações estatísticas sofisticadas e atraentes com Seaborn.

• Pandas (Capítulo 20): A biblioteca essencial para manipulação e análise de dados


tabulares, com suas poderosas estruturas Series e DataFrame, facilitando tarefas
de limpeza, transformação, agrupamento e leitura/escrita de dados.

• SciPy (Capítulo 21): A caixa de ferramentas científicas que complementa o NumPy


com algoritmos de alto nível para otimização, integração, interpolação, álgebra linear
avançada, estatística e muito mais.

Este conjunto de bibliotecas (NumPy, Pandas, Matplotlib/Seaborn, SciPy) forma o núcleo


do que é frequentemente chamado de “Pilha Científica do Python” (Python Scientific
Stack ). Dominá-las, mesmo que em um nível introdutório como o que buscamos neste
21.8. Estatísticas com [Link] (Complementando NumPy) 431

livro, abre um leque imenso de possibilidades para resolver problemas complexos em


engenharia, ciência de dados, finanças, pesquisa e muitas outras áreas. Elas permitem um
fluxo de trabalho completo, desde a aquisição e limpeza de dados, passando pela análise e
computação numérica, até a visualização e comunicação dos resultados.
Com o conhecimento adquirido até aqui, você não apenas entende os fundamentos da
linguagem Python, suas estruturas de dados, o paradigma orientado a objetos e como
modularizar seu código, mas também tem uma introdução sólida às principais ferramentas
que transformam o Python em uma potência para aplicações técnicas e científicas.
Parte VII

Exercícios e Projetos

432
Exercícios e Projetos Sugeridos

Esta seção contém uma coleção de exercícios e sugestões de projetos, organizada por
capítulo, para ajudar a consolidar o aprendizado e a praticar os conceitos apresentados
ao longo do livro “Python Descomplicado: Seu Guia Prático para Começar a Programar”.
Lembre-se: a programação é uma habilidade que se desenvolve com a prática constante!

Parte I: Fundamentos Essenciais do Python


Capítulo 1: Introdução ao Python e à Programação
Os exercícios e o projeto para este capítulo introdutório visam garantir que seu ambiente
de desenvolvimento esteja funcionando, que você tenha seu primeiro contato com a escrita
e execução de código Python, e que comece a refletir sobre a linguagem e seus usos.

Exercícios
1. E1.1: Reflexão sobre Python (Conceitual)
Após ler a Seção 1.1 (“O que é Python? Uma Visão Geral”) e a Seção 1.2 (“Por
que Python para Resolver Problemas?”), liste três características do Python que
você considera mais atraentes ou úteis para a sua área de estudo ou interesse em
engenharia. Justifique brevemente sua escolha para cada uma.

2. E1.2: Verificando sua Instalação (Prático)

• Abra o terminal (no Linux ou macOS) ou o Prompt de Comando/PowerShell


(no Windows).
• Digite o comando python –version (ou python3 –version, dependendo da
sua instalação) e pressione Enter. Anote a versão do Python que aparece.
• Em seguida, digite pip –version (ou pip3 –version) e pressione Enter. Anote
se o pip está instalado e qual a sua versão.
• Se você encontrou algum problema ou os comandos não foram reconhecidos,
revise as instruções de instalação na Seção 1.3 ou procure por guias de instalação
específicos para o seu sistema operacional na internet. O sucesso nesta etapa é
crucial para os próximos capítulos!

3. E1.3: Seu Primeiro Programa Modificado (Prático)

• Usando um editor de texto simples (como Bloco de Notas, TextEdit, Gedit) ou


seu IDE escolhido (como VS Code, Thonny, PyCharm), recrie o programa “Olá,
Mundo!” da Seção 1.4. O código é:

433
434

1 print ( " Ol á , Mundo ! " )

Código 21.24: Olá, Mundo!

• Salve este arquivo como [Link] em uma pasta que você criou para os exercícios
do livro.
• Abra um terminal ou prompt de comando, navegue até a pasta onde você salvou
o arquivo e execute-o com o comando: python [Link] (ou python3 [Link]).
• Agora, modifique o programa para que ele imprima uma saudação personalizada,
incluindo seu nome. Por exemplo:
1 print ( " Ol á , Alan Afif Helal ! Bem - vindo ao Python ! " )

Código 21.25: Saudação Personalizada

• Adicione mais uma linha de código que imprima o nome da sua cidade ou da
sua universidade. Salve e execute novamente para ver o resultado.
4. E1.4: Experimentando com a Função print() (Prático)
• Crie um novo script Python chamado meus_dados.py.
• Dentro deste script, use a função print() para exibir no console as seguintes
informações, cada uma em uma nova linha:
(a) Seu nome completo.
(b) Sua idade (como um número, não precisa estar entre aspas).
(c) O resultado de um cálculo matemático simples, por exemplo, 25 × 4 + 10.
Deixe o Python calcular o resultado. Exemplo de como fazer: print("25
* 4 + 10 =", 25 * 4 + 10).
(d) Uma frase que use aspas simples dentro dela (ex: print(’Ele disse:
"Python é muito legal!"’)).
(e) Uma frase que use aspas duplas dentro dela (ex: print("O livro chama-se
’A Arte de Programar’.")).
• Salve e execute o script meus_dados.py.
5. E1.5: Algoritmo do Cotidiano (Conceitual)
Conforme discutido na Seção 1.5 sobre conceitos básicos de algoritmos, pense em
uma tarefa comum do seu dia a dia que envolva uma sequência de passos. Exemplos:
fazer um café coado, trocar um pneu furado, preparar-se para ir à universidade pela
manhã.
• Escolha uma dessas tarefas.
• Escreva um algoritmo em linguagem natural (português), com passos numerados
e claros, que descreva a sequência de ações para realizar essa tarefa do início ao
fim. Tente ser preciso e não omitir passos importantes.
6. E1.6: Python na Engenharia (Pesquisa)
• Faça uma breve pesquisa online (usando seu buscador preferido) sobre aplicações
da linguagem Python na sua área específica de engenharia (ou em uma área de
engenharia que lhe interesse).
435

• Liste pelo menos duas aplicações ou tipos de problemas específicos onde Python
é comumente utilizado nesse campo.
• Para cada aplicação listada, escreva uma ou duas frases descrevendo brevemente
como o Python auxilia na solução desses problemas ou na realização dessas
tarefas.

Projeto do Capítulo 1: Configurando seu Ambiente e Explorando


Título do Projeto: Meu Ambiente Python e Primeiras Explorações.
Objetivos:

1. Garantir que você tenha um ambiente de desenvolvimento Python funcional e


organizado no seu computador.

2. Familiarizar-se com a criação, salvamento e execução de scripts Python básicos.

3. Explorar diferentes formas de interagir com o Python (scripts vs. interpretador


interativo).

4. Começar a pensar em Python como uma ferramenta para expressar instruções e


resolver problemas simples.

Tarefas:

1. Revisão da Instalação e IDE (se necessário): Se você ainda não tem o Python
instalado ou não escolheu um IDE (Ambiente de Desenvolvimento Integrado), volte
à Seção 1.3 e siga as instruções. Recomendamos VS Code com a extensão Python
ou o Thonny para iniciantes. Se optar por um IDE online (como Google Colab ou
Replit), crie sua conta e familiarize-se com a interface para criar e executar arquivos
Python.

2. Organização de Pastas do Projeto:

• Crie uma pasta principal no seu computador para guardar todos os exercícios e
projetos deste livro. Um bom nome seria python_descomplicado_livro.
• Dentro desta pasta principal, crie uma subpasta chamada capitulo_01.
• Todos os scripts Python que você criar para os exercícios e para este projeto do
Capítulo 1 devem ser salvos dentro da pasta capitulo_01.

3. Script de Apresentação Personalizado (apresentacao_pessoal.py):

• Dentro da pasta capitulo_01, crie um novo arquivo Python e salve-o com o


nome apresentacao_pessoal.py.
• Neste script, escreva código Python que faça o seguinte:
– Imprima seu nome completo no console.
– Imprima o nome do seu curso ou sua principal área de interesse em enge-
nharia.
– Imprima uma frase sobre o que você mais espera aprender ou realizar com
a linguagem Python ao longo deste livro.
436

– Adicione comentários no código explicando o que cada linha (ou grupo de


linhas) faz.
• Salve o arquivo e execute-o a partir do terminal (navegando até a pasta
capitulo_01 e usando python apresentacao_pessoal.py) ou diretamente
do seu IDE. Verifique se a saída no console está correta.

4. Script de Cálculos Simples (minha_calculadora.py):

• Ainda na pasta capitulo_01, crie um novo arquivo Python chamado


minha_calculadora.py.
• Neste script, use a função print() para exibir o resultado de pelo menos três
operações aritméticas diferentes que você ache interessantes. Por exemplo:
– A soma de dois números grandes.
– A sua idade em dias (aproximadamente: sua idade em anos * 365).
– Uma divisão que resulte em um número decimal.
– Uma potência (ex: 210 ).
• Para cada print(), inclua uma descrição do cálculo, como no exemplo:
print("Minha idade em dias é aproximadamente:", 25 * 365).
• Adicione comentários explicando cada cálculo. Salve e execute o script.

5. Exploração do Interpretador Interativo Python:

• Abra o terminal ou prompt de comando do seu sistema.


• Inicie o interpretador interativo Python digitando python (ou python3) e
pressionando Enter. Você deverá ver o prompt do Pytho.
• Experimente digitar as seguintes ações diretamente no interpretador, uma por
vez, pressionando Enter após cada uma:
– 100 + 250
– 50 - 15.5
– 7 * 8
– 100 / 3
– 100 // 3 (lembre-se da divisão inteira!)
– 2 ** 6
– print("Python no modo interativo é divertido!")
– Atribua um valor a uma variável, ex: meu_nome = "Seu Nome Aqui"
– Imprima o valor da variável: print(meu_nome) ou apenas meu_nome (no
modo interativo, apenas digitar o nome da variável geralmente imprime
seu valor).
• Para sair do interpretador interativo, digite exit() ou quit() e pressione
Enter, ou use a combinação de teclas Ctrl+Z seguido de Enter (no Windows)
ou Ctrl+D (no Linux/macOS).
• Reflexão (Anote em um arquivo de texto ou como comentário): Qual
foi a principal diferença que você percebeu entre escrever e executar um script
.py completo versus digitar comandos diretamente no interpretador interativo?
Para que tipo de tarefa você acha que o modo interativo seria mais útil?
437

Entrega (Sugestão, se fosse parte de um curso):


• Os arquivos apresentacao_pessoal.py e minha_calculadora.py.

• Um arquivo de texto simples ou um print da tela mostrando a verificação de versão


do Python e do pip (do Exercício E1.2).

• O texto do seu algoritmo do cotidiano (do Exercício E1.5).

• Os resultados da sua pesquisa sobre Python na Engenharia (do Exercício E1.6).

• Sua breve reflexão sobre o interpretador interativo (da Tarefa 5 do Projeto).


Desafio Extra (Opcional):
• Pesquise sobre o “Zen de Python” (mencionado na Seção 1.1). Para visualizá-lo, abra
o interpretador Python e digite import this. Leia os 19 aforismos ou princípios.

• Escolha um ou dois desses princípios que você achou mais interessantes, surpreenden-
tes ou que parecem particularmente relevantes para a escrita de bom código. Escreva
uma ou duas frases explicando por quê.

Capítulo 2: Sintaxe Básica e Tipos de Dados (Exercícios e Projeto)


Os exercícios e o projeto para este capítulo têm como objetivo solidificar seu entendi-
mento sobre como nomear variáveis, os diferentes tipos de dados primitivos do Python,
como interagir com o usuário para entrada de dados, como formatar a saída, a importância
dos comentários e as operações básicas de manipulação de strings.

Exercícios
1. E2.1: Identificadores Válidos e Inválidos (Conceitual)
Analise a lista de nomes abaixo. Para cada nome, indique se ele seria um identificador
(nome de variável) válido ou inválido em Python. Para os inválidos, explique
brevemente o porquê.

• minha_idade
• 2vezes
• _saldo_conta
• NomeAluno
• altura-media
• max_limite
• False
• print_valor
• nome da rua

Consulte a Seção 2.1 (“Variáveis, Nomes e Atribuição”) e a Seção 2.2 (“Palavras


Reservadas”) se necessário.

2. E2.2: Tipos de Dados e a Função type() (Prático)


438

• Crie um novo script Python.


• Nele, defina quatro variáveis diferentes, atribuindo a cada uma delas:
[label=)]
(a) Um número inteiro de sua escolha.
(b) Um número de ponto flutuante (decimal) de sua escolha.
(c) Uma string de texto contendo uma frase curta.
(d) Um valor booleano (True ou False).
• Para cada uma das quatro variáveis, use a função print() em conjunto com a
função type() para exibir o valor da variável e o seu tipo. A saída para cada
variável deve ser algo como: Valor: [valor da variável], Tipo: <class
’[tipo]’>
• Exemplo de uma linha de saída: Valor: 42, Tipo: <class ’int’>
3. E2.3: Operações Básicas com Strings (Prático)
• Crie duas variáveis string: primeiro_nome = "Grace" e
ultimo_nome = "Hopper".
• a) Concatene (junte) as strings primeiro_nome e ultimo_nome para formar
uma nova string chamada nome_completo. Certifique-se de incluir um espaço
entre o primeiro e o último nome. Imprima a variável nome_completo.
• b) Crie uma string chamada saudacao que seja a string “Olá, ” seguida pelo valor
de nome_completo e um ponto de exclamação no final. Imprima saudacao.
• c) Crie uma string chamada eco que seja o primeiro_nome repetido 4 vezes.
Imprima eco.
• d) Use a função len() para descobrir o número de caracteres na string
nome_completo (incluindo o espaço) e imprima esse valor.
4. E2.4: Entrada do Usuário e Conversão de Tipo (Prático)
Escreva um script Python que realize as seguintes ações em sequência:
(a) Peça ao usuário para digitar seu nome completo usando a função input() e
armazene a entrada em uma variável.
(b) Peça ao usuário para digitar o ano em que nasceu e armazene a entrada em
uma segunda variável.
(c) Lembre-se que input() sempre retorna uma string. Converta o ano de nasci-
mento (que está como string) para um tipo de dado inteiro.
(d) Supondo que o ano atual é 2025, calcule a idade aproximada do usuário (Ano
Atual - Ano de Nascimento Convertido).
(e) Usando uma f-string (introduzida na Seção 2.4), imprima uma mensagem perso-
nalizada para o usuário, como: Olá, [Nome Completo do Usuário]! Você
tem (ou fará) aproximadamente [Idade Calculada] anos em 2025.
5. E2.5: F-strings para Formatação de Saída (Prático)
Em um novo script:
• Crie três variáveis: nome_item = "Parafuso Sextavado M6", preco_unitario
= 0.7543, e quantidade_estoque = 1250.
439

• Use uma única f-string com a função print() para exibir as informações do
item no seguinte formato (preste atenção na formatação do preço com duas
casas decimais):
Item: Parafuso Sextavado M6 | Preço Unitário: R$ 0.75 |
Quantidade em Estoque: 1250 unidades
6. E2.6: Métodos de Strings – Limpeza e Capitalização (Prático)
Dada a seguinte string (com espaços extras no início/fim e caixa mista):
minha_string_suja = "python É UMA LINGUAGEM PODEROSA E versátil "
• a) Use o método strip() para remover os espaços em branco desnecessários
do início e do fim da string. Armazene o resultado em uma nova variável e
imprima-a.
• b) Pegue a string resultante do item (a) e use o método lower() para convertê-la
completamente para letras minúsculas. Imprima o resultado.
• c) Pegue a string resultante do item (b) e use o método capitalize() para
tornar apenas o primeiro caractere da frase maiúsculo e o restante minúsculo.
Imprima o resultado.
• d) Pegue a string resultante do item (b) novamente e use o método title()
para tornar a primeira letra de cada palavra maiúscula. Imprima o resultado.
7. E2.7: Métodos de Strings – Busca, Verificação e Substituição (Prático)
Considere a string: nome_arquivo_log = "log_servidor_web.[Link]"
• a) Use o método startswith() para verificar se a string começa com
"log_servidor". Imprima o resultado (True/False).
• b) Use o método endswith() para verificar se a string termina com ".txt".
Imprima o resultado. (Atenção: ela termina com ".backup").
• c) Use o método find() para encontrar a posição (índice) da primeira ocorrência
da substring "web". Imprima o resultado.
• d) Use o método find() para tentar encontrar a substring "ERRO". Imprima o
resultado (o que acontece se não encontra?).
• e) Use o método replace() para criar uma nova string onde todas as ocorrências
de "_" (underscore) sejam substituídas por -". Imprima a nova string.
8. E2.8: Métodos de Strings – split() e join() (Prático)
• a) Dada a string:
dados_sensores = "temperatura:25.5;umidade:60;pressao:1012"
Use o método split(’;’) para quebrar esta string em uma lista de strings,
usando o ponto e vírgula como delimitador. Imprima a lista resultante.
• b) Dada a lista de palavras:
lista_palavras_chave = ["python", "engenharia", "dados"]
Use o método de string .join(lista) para juntar os elementos desta lista em
uma única string, onde cada palavra é separada da próxima por um espaço em
branco. Imprima a string resultante.
• c) Use o método ", ".join(lista) para juntar os elementos da mesma
lista_palavras_chave, mas desta vez separando-os por uma vírgula seguida
de um espaço. Imprima a string resultante.
440

Projeto do Capítulo 2: Mini Perfil de Usuário Interativo


Título do Projeto: Construtor de Mini Perfil de Usuário Interativo.
Objetivos:

1. Praticar o uso da função input() para coletar diferentes tipos de dados do usuário.

2. Praticar a conversão de tipos de dados (de string para inteiro e/ou float).

3. Utilizar variáveis de forma eficaz para armazenar as informações coletadas.

4. Aplicar métodos de string para limpar e formatar adequadamente a entrada do


usuário e os dados a serem exibidos.

5. Usar f-strings para construir e exibir um resumo formatado das informações coletadas.

Descrição: Crie um script Python que interaja com o usuário através do console
para coletar algumas informações pessoais básicas. Após coletar os dados, o script deve
processar e limpar algumas dessas informações e, ao final, exibir um “mini perfil” bem
formatado para o usuário.
Tarefas Detalhadas:

1. Coleta de Dados do Usuário: Seu script deve usar a função input() para solicitar
e armazenar as seguintes informações:

• O nome completo do usuário.


• A cidade onde o usuário nasceu.
• O ano em que o usuário nasceu.
• A altura do usuário em metros.
• Um hobby principal do usuário.

2. Processamento e Limpeza dos Dados Coletados: Antes de exibir o perfil,


aplique as seguintes transformações:

• Nome Completo: Remova quaisquer espaços em branco extras do início ou


do fim. Em seguida, formate o nome para que cada palavra comece com uma
letra maiúscula e o restante seja minúsculo.
• Cidade de Nascimento: Remova espaços extras e formate para que apenas a
primeira letra da cidade seja maiúscula.
• Ano de Nascimento: Converta a string entrada pelo usuário para um número
inteiro. Calcule a idade aproximada do usuário subtraindo o ano de nascimento
do ano atual.
• Altura: O usuário pode digitar a altura com vírgula (ex: “1,90”) ou ponto
(ex: “1.90”). Seu script deve ser capaz de lidar com ambas as entradas (dica:
substitua a vírgula por ponto, se presente) e então converter o valor para um
número de ponto flutuante (float).
• Hobby Principal: Apenas remova quaisquer espaços em branco extras do
início ou do fim.
441

3. Exibição do Perfil Formatado: Use f-strings para imprimir no console um perfil


bem formatado, mostrando todas as informações coletadas e processadas. A saída
deve ser clara e legível. Exemplo de como poderia ser a saída:

*****************************************
*** Seu Mini Perfil ***
*****************************************
Nome Completo....: Alan Afif Helal
Natural de.......: VItória
Idade Estimada...: 38 anos (em 2025)
Altura...........: 1.90 m
Hobby Principal..: Programar em Python
*****************************************

4. Comentários no Código: Adicione comentários ao seu código Python para explicar


as principais seções e a lógica de processamento de dados.

Desafio Extra (Opcional):

• Após exibir o perfil, pergunte ao usuário se ele gostaria que o programa gerasse uma
sugestão de nome de usuário (username) para um sistema.

• Se o usuário responder “sim” (ou “s”), crie um nome de usuário simples. Por exemplo,
poderia ser a primeira letra do primeiro nome, concatenada com o último sobrenome
completo (tudo em letras minúsculas), e os dois últimos dígitos do ano de nascimento.

– Exemplo: Para “Alan Afif Helal” nascido em “1987”, o username sugerido poderia
ser “ahelal87”.

• Use os métodos de string que você aprendeu (split(), fatiamento, lower()) para
construir essa sugestão de username. Imprima a sugestão para o usuário.

Capítulo 3: Operadores e Expressões (Exercícios e Projeto)


Neste capítulo, exploramos os diversos operadores que o Python oferece para realizar
cálculos, comparações, atribuições, e operações lógicas e de associação. Também entende-
mos o que são expressões e como o Python as avalia. Os exercícios e o projeto a seguir
visam reforçar seu entendimento e aplicação desses conceitos fundamentais.

Exercícios
1. E3.1: Cálculos de Engenharia com Operadores Aritméticos (Prático)
Crie um script Python que realize os seguintes cálculos, pedindo os dados necessários
ao usuário e imprimindo os resultados de forma clara.

(a) Área de um Círculo: Peça ao usuário para fornecer o raio de um círculo


(que pode ser um número decimal, ou seja, float). Calcule a área do círculo
usando a fórmula A = πr2 . Para π, você pode usar o valor aproximado 3.14159.
Imprima o raio fornecido e a área calculada com uma mensagem explicativa.
442

(b) Conversão de Temperatura: Peça ao usuário para fornecer uma temperatura


em graus Celsius (float). Converta essa temperatura para Fahrenheit usando
a fórmula F = C × 95 + 32 e para Kelvin usando K = C + 273.15. Imprima a
temperatura original em Celsius e os resultados convertidos para Fahrenheit e
Kelvin.
(c) Divisão Inteira e Resto: Peça ao usuário para digitar dois números inteiros,
um dividendo e um divisor. Calcule e imprima o resultado da divisão inteira
(//) entre o dividendo e o divisor, e também o resto da divisão (%).

2. E3.2: Simulação de Saldo Bancário com Atribuição Composta (Prático)

• Inicialize uma variável chamada saldo_conta com o valor 1000.00.


• Simule as seguintes transações bancárias, atualizando o saldo_conta após cada
operação usando operadores de atribuição composta (+=, -=, *=). Imprima o
saldo após cada operação com uma mensagem descritiva.
(a) Um depósito de R$ 500.50.
(b) Um saque de R$ 120.75.
(c) Um rendimento de 0.5% sobre o saldo atual (isso é o mesmo que somar
saldo_conta * 0.005 ao saldo, ou multiplicar o saldo por 1.005).
(d) Uma tarifa de manutenção de conta de R$ 25.30.

3. E3.3: Verificações Lógicas com Operadores de Comparação (Prático)

• Escreva um script que peça ao usuário para digitar sua idade (como um número
inteiro).
• Em seguida, use operadores de comparação para verificar as seguintes con-
dições e imprima o resultado booleano (True ou False) de cada verificação,
acompanhado de uma mensagem explicativa.
(a) Se a idade é maior ou igual a 18.
(b) Se a idade é exatamente igual a 25.
(c) Se a idade é diferente de 30.
(d) Se a idade é estritamente menor que 12.
(e) Se a idade está entre 18 (inclusive) e 65 (exclusive) (Dica: você precisará
combinar duas comparações com um operador lógico).

4. E3.4: Explorando Operadores Lógicos e Curto-Circuito (Prático e Con-


ceitual)

(a) Crie duas variáveis booleanas: chovendo = True e guarda_chuva_disponivel


= False. Avalie e imprima o resultado das seguintes expressões lógicas, ex-
plicando (em um comentário no código ou mentalmente) o que cada uma
significa:
• chovendo and guarda_chuva_disponivel (Vou me molhar?)
• chovendo or guarda_chuva_disponivel (Pelo menos uma condição para
não sair totalmente despreparado?)
• not chovendo (O tempo está bom?)
• (chovendo and not guarda_chuva_disponivel) (Situação ruim?)
443

(b) Curto-Circuito e Truthiness/Falsiness: Qual você espera que seja o re-


sultado das seguintes expressões? Execute-as no Python para verificar e tente
explicar o porquê, baseando-se no comportamento de curto-circuito dos ope-
radores and e or e na forma como Python avalia a “verdade” de valores não
booleanos (relembre a Seção 4.8, ou faremos uma breve introdução aqui: valores
como 0, string vazia , e None são considerados “Falsy”; a maioria dos outros
valores são “Truthy”).
• print(0 and "Resultado inesperado")
• print(1 and "Resultado esperado")
• print("Texto"or "Outro texto")
• print( or "Texto padrão se o primeiro for vazio")
• print(not 0)
• print(not "Algum texto")

5. E3.5: Diferenciando Identidade (is) de Igualdade (==) (Prático e Concei-


tual)
Crie as seguintes variáveis no Python:
1 a_lista = [10 , 20 , 30]
2 b_lista = [10 , 20 , 30]
3 c_lista = a_lista
4
5 num1 = 1000
6 num2 = 1000
7
8 str1 = " python "
9 str2 = " py " + " thon "

Código 21.26: Variáveis para teste de identidade vs. igualdade

Para cada par de variáveis abaixo, use print() para mostrar o resultado da compa-
ração com == (igualdade de valor) e com is (identidade de objeto na memória). Em
um comentário, explique por que os resultados são como são.

• a_lista e b_lista
• a_lista e c_lista
• num1 e num2 (Teste com números pequenos como 5 e 5, e depois com números
maiores como 1000 e 1000. O comportamento pode surpreender para inteiros
pequenos devido a otimizações internas do Python, mas o conceito geral é o
importante).
• str1 e str2 (Python também pode otimizar o armazenamento de strings
idênticas).

O objetivo é entender que == verifica se os valores são iguais, enquanto is verifica se


ambas as variáveis apontam para exatamente o mesmo local na memória.

6. E3.6: Verificando Associação com in e not in (Prático)

• Crie uma string contendo uma frase, por exemplo: minha_frase = "A engenharia
de software é fundamental."
444

• Use o operador in para verificar se a substring "software" está presente em


minha_frase. Imprima o resultado.
• Use o operador not in para verificar se a substring "hardware" não está
presente em minha_frase. Imprima o resultado.
• Crie uma lista de alguns componentes eletrônicos: componentes = ["resistor",
"capacitor", "diodo", "transistor"].
• Peça ao usuário para digitar o nome de um componente.
• Use o operador in para verificar se o componente digitado pelo usuário está na
lista componentes e imprima uma mensagem informando se ele foi encontrado
ou não.
7. E3.7: Precedência de Operadores e Uso de Parênteses (Prático)
Considere a seguinte expressão matemática: R = V1 + V2 × I − VPtotal
2
f onte

• a) Suponha os seguintes valores: V1 = 10, V2 = 5, I = 2, Ptotal = 50, Vf onte = 5.


Calcule manualmente o valor de R, prestando atenção à ordem de precedência
padrão dos operadores aritméticos (potenciação primeiro, depois multiplica-
ção/divisão, depois adição/subtração).
• b) Escreva um script Python para calcular e imprimir o valor de R usando os
mesmos valores. Verifique se o resultado do Python coincide com seu cálculo
manual. O código Python seria:
1 V1 = 10
2 V2 = 5
3 I_val = 2 # Evitando usar ’I ’ sozinho , que pode
ser confundido com imagin á rio
4 P_total = 50
5 V_fonte = 5
6 R = V1 + V2 * I_val - P_total / V_fonte ** 2
7 print ( f " O resultado de R é : { R } " )

Código 21.27: Cálculo de R

• c) Agora, modifique a expressão Python usando parênteses para forçar as


seguintes ordens de cálculo:
(a) A adição (V1 + V2 ) deve ser feita primeiro.
(b) O resultado disso deve ser multiplicado por I.
(c) Vf2onte deve ser calculado.
(d) Ptotal deve ser dividido pelo resultado de Vf2onte .
(e) Finalmente, o resultado de (2) deve ter o resultado de (4) subtraído dele.
Escreva a nova expressão Python com parênteses e imprima o novo valor de R.

Capítulo 4: Estruturas de Controle Condicional (Exercícios e Pro-


jeto)
Neste capítulo, aprendemos como dar aos nossos programas a capacidade de tomar
decisões e seguir diferentes caminhos de execução com base em condições. Exploramos
as estruturas if, elif, else, condicionais aninhadas, o operador ternário e o conceito de
valores “Truthy” e “Falsy”. Os exercícios e o projeto a seguir ajudarão você a praticar e
solidificar esses conceitos.
445

Exercícios
1. E4.1: Classificação de Triângulos (Prático)
Escreva um script Python que:
(a) Peça ao usuário para inserir os comprimentos dos três lados de um potencial
triângulo (A, B e C). Permita que os comprimentos sejam números decimais
(float).
(b) Verificação da Condição de Existência: Primeiro, verifique se os lados
fornecidos podem de fato formar um triângulo. Lembre-se da desigualdade
triangular: a soma de quaisquer dois lados de um triângulo deve ser estritamente
maior que o terceiro lado (A + B > C, A + C > B, e B + C > A). Se essa condição
não for atendida para todos os pares, imprima uma mensagem informando que
os lados fornecidos não formam um triângulo.
(c) Classificação (se for um triângulo): Se os lados puderem formar um
triângulo, use estruturas if, elif e else para classificar e imprimir se o
triângulo é:
• Equilátero: Todos os três lados são iguais.
• Isósceles: Exatamente dois lados são iguais.
• Escaleno: Todos os três lados são diferentes.
Certifique-se de testar seu programa com diferentes conjuntos de valores para os
lados.
2. E4.2: Verificador de Ano Bissexto (Prático)
Crie um script que peça ao usuário para inserir um ano (como um número inteiro).
O programa deve então determinar se o ano é bissexto ou não e imprimir uma
mensagem clara para o usuário. As regras para um ano ser bissexto são:
• O ano é divisível por 4, mas não é divisível por 100, A MENOS QUE...
• O ano seja divisível por 400 (nesse caso, ele é bissexto, mesmo sendo divisível
por 100).
Exemplos: 2000 e 2400 são bissextos. 1800, 1900, 2100 não são bissextos. 2023 não
é, 2024 é. Utilize operadores lógicos (and, or) e o operador módulo (%) dentro de
suas estruturas if-else para implementar essa lógica.
3. E4.3: Calculadora Simples com Menu de Opções (Prático)
Desenvolva um script Python que funcione como uma calculadora básica, oferecendo
um menu de opções ao usuário.
(a) Apresente ao usuário o seguinte menu:
--- Calculadora Simples ---
1. Somar dois números
2. Subtrair dois números
3. Multiplicar dois números
4. Dividir dois números
5. Sair
-------------------------
446

(b) Peça ao usuário para digitar a opção desejada (um número de 1 a 5). Converta
a entrada para inteiro.
(c) Use uma estrutura if-elif-else para tratar a escolha do usuário:
• Se a opção for 1, 2, 3 ou 4:
– Peça ao usuário para inserir dois números (permita decimais, ou seja,
float).
– Realize a operação matemática correspondente.
– Imprima o resultado de forma clara (ex: “O resultado de [num1] +
[num2] é [soma]”).
– Tratamento Especial para Divisão: Se a opção for 4 (dividir), antes
de realizar a divisão, verifique se o segundo número (o divisor) é zero.
Se for, imprima uma mensagem de erro (ex: “Erro: Divisão por zero
não é permitida.”) em vez de deixar o programa quebrar. Use um if
aninhado para isso.
• Se a opção for 5: Imprima uma mensagem como “Saindo da calculadora...
Até logo!” e o programa pode terminar.
• Se a opção digitada não for nenhuma das válidas (1 a 5): Imprima “Opção
inválida. Por favor, tente novamente.”

4. E4.4: Interpretando Valores “Truthy” e “Falsy” (Conceitual/Prático)


Para cada um dos blocos de código Python abaixo, primeiro preveja qual mensagem
será impressa no console. Justifique sua previsão com base no conceito de valores
“Truthy” e “Falsy” que foi apresentado na Seção 4.8. Depois de fazer suas previsões,
execute o código em um script Python para verificar se você acertou.
1 # Bloco a )
2 valor_a = 0
3 if valor_a :
4 print ( " Bloco a : Truthy " )
5 else :
6 print ( " Bloco a : Falsy " )
7
8 # Bloco b )
9 valor_b = " " # String vazia
10 if valor_b :
11 print ( " Bloco b : Truthy " )
12 else :
13 print ( " Bloco b : Falsy " )
14
15 # Bloco c )
16 valor_c = [] # Lista vazia
17 if valor_c :
18 print ( " Bloco c : Truthy " )
19 else :
20 print ( " Bloco c : Falsy " )
21
22 # Bloco d )
23 valor_d = -5 # N ú mero negativo
24 if valor_d :
25 print ( " Bloco d : Truthy " )
26 else :
27 print ( " Bloco d : Falsy " )
447

28
29 # Bloco e )
30 valor_e = " Ol á " # String com conte ú do
31 if valor_e :
32 print ( " Bloco e : Truthy " )
33 else :
34 print ( " Bloco e : Falsy " )
35
36 # Bloco f )
37 valor_f = None
38 if valor_f :
39 print ( " Bloco f : Truthy " )
40 else :
41 print ( " Bloco f : Falsy " )

Código 21.28: Blocos para análise de Truthy/Falsy

5. E4.5: Operador Ternário para Mensagens Simples (Prático)

• a) Peça ao usuário para digitar sua nota em uma prova (um número de 0 a 10,
que pode ser float).
• b) Usando o operador condicional ternário (Seção 4.7), atribua a uma variável
chamada status_aluno o valor string "Aprovado(a)" se a nota for maior ou
igual a 7.0, ou o valor string "Reprovado(a)" caso contrário.
• c) Imprima o valor da variável status_aluno.
• d) Em seguida, use outro operador ternário para atribuir a uma variável
mensagem_adicional uma frase de incentivo como "Continue estudando para
a recuperação!" se o status for “Reprovado(a)”, ou uma frase de parabéns como
"Excelente! Parabéns pelo resultado!" se o status for “Aprovado(a)”.
• e) Imprima o valor da variável mensagem_adicional.

6. E4.6: Múltiplos ifs Independentes vs. Estrutura if-elif-else (Conceitu-


al/Análise de Código)
Considere os dois blocos de código Python abaixo, que tentam classificar um número
fornecido pelo usuário:
1 # --- Bloco 1: M ú ltiplos ifs independentes ---
2 print ( " --- Bloco 1 ---" )
3 numero_bloco1 = int ( input ( " Digite um n ú mero para o Bloco
1: " ) )
4 if numero_bloco1 > 0:
5 print ( " O n ú mero é positivo . " )
6 if numero_bloco1 < 10: # Esta condi ç ã o é checada
indep endentem ente da anterior
7 print ( " O n ú mero é menor que 10. " )
8 if numero_bloco1 % 2 == 0: # Esta tamb é m é checada
indep endentem ente
9 print ( " O n ú mero é par . " )
10
11 # --- Bloco 2: Estrutura if - elif - else ---
12 print ( " \n - - - Bloco 2 ---" )
13 numero_bloco2 = int ( input ( " Digite um n ú mero para o Bloco
2: " ) )
14 if numero_bloco2 < 0:
448

15 print ( " O n ú mero é negativo . " )


16 elif numero_bloco2 < 10: # S ó é checado se o ’ if
numero_bloco2 < 0 ’ for FALSO
17 print ( " O n ú mero é positivo e menor que 10 ( ou zero ) . " )
18 elif numero_bloco2 < 20: # S ó é checado se os anteriores
forem FALSOS
19 print ( " O n ú mero est á entre 10 ( inclusive ) e 19
( inclusive ) . " )
20 else : # S ó executa se NENHUMA das condi ç õ es anteriores for
VERDADEIRA
21 print ( " O n ú mero é 20 ou maior . " )

Código 21.29: Comparando múltiplos ifs com if-elif-else

Responda (pode ser em comentários no seu código ou em um arquivo de texto):

(a) Para o Bloco 1, se o usuário digitar o número 6, quais mensagens serão impressas?
E se o usuário digitar 12? E se digitar -4? Explique o porquê em cada caso.
(b) Para o Bloco 2, se o usuário digitar o número 6, qual mensagem será impressa?
E se digitar 12? E se digitar -4? E se digitar 25? Explique o porquê em cada
caso.
(c) Qual é a principal diferença no fluxo de execução e no comportamento lógico
entre usar múltiplos ifs independentes (como no Bloco 1) e usar uma estrutura
if-elif-else (como no Bloco 2)? Em que tipo de situação cada abordagem
seria mais apropriada? (Releia a Seção 4.5 se necessário).

Projeto do Capítulo 4: Sistema Simples de Validação de Acesso


Título do Projeto: Validador de Acesso Básico.
Objetivos:

1. Praticar o uso da função input() para obter credenciais do usuário (nome de usuário
e senha).

2. Aplicar estruturas if, elif, e else para validar as credenciais fornecidas.

3. Utilizar operadores de comparação (==, !=) e operadores lógicos (and, or, not).

4. Implementar estruturas condicionais aninhadas para lidar com diferentes níveis de


verificação ou mensagens de erro.

5. Fornecer feedback claro e informativo ao usuário sobre o resultado da sua tentativa


de acesso.

Descrição: Crie um script Python que simule um sistema de login muito simples.
O sistema terá um nome de usuário e senha “mestres” pré-definidos em variáveis no seu
código. Ele também pode ter um usuário “convidado” com acesso limitado (que pode ou
não precisar de senha, a seu critério).
Credenciais Pré-definidas (Defina-as no início do seu script):

• Usuário Mestre (Administrador): usuario_mestre = "admin"

• Senha Mestre: senha_mestre = "PythonRocks123"


449

• Usuário Convidado: usuario_convidado = "guest"


• Senha do Convidado (Opcional): Se decidir que o convidado precisa de senha, defina
uma, por exemplo: senha_convidado = "1234"
Tarefas Detalhadas:
1. Solicitar Credenciais ao Usuário:
• Use a função input() para pedir ao usuário que digite seu nome de usuário.
Armazene em uma variável.
• Use a função input() para pedir ao usuário que digite sua senha. Armazene
em outra variável.
2. Lógica de Validação Principal: Implemente a lógica de validação usando if,
elif e else:
• Verificar Usuário Mestre:
– Use um if para verificar se o nome de usuário digitado é igual ao usuario_mestre.
– Se for o usuário mestre, aninhe outro if-else para verificar a senha:
∗ Se a senha digitada for igual à senha_mestre, imprima uma mensagem
de boas-vindas como: “Acesso concedido! Bem-vindo(a), Administra-
dor(a) [Nome do Usuário Mestre]! Você tem acesso total ao sistema.”
∗ Caso contrário (senha do administrador incorreta), imprima: “Senha
incorreta para o usuário administrador.”
• Verificar Usuário Convidado:
– Use um elif para verificar se o nome de usuário digitado é igual ao
usuario_convidado.
– (Opcional: se você definiu uma senha para convidado, adicione uma verifi-
cação de senha aninhada aqui também).
– Se for o usuário convidado (e a senha, se houver, estiver correta), imprima:
“Acesso concedido! Bem-vindo(a), Convidado(a)! Você tem acesso limitado
ao sistema.”
– (Opcional: Se houver senha de convidado e ela estiver errada, imprima uma
mensagem apropriada).
• Usuário Inválido:
– Use um else para o caso de o nome de usuário digitado não corresponder
a nenhum dos anteriores. Imprima: “Nome de usuário não reconhecido ou
inválido.”
3. Feedback Adicional Usando “Truthiness” (Opcional):
• No início da lógica de validação, antes de comparar os usuários e senhas, verifique
se o usuário digitou algo para o nome de usuário e para a senha (ou seja, se as
strings retornadas por input() não são vazias).
• Se o nome de usuário OU a senha estiverem em branco, imprima uma men-
sagem como “Nome de usuário e senha não podem estar em branco. Tente
novamente.” e talvez não prossiga com as verificações de ‘usuario_mestre‘ ou
‘usuario_convidado‘. Você pode usar a “truthiness” de strings vazias (que são
“Falsy”) para essa verificação.
450

4. Comentários no Código: Adicione comentários para explicar as diferentes partes


da sua lógica de validação.

Desafio Extra (Opcional):

• Múltiplas Tentativas de Senha: Para o usuário administrador, se a senha estiver


incorreta, em vez de apenas dar uma mensagem de erro, permita que ele tente digitar
a senha mais uma ou duas vezes. (Isso provavelmente exigiria um laço, que é o
tópico do Capítulo 5. Se você quiser se adiantar e pesquisar sobre laços while ou for
para implementar isso, seria um ótimo desafio! Caso contrário, apenas a verificação
simples da senha é suficiente para este projeto do Capítulo 4).

• Operador Ternário para Mensagem de Status: Ao final do script, use o operador


ternário para definir uma variável status_final_acesso com base no sucesso ou
falha do login (ex: “Logado como Administrador”, “Logado como Convidado”, “Falha
no Login”). Imprima essa variável.

Capítulo 5: Estruturas de Repetição (Exercícios e Projeto)


Neste capítulo, exploramos como fazer nossos programas repetirem tarefas usando os
laços while e for. Vimos também os comandos break e continue para controlar o fluxo
dos laços, a cláusula else em laços, e como aninhar laços para tarefas mais complexas.
Os exercícios e o projeto a seguir visam consolidar esses conceitos essenciais.

Exercícios
1. E5.1: Contagem com o Laço while (Prático)
Crie um script Python que realize as seguintes tarefas de contagem usando o laço
while:

(a) Imprima os números de 1 a 10 (inclusive), um por linha.


(b) Modifique o script para imprimir apenas os números pares de 2 a 20 (inclusive).
(c) Modifique o script novamente para imprimir os números de 10 de volta a 1
(contagem regressiva), um por linha.

Para cada item, certifique-se de inicializar corretamente sua variável de controle,


definir a condição do while e atualizar a variável de controle dentro do laço para
evitar laços infinitos.

2. E5.2: Jogo de Adivinhação com while (Prático)

• Defina uma variável numero_secreto com um valor inteiro à sua escolha (ex:
42).
• Crie um laço while que continue executando enquanto o usuário não adivinhar
o número.
• Dentro do laço:
– Peça ao usuário para digitar um palpite (converta para inteiro).
– Compare o palpite com o numero_secreto.
451

– Se o palpite for igual ao numero_secreto, imprima uma mensagem de


parabéns (ex: “Parabéns, você acertou!”) e use a instrução break para sair
do laço.
– Se o palpite for menor que o número secreto, imprima “Muito baixo! Tente
novamente.”
– Se o palpite for maior que o número secreto, imprima “Muito alto! Tente
novamente.”
• Desafio: Adicione um contador de tentativas. No final, se o usuário acertar,
diga quantas tentativas foram necessárias.
3. E5.3: Tabuada de Multiplicação com for e range() (Prático)
• Peça ao usuário para digitar um número inteiro (ex: 7).
• Use um laço for em conjunto com a função range() para imprimir a tabuada
de multiplicação desse número, de 1 até 10.
• A saída deve ser formatada de forma clara, por exemplo, para o número 7:
Tabuada do 7:
7 x 1 = 7
7 x 2 = 14
...
7 x 10 = 70

4. E5.4: Soma de Números Ímpares com for e continue (Prático)


• O objetivo é calcular e imprimir a soma de todos os números ímpares no
intervalo de 1 a 100 (inclusive).
• Use um laço for e a função range() para iterar por todos os números de 1 a
100.
• Dentro do laço, use uma instrução if para verificar se o número atual é par.
• Se o número for par, use a instrução continue para pular o restante do código
do laço para aquela iteração e ir para o próximo número.
• Se o número for ímpar, adicione-o a uma variável que acumula a soma.
• Após o laço terminar, imprima a soma total dos números ímpares.
5. E5.5: Procurando um Item na Lista com for...else (Prático)
Lembre-se que laços for (e while) em Python podem ter uma cláusula else que é
executada se o laço terminar normalmente (ou seja, sem ser interrompido por um
break).
• Crie uma lista de nomes, por exemplo: nomes_convidados = ["Alice", "Bruno",
"Carlos", "Diana", "Eduardo"].
• Peça ao usuário para digitar um nome que ele gostaria de procurar na lista.
• Use um laço for para percorrer a lista nomes_convidados.
• Dentro do laço, verifique se o nome atual da iteração é igual ao nome procu-
rado (lembre-se de considerar a sensibilidade a maiúsculas/minúsculas ou use
.lower() para comparar).
452

• Se o nome for encontrado, imprima uma mensagem como “[NomeProcurado]


está na lista de convidados!” e use a instrução break para sair do laço.
• Adicione uma cláusula else ao laço for. O código dentro do else só será
executado se o break nunca for chamado. Nele, imprima uma mensagem como
“[NomeProcurado] NÃO foi encontrado na lista de convidados.”

Teste seu script com um nome que está na lista e com um que não está.

6. E5.6: Desenhando Padrões Simples com Laços Aninhados (Prático)


Use laços for aninhados (um laço dentro de outro) e a função print() para desenhar
os seguintes padrões de asteriscos no console. (Dica: print(’*’, end=”) imprime
um asterisco sem pular para a próxima linha; print() sozinho pula uma linha).

(a) Triângulo Crescente:


*
**
***
****
*****

(b) Triângulo Decrescente:


*****
****
***
**
*

(c) (Mais desafiador) Quadrado de 5x5:


*****
*****
*****
*****
*****

7. E5.7: Validação de Senha Simples com while True (Prático)

• Defina uma senha correta em uma variável


(ex: senha_correta = "PythonÉLegal!2025").
• Use um laço while True: para criar um loop "infinito".
• Dentro do laço, peça ao usuário para digitar a senha.
• Verifique se a senha digitada é igual à senha_correta.
• Se for, imprima “Acesso concedido! Bem-vindo(a)!” e use break para sair do
laço.
• Se não for, imprima “Senha incorreta. Tente novamente.”
453

Projeto do Capítulo 5: Jogo Simples de “Acerte o Alvo” no Console


Título do Projeto: Mini Jogo de “Acerte o Alvo” no Console.
Objetivos:
1. Praticar o uso de laços while para controlar o fluxo principal do jogo e o número de
tentativas.

2. Utilizar laços for para iterações ou contagens, se necessário (embora while seja
mais central aqui).

3. Aplicar estruturas if-elif-else para tomar decisões com base na entrada do


jogador e no resultado do tiro.

4. Usar a função input() para obter dados do jogador.

5. Praticar o uso de break para terminar o jogo em certas condições.

6. (Opcional) Introduzir a geração de números aleatórios com o módulo random.


Descrição do Jogo: Crie um jogo simples baseado em texto onde um “canhão”
(controlado pelo jogador) tenta acertar um “alvo” que está posicionado em uma linha
unidimensional (imagine uma reta numérica).
Funcionalidades a Implementar:
1. Configuração Inicial do Jogo:

• Posição do Alvo: O alvo estará em uma posição inteira secreta entre 1 e 100
(inclusive).
– Forma Simples: Você pode definir um valor fixo para o alvo no início do
seu código (ex: posicao_alvo = 73).
– Forma Mais Interessante (Requer import random): Gere a posição do alvo
aleatoriamente. Para isso, no início do seu script, adicione import random.
Depois, use posicao_alvo = [Link](1, 100) para sortear um
número entre 1 e 100.
• Munição Inicial: O jogador começa com um número fixo de munições (ex:
municao = 10).
• Tentativas: Inicialize um contador de tentativas em 0.

2. Loop Principal do Jogo (Controlado por um Laço while): O jogo deve conti-
nuar enquanto o jogador tiver munição (municao > 0) E ainda não tiver acertado o
alvo (você precisará de uma variável booleana para controlar isso, ex: alvo_acertado
= False).

• Dentro de cada rodada do laço:


(a) Imprima para o jogador quantas munições ele ainda tem e qual é a sua
tentativa atual (ex: “Tentativa 1/10, Munição: 10”).
(b) Peça ao jogador para digitar a posição do seu tiro (um número inteiro entre
1 e 100). Converta a entrada para inteiro.
(c) Decremente a quantidade de munição em 1.
(d) Incremente o contador de tentativas.
454

3. Verificação do Tiro (Usando if-elif-else): Após o jogador dar o tiro:


• Acerto!: Se a posição do tiro for igual à posicao_alvo:
– Imprima uma mensagem de parabéns, como: “BOOM! Você acertou o alvo
em tentativas tentativa(s)!”
– Defina sua variável booleana alvo_acertado para True (ou use break
diretamente para sair do laço principal).
• Erro!: Se o tiro errar o alvo:
– Calcule a distância entre o tiro e o alvo (use abs(tiro - posicao_alvo)).
– Dê um feedback sobre a proximidade:
∗ Se a distância for de 1 a 5: Imprima “MUITO PERTO!”
∗ Se a distância for de 6 a 10: Imprima “PERTO.”
∗ Se a distância for maior que 10: Imprima “LONGE...”
– Além da proximidade, dê uma dica direcional:
∗ Se o tiro foi menor que a posição do alvo: Imprima “Seu tiro foi MUITO
BAIXO.”
∗ Se o tiro foi maior que a posição do alvo: Imprima “Seu tiro foi MUITO
ALTO.”
4. Condições de Fim de Jogo: Após o laço while terminar (seja por acertar o alvo
ou por acabar a munição):
• Se o alvo não foi acertado (a variável alvo_acertado ainda é False, ou você
saiu do laço porque municao chegou a 0):
– Imprima: “FIM DE JOGO! Você ficou sem munição. O alvo estava na
posição posicao_alvo.”
5. Comentários: Não se esqueça de comentar seu código.
Desafios Extras (Opcionais):
• Alvo Móvel: Após cada tiro errado do jogador, faça o alvo se mover aleatoria-
mente 1 ou 2 posições para a esquerda ou para a direita (ex: posicao_alvo +=
[Link]([-2, -1, 1, 2])), mas garantindo que ele permaneça dentro do
limite de 1 a 100. Informe ao jogador se o alvo se moveu (sem dizer para onde).
• Jogar Novamente: No final do jogo (ganhando ou perdendo), pergunte ao usuário
se ele quer jogar novamente. Se ele digitar “sim” (ou “s”), reinicie todo o jogo (isso
geralmente envolve colocar o loop principal do jogo dentro de outro loop while
True: e usar break para sair completamente quando o jogador não quiser mais
jogar).

Parte II: Estruturas de Dados Fundamentais


Capítulo 6: Listas (Lists) (Exercícios e Projeto)
Neste capítulo, mergulhamos na estrutura de dados mais versátil e fundamental do
Python: as listas. Aprendemos a criá-las, acessá-las, fatiá-las, modificá-las, usar seus
diversos métodos, aplicar compreensões de listas e trabalhar com listas aninhadas. Os
exercícios e o projeto a seguir têm como objetivo solidificar seu domínio sobre as listas.
455

Exercícios
1. E6.1: Criação e Manipulação Básica de Listas (Prático)

(a) Crie uma lista chamada disciplinas_semestre e adicione a ela os nomes de


pelo menos 5 disciplinas que você está cursando atualmente ou que tem interesse
em cursar. Imprima a lista completa no console.
(b) Crie uma segunda lista chamada notas_desejadas contendo 5 notas (números
de ponto flutuante) que você gostaria de alcançar ou que considera como
metas para as disciplinas listadas anteriormente (não precisam corresponder
exatamente, apenas ser uma lista de notas). Imprima esta lista.
(c) Crie uma terceira lista chamada meus_dados_academicos. Esta lista deve ser
uma lista mista contendo: seu nome completo (como uma string), sua idade
(como um inteiro), e a lista notas_desejadas inteira como um de seus elementos
(criando assim uma lista aninhada). Imprima a lista meus_dados_academicos.
(d) Use a função list() em conjunto com a função range() para criar uma lista
chamada numeros_impares_ate_10 que contenha os números ímpares de 1 até
9 (inclusive). Imprima esta lista.
(e) Converta a string "Engenharia" em uma lista onde cada caractere da string
é um elemento da lista. Use a função list() para isso. Imprima a lista de
caracteres resultante.

2. E6.2: Acesso a Elementos e Fatiamento de Listas (Prático)


Considere a seguinte lista de elementos químicos (os primeiros 8 da tabela periódica):
1 e le men to s_ qui mi co s = [ " Hidrog ê nio " , " H é lio " , " L í tio " ,
" Ber í lio " ,
2 " Boro " , " Carbono " , " Nitrog ê nio " , " Oxig ê nio " ]

Código 21.30: Lista de elementos químicos para E6.2

Realize as seguintes operações e imprima o resultado de cada uma:

(a) Imprima o primeiro elemento da lista elementos_quimicos.


(b) Imprima o último elemento da lista usando indexação negativa.
(c) Imprima o quarto elemento da lista (que é “Berílio”).
(d) Use fatiamento para criar uma nova lista chamada metais_alcalinos_terrosos
contendo os elementos do “Berílio” (inclusive) até o “Carbono” (inclusive). Im-
prima esta nova lista.
(e) Use fatiamento para criar uma nova lista contendo apenas os três primeiros
elementos da lista elementos_quimicos.
(f) Use fatiamento para criar uma nova lista contendo apenas os três últimos
elementos da lista elementos_quimicos.
(g) Use fatiamento com passo (stride) para criar uma nova lista que contenha
todos os elementos de elementos_quimicos em posições de índice ímpar (Hélio,
Berílio, Carbono, Oxigênio).
456

(h) Crie uma cópia exata da lista elementos_quimicos usando a técnica de fa-
tiamento [:]. Chame esta cópia de elementos_copia. Modifique o pri-
meiro item da elementos_copia para "Deutério". Imprima tanto a lista
elementos_copia quanto a lista elementos_quimicos original para verificar
que a original não foi alterada pela modificação na cópia.

3. E6.3: Modificando Listas – Adição e Remoção (Prático)


Comece com a seguinte lista de tarefas: minhas_tarefas = ["Planejar o projeto",
"Escrever a introdução"]

(a) Imprima a lista minhas_tarefas inicial.


(b) O planejamento do projeto foi atualizado. Altere o primeiro item da lista (índice
0) para "Revisar e detalhar o planejamento do projeto". Imprima a
lista modificada.
(c) Adicione a tarefa "Desenvolver o primeiro módulo" ao final da lista usando
o método append(). Imprima a lista.
(d) Houve uma urgência! Insira a tarefa "Reunião urgente com cliente" na
segunda posição da lista (ou seja, no índice 1) usando o método insert().
Imprima a lista.
(e) Crie uma segunda lista chamada tarefas_adicionais = ["Testar o módulo",
"Documentar o código"]. Use o método extend() para adicionar todos os
itens de tarefas_adicionais ao final da lista minhas_tarefas. Imprima a
lista minhas_tarefas resultante.
(f) A “Reunião urgente com cliente” foi cancelada. Remova este item específico da
lista minhas_tarefas usando o método remove(). (Atenção: certifique-se de
que a string que você passa para remove() seja idêntica à da lista). Imprima a
lista.
(g) Você concluiu a última tarefa da lista. Use o método pop() para remover o
último item de minhas_tarefas e armazene esse item removido em uma variável
chamada tarefa_concluida. Imprima a tarefa_concluida e também a lista
minhas_tarefas atualizada.
(h) Decidiu-se que a primeira tarefa da lista atual não é mais prioritária. Use a
instrução del para remover o primeiro item (índice 0) da lista minhas_tarefas.
Imprima a lista final.

4. E6.4: Métodos Comuns de Listas – Ordenação, Contagem, Busca (Prático)


Considere a lista de medições (simuladas) de um sensor: leituras_sensor = [22.5,
23.1, 21.9, 23.1, 24.0, 22.5, 23.1, 22.8]

(a) Use o método count() para descobrir e imprimir quantas vezes o valor 23.1
aparece na lista.
(b) Use o método index() para encontrar e imprimir a posição (índice) da primeira
ocorrência do valor 24.0.
(c) Tente usar index() para encontrar um valor que não está na lista (ex: 25.0).
O que acontece? (Você pode comentar esta linha para que o script não pare
com erro, ou usar um if valor in lista: antes de chamar index() para
evitar o erro).
457

(d) Crie uma cópia da lista leituras_sensor chamada leituras_ordenadas


usando o método copy().
(e) Use o método sort() para ordenar a lista leituras_ordenadas em ordem
crescente (do menor para o maior). Imprima a lista leituras_ordenadas.
Verifique se leituras_sensor original permaneceu inalterada.
(f) Agora, use o método sort(reverse=True) na lista leituras_ordenadas para
ordená-la em ordem decrescente. Imprima o resultado.
(g) Use o método reverse() (não sort(reverse=True)) na lista
leituras_ordenadas (que agora está em ordem decrescente) para inverter a
ordem atual dos seus elementos. Imprima a lista e observe o resultado.
(h) Use a função len() para imprimir quantos elementos existem na lista
leituras_sensor.
(i) Use a função sorted() para criar uma nova lista contendo os elementos de
leituras_sensor em ordem crescente, sem modificar a lista leituras_sensor
original. Imprima a nova lista ordenada e a lista leituras_sensor original
para confirmar.

5. E6.5: Compreensões de Listas (List Comprehensions) (Prático)

(a) Use uma compreensão de lista para criar uma lista chamada
cubos_dos_primeiros_dez que contenha os cubos dos números de 1 a 10
(inclusive, ou seja, 13 , 23 , . . . , 103 ). Imprima a lista.
(b) Dada a lista de palavras: frase_lista = ["A", "engenharia", "é", "a",
"arte", "de", "organizar", "e", "construir"]. Crie uma nova lista cha-
mada comprimento_das_palavras que contenha o comprimento de cada pala-
vra da frase_lista, usando uma compreensão de lista. Imprima
comprimento_das_palavras.
(c) Crie uma lista de números de 1 a 30 usando list(range(1, 31)). A partir
desta lista, use uma compreensão de lista para criar uma nova lista chamada
multiplos_de_3_ou_5 que contenha apenas os números que são múltiplos de
3 OU múltiplos de 5. Imprima a nova lista.
(d) Dada a lista de strings:
nomes_sujos = ["ana ", "bruno ", "CARLA", "david eduardo "]. Crie
uma nova lista chamada nomes_limpos_formatados onde cada nome:
• Teve os espaços em branco extras do início e do fim removidos.
• Está formatado com apenas a primeira letra de cada parte do nome em
maiúscula (ex: “Ana”, “Bruno”, “Carla”, “David Eduardo”). (Dica: strip()
e title()).
Use uma compreensão de lista para esta tarefa. Imprima a lista resultante.

6. E6.6: Trabalhando com Listas Aninhadas (Matrizes Simples) (Prático)


Considere a seguinte matriz representada como uma lista de listas:
1 matriz_A = [
2 [10 , 20 , 30] ,
3 [40 , 50 , 60] ,
4 [70 , 80 , 90]
458

5 ]

Código 21.31: Matriz para E6.6

(a) Acesse e imprima o elemento que está na segunda linha (índice 1) e na terceira
coluna (índice 2). (O valor esperado é 60).
(b) Modifique o elemento que está na primeira linha (índice 0) e primeira co-
luna (índice 0) para o valor 100. Imprima a matriz_A completa para ver a
modificação.
(c) Use laços for aninhados para imprimir todos os elementos da matriz_A, um
por um, cada um em uma nova linha.
(d) (Desafio) Use laços for aninhados para calcular a soma de todos os elementos
da diagonal principal da matriz_A (os elementos onde o índice da linha é igual
ao índice da coluna: A[0][0], A[1][1], A[2][2]). Imprima essa soma.

Projeto do Capítulo 6: Gerenciador Simples de Lista de Compras Interativo


Título do Projeto: Gerenciador Interativo de Lista de Compras.
Objetivos:

1. Praticar intensivamente a criação e manipulação de listas em Python.

2. Utilizar uma variedade de métodos de lista, como append(), remove(), index()


(opcionalmente), sort() (opcionalmente).

3. Implementar um menu de opções interativo para o usuário.

4. Empregar laços while para o fluxo principal do programa e laços for para iterar
sobre a lista.

5. Aplicar estruturas if-elif-else para processar as escolhas do menu.

6. Lidar com a entrada do usuário e fornecer feedback apropriado.

Descrição: Crie um programa Python que funcione como um gerenciador de lista


de compras. O programa deve permitir ao usuário adicionar itens à lista, remover itens,
visualizar a lista atual e sair do programa. A lista de compras será armazenada em uma
lista Python.
Funcionalidades Detalhadas:

1. Lista de Compras Principal: No início do seu script, crie uma lista vazia chamada,
por exemplo, lista_compras. Esta lista armazenará os itens (strings) que o usuário
adicionar.

2. Menu de Opções Interativo: O programa deve apresentar ao usuário um menu


de opções dentro de um laço principal (provavelmente um while True:). O menu
deve se parecer com algo assim:
459

--- Gerenciador de Lista de Compras ---


1. Adicionar item à lista
2. Remover item da lista
3. Visualizar lista de compras
4. Marcar item como comprado (Desafio Opcional)
5. Remover itens comprados (Desafio Opcional)
6. Sair do programa
---------------------------------------
Escolha uma opção (1-6):

O programa deve ler a escolha do usuário.


3. Implementando as Opções:
• Opção 1: Adicionar item
– Peça ao usuário para digitar o nome do item que deseja adicionar.
– Limpe quaisquer espaços em branco extras do início/fim do nome do item
(use strip()).
– Verificação (Opcional, mas bom): Antes de adicionar, verifique se o
item (após strip()) não é uma string vazia. Além disso, verifique se o
item (talvez comparando em minúsculas com lower()) já não existe na
lista para evitar duplicatas. Se já existir, informe ao usuário.
– Se o item for válido e (opcionalmente) não for duplicado, adicione-o à
lista_compras usando append().
– Imprima uma mensagem de confirmação (ex: “’[Nome do item]’ foi adicio-
nado à lista!”).
• Opção 2: Remover item
– Peça ao usuário para digitar o nome do item que deseja remover.
– Limpe a entrada do usuário com strip().
– Verifique se o item (talvez comparando com lower()) está presente na
lista_compras.
– Se estiver, use o método remove() para removê-lo. Imprima uma mensagem
de confirmação (ex: “’[Nome do item]’ foi removido da lista.”).
– Se o item não for encontrado, imprima uma mensagem informando isso (ex:
“Item ’[Nome do item]’ não encontrado na lista.”).
• Opção 3: Visualizar lista
– Se a lista_compras estiver vazia, imprima “Sua lista de compras está
vazia!”.
– Caso contrário, imprima os itens da lista. Para uma melhor visualização,
numere os itens. Você pode usar um laço for com enumerate() para isso:
--- Sua Lista de Compras ---
1. Leite
2. Pão
3. Ovos
----------------------------
460

• Opção 6: Sair
– Imprima uma mensagem de despedida (ex: “Obrigado por usar o Gerencia-
dor de Compras! Até logo.”).
– Use a instrução break para sair do laço principal do menu, encerrando o
programa.
• Opção Inválida:
– Se o usuário digitar um número que não corresponde a uma opção válida do
menu, imprima uma mensagem como “Opção inválida. Por favor, escolha
um número entre 1 e 6.”

4. Comentários: Adicione comentários ao seu código para explicar as diferentes partes


e a lógica.

Desafios Extras (Opcionais – para quem quiser ir além):

1. Opção 4: Marcar Item como Comprado

• Altere a estrutura da sua lista_compras. Em vez de armazenar apenas


strings, cada item da lista poderia ser uma pequena lista interna ou uma tupla,
contendo o nome do item e um status booleano indicando se foi comprado. Ex:
[["Leite", False], ["Pão", False]].
• Ao visualizar a lista (Opção 3), mostre o status de cada item. Ex: "[ ] Leite"
se não comprado, "[X] Pão" se comprado.
• Nesta nova Opção 4, peça ao usuário o número (ou nome) do item que ele
comprou e altere o status booleano correspondente para True.

2. Opção 5: Remover Itens Comprados

• Se você implementou o Desafio 1, adicione uma opção para remover da


lista_compras todos os itens que foram marcados como “comprados”. (Dica:
pode ser mais fácil criar uma nova lista apenas com os itens não comprados e
depois substituí-la pela original, ou iterar de trás para frente se for remover de
uma lista enquanto itera).

3. Ordenar a Lista: Antes de visualizar (Opção 3), use o método sort() para exibir
os itens da lista de compras em ordem alfabética.

4. Persistência de Dados (Antecipando o Capítulo 16): Para um desafio real-


mente avançado neste ponto, pesquise como você poderia salvar a lista de compras
em um arquivo de texto simples quando o usuário sai do programa, e como carregar
essa lista de volta do arquivo quando o programa é iniciado novamente.

Capítulo 7: Tuplas (Tuples) (Exercícios e Projeto)


Neste capítulo, exploramos as tuplas, as “primas” imutáveis das listas. Vimos como sua
imutabilidade as torna úteis para representar dados fixos, como chaves de dicionário e para
garantir a integridade de coleções que não devem ser alteradas. Aprendemos a criá-las,
acessá-las, a realizar operações comuns e o poderoso desempacotamento. Os exercícios e o
projeto a seguir ajudarão a solidificar esses conceitos.
461

Exercícios
1. E7.1: Criação e Características Básicas de Tuplas (Prático e Conceitual)

(a) Crie uma tupla chamada coordenadas_ponto_3d que represente as coordenadas


(x, y, z) de um ponto no espaço tridimensional. Use os valores inteiros 10, 20, e
30 para x, y, e z, respectivamente. Imprima a tupla criada e, em seguida, use a
função type() para imprimir o tipo do objeto coordenadas_ponto_3d.
(b) Tente criar uma tupla chamada cor_unica_tupla que contenha um único
elemento: a string "azul". Lembre-se da sintaxe especial necessária para tuplas
de um único elemento! Imprima a tupla e seu tipo para confirmar que você
criou uma tupla.
(c) Crie uma tupla vazia chamada configuracoes_padrao_vazias usando parên-
teses. Imprima-a e seu tipo.
(d) Crie uma lista Python chamada dias_da_semana_lista contendo os nomes dos
dias da semana (ex: ["Segunda", "Terça", ..., "Domingo"]). Em seguida,
use a função construtora tuple() para converter esta lista em uma tupla
chamada dias_da_semana_tupla. Imprima a tupla resultante.
(e) Com suas palavras, explique qual é a principal diferença fundamental entre uma
tupla e uma lista em Python, especialmente no que diz respeito à modificação
de seus conteúdos após a criação.

2. E7.2: Acesso e Fatiamento em Tuplas (Prático)


Dada a seguinte tupla, que representa um registro de dados de um sensor:
1 registro_sensor = ( " Sensor_Temp_A " , 28.5 ,
" Umidade_Relativa " , 65.2 , " 2025 -05 -26 T10 :30:00 Z " , True )
2 # ( Nome , ValorTemp , NomeUmidade , ValorUmidade , Timestamp ,
StatusAtivo )

Código 21.32: Tupla de dados de sensor para E7.2

Realize as seguintes operações e imprima o resultado de cada uma:

(a) Acesse e imprima o segundo elemento da tupla registro_sensor (que corres-


ponde ao valor da temperatura, 28.5).
(b) Acesse e imprima o último elemento da tupla (o status booleano) usando
indexação negativa.
(c) Use fatiamento para criar uma nova tupla chamada identificacao_sensor
que contenha apenas os dois primeiros elementos da registro_sensor (o nome
e o valor da temperatura). Imprima a nova tupla.
(d) Use fatiamento para criar uma nova tupla chamada leituras_principais que
contenha os elementos do terceiro ao quinto (inclusive), ou seja, "Umidade_Relativa",
65.2, e "2025-05-26T10:30:00Z". Imprima a nova tupla.
(e) Tente executar a seguinte linha de código:
registro_sensor[0] = "Sensor_Pressao_B". O que acontece? Explique o
resultado (ou o erro) em um comentário no seu script, relacionando-o com as
propriedades das tuplas. (Comente a linha que causa o erro para que o restante
do seu script possa rodar).
462

3. E7.3: Imutabilidade e Objetos Mutáveis Internos (Conceitual/Prático)


Este exercício explora uma nuance importante da imutabilidade das tuplas.

(a) Crie uma tupla chamada dados_pessoais_com_lista que contenha três ele-
mentos: seu nome (uma string), sua idade (um inteiro), e uma lista contendo
duas ou três das suas habilidades ou interesses (strings). Por exemplo: ("Seu
Nome Aqui", 30, ["Programação", "Música", "Esportes"]).
(b) Imprima a tupla dados_pessoais_com_lista original.
(c) Tente alterar diretamente sua idade na tupla (o segundo elemento). Por exem-
plo, tente algo como: dados_pessoais_com_lista[1] = 31. O que acontece?
Explique o resultado ou o erro em um comentário. (Comente a linha que causa
o erro).
(d) Agora, acesse a lista de habilidades que está dentro da tupla (o terceiro
elemento da tupla). Use o método append() das listas para adicionar uma nova
habilidade a esta lista interna.
(e) Imprima a tupla dados_pessoais_com_lista novamente. O que aconteceu
com a lista de habilidades? A tupla dados_pessoais_com_lista em si (o
objeto tupla) mudou sua identidade na memória? (Você pode verificar isso
comparando o resultado de id(dados_pessoais_com_lista) antes e depois
da modificação na lista interna, se quiser ser mais técnico).
(f) Explique o comportamento observado, relacionando-o com o fato de que a tupla
é imutável (suas referências diretas a objetos não mudam), mas os objetos que
ela referencia podem ser mutáveis (como a lista interna).

4. E7.4: Operações e Métodos Comuns com Tuplas (Prático)


Crie as seguintes tuplas: tupla1 = (10, 20, 30)
tupla2 = (40, 50, 20, 60)

(a) Use o operador de concatenação + para criar uma nova tupla chamada
tupla_combinada que seja a junção de tupla1 e tupla2. Imprima tupla_combinada.
(b) Crie uma nova tupla chamada tupla_repetida que seja a tupla1 repetida 2
vezes usando o operador de repetição *. Imprima tupla_repetida.
(c) Verifique se o número 50 está presente na tupla_combinada usando o operador
in. Imprima o resultado (True/False).
(d) Use um laço for para iterar sobre todos os elementos da tupla_combinada e
imprimir cada elemento em uma nova linha.
(e) Dada a tupla
notas_fiscais = (1001, 1005, 1001, 1020, 1001, 1005, 1030), use o mé-
todo .count() para contar e imprimir quantas vezes o número 1001 aparece.
(f) Na mesma tupla notas_fiscais, use o método .index() para encontrar e
imprimir a posição (índice) da primeira ocorrência do número 1020.

5. E7.5: Desempacotamento de Tuplas (Tuple Unpacking) (Prático)

(a) Suponha que uma função (que você não precisa definir, apenas use o resultado)
retorna os dados de um retângulo como uma tupla: dimensoes_retangulo =
(15.0, 7.5, "metros") (base, altura, unidade). Use o desempacotamento de
463

tuplas para atribuir esses três valores a três variáveis separadas: base, altura,
e unidade. Imprima os valores de cada uma dessas três novas variáveis.
(b) Considere a seguinte lista de tuplas, onde cada tupla representa um aluno e
sua nota final:
1 alunos_e_notas = [( " Mariana Silva " , 8.8) , ( " Jo ã o
Pereira " , 9.2) ,
2 ( " Lucas Andrade " , 7.5) , ( " Beatriz Costa " , 6.9) ]

Código 21.33: Lista de alunos e notas para E7.5b

Use um laço for e o desempacotamento de tuplas (diretamente na declaração


do laço, ex: for nome, nota in ...) para imprimir o nome e a nota de cada
aluno em uma frase formatada, como: “O(A) aluno(a) Mariana Silva tirou nota
8.8.”
(c) Você tem duas variáveis, variavel_x = "Python" e variavel_y = 2025. Use
a técnica de desempacotamento de tuplas para trocar os valores entre variavel_x
e variavel_y em uma única linha de atribuição (o "Pythonic swap"). Imprima
os valores de variavel_x e variavel_y antes e depois da troca para verificar.
(d) Dada a tupla dados_sequenciais = (10, 20, 30, 40, 50, 60, 70). Use
o desempacotamento estendido com o operador asterisco (*) para atribuir o
primeiro elemento a uma variável chamada primeiro_valor, os dois últimos
elementos a variáveis chamadas penultimo_valor e ultimo_valor respecti-
vamente, e todos os elementos que sobrarem no meio a uma lista chamada
valores_do_meio. Imprima todas as quatro variáveis resultantes
(primeiro_valor, valores_do_meio, penultimo_valor, ultimo_valor).

Projeto do Capítulo 7: Gerenciador de Registros Fixos de Configuração de


Equipamentos
Título do Projeto: Sistema de Gerenciamento de Configurações Imutáveis para
Equipamentos de Engenharia.
Objetivos:

1. Praticar o uso de tuplas para armazenar conjuntos de dados que representam registros
fixos e imutáveis.

2. Utilizar listas para armazenar uma coleção dinâmica dessas tuplas (registros).

3. Implementar funcionalidades para adicionar novos registros (que, uma vez criados
como tuplas, são imutáveis), visualizar todos os registros e buscar registros específicos.

4. Praticar intensivamente o desempacotamento de tuplas para acessar e exibir infor-


mações de forma organizada e legível.

5. Reforçar a compreensão e as implicações da imutabilidade das tuplas em um contexto


prático.

Descrição: Crie um programa Python que gerencie um conjunto de registros de


configuração para diferentes equipamentos de um laboratório de engenharia. Cada registro
de configuração para um equipamento será uma tupla, pois supomos que, uma vez definido,
464

um registro de configuração de equipamento (ex: ID, tipo, data de calibração, responsável)


é fixo. O programa armazenará uma lista desses registros (que são tuplas).
Funcionalidades a Implementar:

1. Armazenamento Principal: Use uma lista Python (ex: todos_os_equipamentos)


para armazenar os registros. Cada item nesta lista será uma tupla.

2. Formato do Registro (Tupla): Cada tupla de equipamento deve conter, no


mínimo, as seguintes informações, nesta ordem:

• ID do Equipamento (string, ex: “EQP-001”)


• Tipo de Equipamento (string, ex: “Osciloscópio”)
• Data da Última Calibração (string, ex: “15/10/2024”)
• Nome do Responsável (string, ex: “Dr. Silva”)
• Status (string, ex: “Operacional”, “Em Manutenção”, “Desativado”)

Exemplo de uma tupla de registro:


("EQP-001", "Osciloscópio", "15/10/2024", "Dr. Silva", "Operacional")

3. Menu de Opções Interativo: O programa deve apresentar ao usuário um menu


de opções dentro de um laço principal. O menu deve oferecer, no mínimo:

--- Gerenciador de Configurações de Equipamentos ---


1. Adicionar Novo Equipamento
2. Visualizar Todos os Equipamentos
3. Buscar Equipamento por ID
4. Atualizar Status de um Equipamento (Desafio)
5. Sair
----------------------------------------------------
Escolha uma opção:

4. Opção 1: Adicionar Novo Equipamento

• Peça ao usuário para fornecer todas as informações necessárias para um novo


equipamento (ID, Tipo, Data de Calibração, Responsável, Status).
• Validação (Simples): Verifique se o ID do Equipamento já não existe na lista
de todos_os_equipamentos para evitar duplicatas de ID. Se já existir, informe
ao usuário e não adicione.
• Crie uma nova tupla com essas informações.
• Adicione esta nova tupla à lista todos_os_equipamentos.
• Imprima uma mensagem de confirmação.

5. Opção 2: Visualizar Todos os Equipamentos

• Se a lista todos_os_equipamentos estiver vazia, informe ao usuário.


• Caso contrário, itere sobre a lista. Para cada registro (tupla) na lista:
465

– Use o desempacotamento de tuplas para extrair os valores individuais (ID,


Tipo, Data, etc.).
– Imprima as informações de cada equipamento de forma bem formatada e
legível. Por exemplo:
---------------------------------
ID do Equipamento..: EQP-001
Tipo...............: Osciloscópio Digital
Última Calibração..: 2024-10-15
Responsável........: Dr. Silva
Status.............: Operacional

6. Opção 3: Buscar Equipamento por ID

• Peça ao usuário o ID do equipamento que ele deseja buscar.


• Itere pela lista todos_os_equipamentos. Para cada tupla de registro, verifique
se o primeiro elemento (o ID) corresponde ao ID buscado (considere busca
insensível a maiúsculas/minúsculas para o ID, se desejar).
• Se encontrar o equipamento, imprima todas as suas informações de forma
formatada (como na visualização).
• Se não encontrar o equipamento após percorrer toda a lista, informe ao usuário.

7. Opção 5: Sair

• Imprima uma mensagem de despedida e encerre o programa.

8. Opção Inválida: Se o usuário digitar uma opção que não está no menu, imprima
uma mensagem apropriada.

Desafio Extra (Opcional): Opção 4: Atualizar Status de um Equipamento

• Implemente a opção 4 do menu.

• Peça ao usuário o ID do equipamento cujo status ele deseja atualizar.

• Se o equipamento for encontrado, peça o novo status (ex: “Em Manutenção”).

• Lembre-se que tuplas são imutáveis! Você não pode alterar diretamente o status na
tupla original.

• Como fazer? Você precisará:

1. Encontrar o índice da tupla do equipamento na lista todos_os_equipamentos.


2. Criar uma nova tupla com todas as informações do equipamento original, mas
com o status atualizado.
3. Substituir a tupla antiga na lista pela nova tupla (usando o índice que você
encontrou).

• Imprima uma mensagem de confirmação ou erro.


466

Capítulo 8: Dicionários (Dictionaries) (Exercícios e Projeto)


Neste capítulo, exploramos os dicionários, estruturas de dados incrivelmente versáteis
do Python que armazenam informações em pares chave-valor. Vimos como criá-los, acessá-
los, modificá-los, iterar sobre eles, usar seus métodos e até mesmo como criar dicionários
aninhados e compreensões de dicionários. Os exercícios e o projeto a seguir têm como
objetivo solidificar seu domínio sobre esta estrutura de dados fundamental.

Exercícios
1. E8.1: Criação e Acesso Básico a Dicionários (Prático)

(a) Crie um dicionário chamado informacoes_aluno para armazenar os seguintes


dados sobre um aluno fictício:
• "nome": (uma string com o nome do aluno)
• "matricula": (um número inteiro ou string para a matrícula)
• "curso": (uma string com o nome do curso)
• "notas": (uma lista de três notas, ex: [8.5, 7.0, 9.3])
Preencha com dados fictícios à sua escolha e, em seguida, imprima o dicionário
completo.
(b) A partir do dicionário informacoes_aluno criado no item anterior, acesse e
imprima apenas o nome do aluno.
(c) Acesse e imprima a segunda nota da lista de notas do aluno (lembre-se que a
lista está dentro do dicionário).
(d) Tente acessar uma chave que você sabe que não existe no dicionário
informacoes_aluno (por exemplo, "data_nascimento"). O que acontece se
você tentar acessar diretamente com informacoes_aluno["data_nascimento"]?
(Você pode comentar essa linha para o script não parar com erro).
(e) Agora, use o método get() para tentar acessar a chave "data_nascimento".
Forneça um valor padrão (como a string "Não informada") caso a chave não
seja encontrada. Imprima o resultado obtido com get().

2. E8.2: Modificando e Adicionando Itens em Dicionários (Prático)


Comece com o seguinte dicionário que representa um pequeno estoque de produtos:
1 estoque_loja = { " Caneta Azul " : 150 , " L á pis HB " : 200 ,
" Borracha M é dia " : 120}

Código 21.34: Dicionário de estoque inicial para E8.2

(a) Imprima o dicionário estoque_loja para ver seu estado inicial.


(b) Foram vendidas 30 unidades de "Lápis HB". Atualize o valor da quantidade
em estoque para "Lápis HB"no dicionário. Imprima o dicionário atualizado.
(c) Chegou um novo produto na loja: "Apontador", com 75 unidades em estoque.
Adicione este novo par chave-valor ao dicionário estoque_loja. Imprima o
dicionário novamente.
467

(d) Use o método update() para realizar as seguintes atualizações/adições de uma


só vez: diminuir o estoque de "Caneta Azul"para 100 unidades, adicionar
"Caderno Universitário"com 60 unidades, e atualizar a "Borracha Média"para
110 unidades. Imprima o dicionário final.

3. E8.3: Removendo Itens de Dicionários (Prático)


Use o dicionário estoque_loja resultante ao final do Exercício E8.2.

(a) O item "Borracha Média"foi descontinuado e precisa ser removido do estoque.


Use a instrução del para remover este par chave-valor do dicionário. Imprima
o dicionário atualizado.
(b) Você quer processar o estoque do "Caderno Universitário"e depois removê-lo.
Use o método pop() para remover o item "Caderno Universitário"do dicionário
e armazene o valor do seu estoque (a quantidade) em uma variável. Imprima a
quantidade removida e o dicionário após a remoção.
(c) Tente usar o método pop() para remover uma chave que você sabe que não
existe (ex: "Régua"). O que acontece? Agora, tente novamente, mas desta vez
fornecendo um valor padrão (ex: 0 ou "Item não encontrado") para o caso
de a chave não existir. Imprima o valor retornado por pop() neste segundo
caso.
(d) Use o método popitem() para remover e imprimir o último par chave-valor que
foi inserido no dicionário (em Python 3.7+, popitem() remove no estilo LIFO -
Last In, First Out). Imprima o par removido (que é uma tupla chave-valor) e o
dicionário atualizado. Chame popitem() novamente e veja o que acontece.
(e) Finalmente, limpe (remova todos os itens) do dicionário estoque_loja usando
o método clear(). Imprima o dicionário para confirmar que ele está vazio.

4. E8.4: Iterando sobre Dicionários (Prático)


Crie o seguinte dicionário representando um pequeno glossário de termos de enge-
nharia:
1 g l o s s a ri o _ e n g e n h a r i a = {
2 " Resili ê ncia " : " Capacidade de um material absorver
energia e deformar plasticamente antes de
fraturar . " ,
3 " Tens ã o " : " For ç a interna por unidade de á rea que um
material suporta . " ,
4 " Deforma ç ã o " : " Mudan ç a na forma ou tamanho de um
objeto devido a uma for ç a aplicada . "
5 }

Código 21.35: Glossário para E8.4

(a) Use um laço for para iterar diretamente sobre o dicionário


glossario_engenharia (lembre-se que isso itera sobre as chaves). Dentro do
laço, imprima a chave (o termo) e também acesse e imprima o valor correspon-
dente (a definição) do dicionário.
(b) Use o método .values() e um laço for para imprimir apenas todas as definições
do glossário, uma por linha.
468

(c) Use o método .items() e um laço for com desempacotamento de tuplas (ex:
for termo, definicao in ...) para imprimir o termo e sua definição em
uma frase formatada, como: “O termo ’[TERMO]’ significa: [DEFINIÇÃO]”.

5. E8.5: Métodos Úteis – keys(), values(), items(), e o operador in (Prático)


Utilize o dicionário glossario_engenharia do exercício anterior.

(a) Obtenha uma “visão” de todas as chaves do dicionário usando o método


glossario_engenharia.keys(). Imprima essa visão. Em seguida, converta
essa visão para uma lista e imprima a lista de chaves.
(b) Faça o mesmo para os valores, usando glossario_engenharia.values(). Im-
prima a visão e a lista de valores.
(c) Faça o mesmo para os itens (pares chave-valor), usando
glossario_engenharia.items(). Imprima a visão e a lista de tuplas (chave,
valor).
(d) Peça ao usuário para digitar um termo de engenharia. Use o operador in
para verificar de forma eficiente se o termo digitado existe como uma chave
no dicionário glossario_engenharia. Imprima uma mensagem apropriada
indicando se o termo foi encontrado ou não no glossário.

6. E8.6: Compreensão de Dicionários (Dictionary Comprehensions) (Prático)

(a) Crie uma lista de números: numeros_originais = [1, 2, 3, 4, 5, 6]. Use


uma compreensão de dicionário para criar um novo dicionário chamado
numeros_e_seus_cubos onde as chaves são os números da lista e os valores
são os respectivos cubos (numero3 ) desses números. Imprima o dicionário
numeros_e_seus_cubos.
(b) Dada a lista de palavras: lista_de_palavras = ["circuito", "tensão",
"corrente", "resistência"]. Use uma compreensão de dicionário para criar
um dicionário chamado palavras_e_comprimentos onde as chaves são as pa-
lavras da lista e os valores são o comprimento (número de caracteres) de cada
palavra. Imprima o dicionário resultante.
(c) Dado o dicionário de preços de componentes: precos_componentes = "resistor":
0.10, "capacitor": 0.25, "led": 0.15, "transistor": 0.40. Use uma
compreensão de dicionário para criar um novo dicionário chamado
precos_com_acrescimo onde as chaves são os mesmos componentes, mas os
valores são os preços originais aumentados em 20% (ou seja, multiplicados por
1.2). Imprima o novo dicionário, formatando os preços para terem duas casas
decimais na impressão se possível (a formatação é na hora de imprimir, não
necessariamente na compreensão).

7. E8.7: Dicionários Aninhados – Modelando Dados Estruturados (Prático)


Crie um dicionário principal chamado catalogo_sensores. As chaves deste dicioná-
rio serão códigos de sensores (ex: "SENS-TEMP-001", "SENS-UMID-007"). Os valores
associados a cada chave serão outros dicionários, contendo as seguintes informações
sobre cada sensor:

• "tipo": (string, ex: “Temperatura”, “Umidade”, “Pressão”)


469

• "unidade": (string, ex: “Celsius”, “%RH”, “hPa”)


• "faixa_operacao": (uma tupla de dois números, ex: (-40, 85) para tempe-
ratura)
• "ativo": (booleano, True ou False)

Adicione pelo menos três sensores diferentes ao seu catálogo.

(a) Imprima o dicionário catalogo_sensores completo.


(b) Acesse e imprima o tipo do sensor com o código “SENS-TEMP-001” (ou o
primeiro que você adicionou).
(c) Acesse e imprima a faixa de operação (a tupla) do segundo sensor que você
adicionou.
(d) Modifique o status do primeiro sensor para False (inativo). Imprima o dicioná-
rio interno daquele sensor para ver a alteração.
(e) Use laços aninhados (um for para iterar sobre os itens do catalogo_sensores,
e outro for interno para iterar sobre os itens de cada dicionário de sensor)
para imprimir todas as informações de todos os sensores de forma organizada e
legível. Exemplo de uma entrada:
Sensor ID: SENS-TEMP-001
Tipo: Temperatura
Unidade: Celsius
Faixa_operacao: (-40, 85)
Ativo: False
--------------------

Projeto do Capítulo 8: Mini Sistema de Gerenciamento de Estoque de Labora-


tório de Eletrônica
Título do Projeto: Gerenciador Simples de Estoque de Componentes Eletrônicos.
Objetivos:

1. Utilizar dicionários como a estrutura de dados principal para armazenar e gerenciar


informações detalhadas sobre itens de um estoque.

2. Praticar a adição, remoção, modificação e consulta de itens em dicionários, incluindo


o uso de dicionários aninhados.

3. Implementar um menu de opções interativo para o usuário, utilizando laços while e


estruturas if-elif-else.

4. Aplicar a iteração sobre dicionários (chaves, valores, itens) para exibir informações
de forma organizada.

5. Reforçar o tratamento de entrada do usuário e a apresentação de feedback claro.

Descrição: Crie um programa Python que simule um sistema básico de gerenciamento


de estoque para componentes eletrônicos de um laboratório. Cada componente no estoque
será identificado por um código de referência único (que servirá como chave no dicionário
470

principal). As informações detalhadas associadas a cada componente (como nome des-


critivo, quantidade em estoque, tipo de encapsulamento, localização na prateleira) serão
armazenadas em um dicionário aninhado.
Estrutura de Dados Principal Sugerida:

• Um dicionário principal chamado estoque_componentes.

• As chaves deste dicionário serão os códigos dos componentes (strings, ex: "RES-10K-01",
"CAP-100NF-02", "LED-VM-003").

• Os valores associados a cada chave serão outros dicionários contendo informações


como:

– "nome_descritivo": (string, ex: “Resistor 10k Ohm 1/4W”, “Capacitor Cerâ-


mico 100nF”, “LED Vermelho Difuso 5mm”)
– "quantidade": (inteiro, a quantidade atual em estoque)
– "encapsulamento": (string, ex: “PTH Axial”, “SMD 0805”, “PTH 5mm”)
– "prateleira": (string, ex: “Gaveta A-01”, “Caixa C-05”, “Prateleira B-02”)
– "estoque_minimo": (inteiro, opcional, para o desafio extra)

Exemplo de um item:
1 # e st o qu e _ co m po n e nt e s = {
2 # " RES -10 K -01": {
3 # " nome_descritivo ": " Resistor 10 k Ohm 1/4 W " ,
4 # " quantidade ": 250 ,
5 # " encapsulamento ": " PTH Axial " ,
6 # " prateleira ": " Gaveta R -01" ,
7 # " estoque_minimo ": 50
8 # },
9 # # ... outros componentes ...
10 # }

Código 21.36: Exemplo de item no estoque de componentes

Funcionalidades a Implementar:

1. Menu de Opções Interativo: Crie um menu principal que ofereça, no mínimo:

### Gerenciador de Estoque de Componentes ###


1. Adicionar Novo Componente
2. Consultar Componente por Código
3. Atualizar Quantidade em Estoque
4. Listar Todos os Componentes
5. Remover Componente
6. (Desafio) Listar Componentes com Estoque Baixo
7. Sair
Escolha uma opção:

2. Opção 1: Adicionar Novo Componente


471

• Peça ao usuário o código do novo componente. Verifique se este código já não


existe no estoque para evitar duplicatas. Se já existir, informe e não adicione.
• Se o código for novo, peça as demais informações (nome descritivo, quantidade
inicial, encapsulamento, prateleira, e opcionalmente, estoque mínimo).
• Crie o dicionário interno com essas informações e adicione-o ao dicionário
estoque_componentes usando o código do componente como chave.
• Confirme a adição.

3. Opção 2: Consultar Componente por Código

• Peça ao usuário o código do componente a ser consultado.


• Se o código existir no estoque, exiba todas as informações do componente de
forma clara e formatada.
• Se não existir, informe ao usuário.

4. Opção 3: Atualizar Quantidade em Estoque

• Peça o código do componente.


• Se existir, mostre a quantidade atual e peça a nova quantidade total em estoque.
Valide se a nova quantidade é um número não negativo.
• Atualize o valor da chave "quantidade" no dicionário interno do componente.
• (Alternativa: Pergunte se é uma entrada ou saída de estoque e a quantidade, e
então some ou subtraia da quantidade atual, validando se o estoque não fica
negativo em caso de saída).

5. Opção 4: Listar Todos os Componentes

• Se o estoque estiver vazio, informe.


• Caso contrário, itere sobre o dicionário estoque_componentes.items() e, para
cada componente, imprima seu código, nome descritivo e quantidade atual de
forma organizada.

6. Opção 5: Remover Componente

• Peça o código do componente a ser removido.


• Se existir, remova-o do estoque (usando pop() ou del). Confirme a remoção.
• Se não existir, informe ao usuário.

7. Opção 7: Sair

• Imprima uma mensagem de despedida e encerre o programa.

Desafios Extras (Opcionais):

1. Opção 6: Listar Componentes com Estoque Baixo:

• Implemente a funcionalidade onde o usuário pode definir um “estoque mínimo”


ao adicionar/atualizar um componente.
472

• Crie uma opção no menu para listar todos os componentes cuja quantidade atual
está abaixo (ou igual) ao seu respectivo “estoque_minimo”. Se um componente
não tiver um estoque mínimo definido, ele pode ser ignorado ou tratado de uma
forma específica.
2. Interface de Usuário Mais Amigável: Pense em como tornar as mensagens e a
interação com o usuário mais claras e intuitivas.
3. Validação de Dados Mais Robusta: Ao adicionar ou atualizar, implemente
validações mais rigorosas para os tipos de dados e formatos esperados (ex: quantidade
deve ser numérica e positiva, data de validade em formato DD/MM/AAAA).
4. Persistência de Dados (Antecipando Capítulos Futuros): Para um desafio
maior, pesquise sobre como salvar o dicionário estoque_componentes em um arquivo
(ex: usando o módulo json) quando o programa sai, e como carregar esses dados do
arquivo quando o programa inicia.

Capítulo 9: Conjuntos (Sets) (Exercícios e Projeto)


Neste capítulo, exploramos os conjuntos (set) e os conjuntos imutáveis (frozenset),
estruturas de dados que armazenam coleções não ordenadas de itens únicos. Vimos como
criá-los, realizar operações básicas, adicionar e remover elementos (para sets), e aplicar as
poderosas operações de teoria dos conjuntos. Os exercícios e o projeto a seguir ajudarão a
solidificar seu entendimento sobre quando e como usar essas estruturas de dados únicas.

Exercícios
1. E9.1: Criação e Unicidade de Conjuntos (Prático)
(a) Crie um conjunto chamado meus_numeros_sorte contendo os seguintes números
inteiros: 1, 7, 13, 7, 22, 13, 8. Imprima o conjunto para verificar como as
duplicatas são tratadas.
(b) Crie um conjunto chamado letras_distintas a partir da string "engenharia
de software". Imprima o conjunto para ver quais letras únicas (incluindo
espaços, se houver na string original) ele contém. (Lembre-se que a ordem não
é garantida).
(c) Tente criar um conjunto vazio usando a sintaxe conjunto_errado = {}. Ve-
rifique o tipo de conjunto_errado usando type(conjunto_errado). O que
aconteceu?
(d) Agora, crie um conjunto vazio da maneira correta, usando a função set(), e
chame-o de conjunto_vazio_correto. Verifique seu tipo e imprima o conjunto.
Explique a diferença entre os resultados de c) e d).
(e) Crie uma lista lista_de_frutas = ["maçã", "banana", "laranja", "maçã",
"uva", "banana"]. Converta esta lista em um conjunto chamado frutas_unicas
para remover as duplicatas. Imprima o conjunto frutas_unicas.
2. E9.2: Operações Básicas e Modificações em Conjuntos Mutáveis (set)
(Prático)
Comece com o seguinte conjunto de habilidades de programação:
habilidades_programador = {"Python", "Java", "C++", "SQL"}
473

(a) Imprima o conjunto habilidades_programador inicial.


(b) Adicione a habilidade "JavaScript" ao conjunto usando o método add().
Imprima o conjunto atualizado.
(c) Tente adicionar a habilidade "Python" novamente ao conjunto. O que acontece
com o conjunto? Imprima-o para verificar.
(d) Crie uma lista de novas habilidades que você gostaria de aprender, por exemplo:
aprender_agora = ["Git", "Docker", "Java"]. Use o método update()
para adicionar todos os itens desta lista ao conjunto habilidades_programador.
Imprima o conjunto resultante (observe como "Java", que já existia, não é
duplicado, e os novos itens são adicionados).
(e) Suponha que você decidiu não focar mais em "C++"por enquanto. Remova
"C++" do conjunto habilidades_programador usando o método remove().
Imprima o conjunto.
(f) Agora, tente remover a habilidade "Ruby" (que provavelmente não está no seu
conjunto) usando o método remove(). O que acontece? (Para que seu script
não pare com um erro, você pode comentar esta linha após observar o resultado,
ou envolvê-la em um bloco try-except que veremos no Capítulo 17, ou ainda,
verificar antes com if "Ruby"in habilidades_programador:).
(g) Em vez de remove(), use o método discard() para tentar remover a habilidade
"Ruby" novamente. O que acontece desta vez se "Ruby"não estiver no conjunto?
Imprima o conjunto para confirmar.
(h) Use o método pop() uma vez no seu conjunto habilidades_programador.
Imprima o item que foi removido e também o conjunto restante. Lembre-se que
pop() remove um item arbitrário.
(i) Finalmente, use o método clear() para remover todos os itens do conjunto
habilidades_programador. Imprima o conjunto para confirmar que ele está
vazio.

3. E9.3: Operações de Teoria dos Conjuntos (Prático)


Crie os dois seguintes conjuntos de alunos matriculados em diferentes clubes de uma
universidade:
1 clube _de_robo tica = { " Ana " , " Bruno " , " Carlos " , " Diana " ,
" Eduardo " }
2 c l u b e _ de _ p r o g r a m a c a o = { " Carlos " , " Fernanda " , " Gustavo " ,
" Ana " , " Henrique " }

Código 21.37: Conjuntos de alunos em clubes para E9.3

Realize as seguintes operações e imprima o resultado de cada uma, com uma


mensagem explicativa:

(a) A união dos dois clubes: todos os alunos únicos que participam de robótica
OU de programação (ou de ambos). Demonstre usando tanto o operador |
quanto o método .union().
(b) A interseção dos dois clubes: apenas os alunos que participam de AM-
BOS os clubes. Demonstre usando tanto o operador & quanto o método
.intersection().
474

(c) A diferença clube_de_robotica - clube_de_programacao: alunos que es-


tão no clube de robótica MAS NÃO estão no de programação. Demonstre
usando tanto o operador - quanto o método .difference().
(d) A diferença simétrica entre os dois clubes: alunos que estão em APENAS UM
dos clubes (ou seja, em robótica mas não em programação, OU em programação
mas não em robótica). Demonstre usando tanto o operador ˆ quanto o método
.symmetric_difference().
(e) Crie uma cópia do clube_de_robotica chamada robotica_atualizado. Use
o método de atualização in-place .difference_update() (ou o operador -=)
em robotica_atualizado para remover todos os membros que também estão
no clube_de_programacao. Imprima robotica_atualizado após a operação.

4. E9.4: Métodos de Comparação de Conjuntos (Prático)


Crie os seguintes conjuntos:
1 c o n j u n t o _ u n i v e r s a l _ n u m e r o s = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ,
10}
2 conjunto_pares = {2 , 4 , 6 , 8 , 10}
3 c o n j u n to _ p e q u e n o _ p a r = {2 , 4}
4 conjunto_impares = {1 , 3 , 5 , 7 , 9}
5 conjunto_primos = {2 , 3 , 5 , 7}

Código 21.38: Conjuntos para E9.4

Verifique e imprima o resultado booleano (True ou False) das seguintes relações:

(a) Se conjunto_pequeno_par é um subconjunto de conjunto_pares


(use issubset() e o operador <=).
(b) Se conjunto_pequeno_par é um subconjunto próprio de conjunto_pares (use
o operador <).
(c) Se conjunto_universal_numeros é um superconjunto de conjunto_primos
(use issuperset() e o operador >=).
(d) Se conjunto_pares é um superconjunto próprio de
conjunto_universal_numeros (use o operador >).
(e) Se conjunto_pares e conjunto_impares são disjuntos (não têm elementos em
comum) usando isdisjoint().
(f) Se conjunto_pares e conjunto_primos são disjuntos. (Qual elemento eles
têm em comum?)

5. E9.5: frozenset e a Propriedade de Ser Hasheável (Prático/Conceitual)

(a) Crie um frozenset chamado fs_elementos_fixos contendo os números 10,


20 e 30. Imprima este frozenset e seu tipo.
(b) Tente adicionar o número 40 ao fs_elementos_fixos usando um método add().
O que acontece? Explique o resultado (ou erro) em um comentário. (Comente
a linha que causa o erro).
(c) Crie um set normal (mutável) chamado conjunto_principal. Tente adicionar
o fs_elementos_fixos como um elemento dentro do conjunto_principal.
Isso funciona? Imprima o conjunto_principal.
475

(d) Agora, crie outro set normal chamado outro_set_mutavel = {100, 200}.
Tente adicionar outro_set_mutavel como um elemento dentro do
conjunto_principal. Isso funciona? Imprima o conjunto_principal nova-
mente.
(e) Explique a diferença de comportamento observada nos itens c) e d) em termos
de imutabilidade e da propriedade de ser “hasheável” (necessária para elementos
de conjuntos).
(f) Crie um dicionário Python. Tente usar fs_elementos_fixos como uma chave
nesse dicionário (atribuindo qualquer valor a essa chave). Isso funciona?
(g) Agora, tente usar outro_set_mutavel como uma chave no mesmo dicionário.
Isso funciona? Explique por que, relacionando com a exigência de que chaves
de dicionário sejam hasheáveis.

Projeto do Capítulo 9: Analisador de Habilidades Comuns e Únicas em Equipes


de Projeto
Título do Projeto: Analisador de Habilidades Comuns e Únicas em Equipes de
Projeto.
Objetivos:

1. Praticar intensivamente o uso de conjuntos (set) para representar coleções de itens


únicos (habilidades).

2. Aplicar as principais operações de teoria dos conjuntos (união, interseção, diferença,


diferença simétrica) para analisar e comparar os conjuntos de habilidades.

3. Coletar dados do usuário (nomes de habilidades) para popular os conjuntos.

4. Apresentar os resultados da análise de forma clara e organizada para o usuário.

5. Reforçar a compreensão de como os conjuntos podem ser úteis para tarefas de


comparação e análise de grupos.

Descrição: Crie um programa Python que permita ao usuário inserir as habilidades


dos membros de duas equipes de projeto (Equipe Alfa e Equipe Beta). Após a entrada
dos dados, o programa deve analisar esses conjuntos de habilidades e exibir um relatório
mostrando:

• Todas as habilidades únicas presentes no conjunto total de membros de ambas as


equipes.

• As habilidades que são comuns (compartilhadas) pelos membros de ambas as equipes.

• As habilidades que são exclusivas da Equipe Alfa (presentes na Alfa, mas não na
Beta).

• As habilidades que são exclusivas da Equipe Beta (presentes na Beta, mas não na
Alfa).

• Todas as habilidades que não são compartilhadas entre as equipes (ou seja, são
exclusivas de uma ou de outra).
476

Funcionalidades Detalhadas:
1. Entrada de Habilidades para Cada Equipe:

• Para a Equipe Alfa:


– Peça ao usuário para digitar as habilidades dos membros da Equipe Alfa,
uma por vez. O usuário pode digitar, por exemplo, “Python”, “Gestão de
Projetos”, “Comunicação”, etc.
– Permita que o usuário digite “fim” (ou uma string vazia) quando terminar
de listar as habilidades para a Equipe Alfa.
– Armazene essas habilidades em um conjunto chamado habilidades_alfa.
– Dica de Processamento: Ao ler cada habilidade, converta-a para le-
tras minúsculas e remova espaços extras do início/fim (usando lower() e
strip()) antes de adicioná-la ao conjunto. Isso garante que “python” e “
Python ” sejam tratados como a mesma habilidade.
• Repita o mesmo processo para a Equipe Beta, armazenando as habilidades em
um conjunto chamado habilidades_beta.

2. Cálculo e Análise com Operações de Conjunto: Após coletar as habilidades


para ambas as equipes:

• Calcule a união dos conjuntos habilidades_alfa e habilidades_beta. Ar-


mazene em todas_habilidades_unicas.
• Calcule a interseção dos dois conjuntos. Armazene em habilidades_comuns.
• Calcule a diferença habilidades_alfa - habilidades_beta. Armazene em
habilidades_so_alfa.
• Calcule a diferença habilidades_beta - habilidades_alfa. Armazene em
habilidades_so_beta.
• Calcule a diferença simétrica entre os dois conjuntos.
Armazene em habilidades_nao_compartilhadas.

3. Exibição dos Resultados Formatados: Imprima os resultados da análise de


forma clara e bem rotulada. Por exemplo:

--- Análise de Habilidades das Equipes de Projeto ---

Habilidades da Equipe Alfa:


{’python’, ’gestão de projetos’, ’comunicação’}

Habilidades da Equipe Beta:


{’python’, ’liderança’, ’sql’, ’comunicação’}

== Relatório de Habilidades ==
1. Todas as Habilidades Únicas Presentes:
{python, gestão de projetos, comunicação, liderança, sql}
2. Habilidades Comuns a Ambas as Equipes:
{python, comunicação}
3. Habilidades Exclusivas da Equipe Alfa:
477

{gestão de projetos}
4. Habilidades Exclusivas da Equipe Beta:
{liderança, sql}
5. Habilidades Não Compartilhadas:
{gestão de projetos, liderança, sql}
-----------------------------------------------------

(Lembre-se que a ordem dos itens impressos de um conjunto pode variar).

Desafios Extras (Opcionais):

1. Múltiplas Equipes: Modifique o programa para que o usuário possa primeiro


definir quantas equipes deseja analisar (ex: 2, 3 ou mais). Em seguida, colete as
habilidades para cada uma dessas N equipes. Depois, calcule e exiba:

• Todas as habilidades únicas presentes em todas as N equipes combinadas.


• As habilidades que são comuns a todas as N equipes (a interseção de todos os
conjuntos de habilidades das equipes).

2. Análise de Cobertura de Habilidades:

• Defina um conjunto de “habilidades_essenciais_projeto” (ex: {"python",


"git", "documentação"}).
• Para cada equipe (Alfa e Beta, ou para as N equipes do desafio anterior),
verifique se o conjunto de habilidades da equipe cobre todas as habilidades
essenciais (ou seja, se habilidades_essenciais_projeto é um subconjunto
das habilidades da equipe). Imprima quais equipes atendem a esse critério.

3. Interface Mais Amigável: Use um laço while True: para o menu principal,
permitindo que o usuário realize múltiplas análises (definindo novas equipes) antes
de escolher sair explicitamente.

Parte III: Funções e Modularização


Capítulo 10: Definindo e Usando Funções (Exercícios e Projeto)
Neste capítulo, exploramos a fundo o conceito de funções em Python: como defini-las
com def, como chamá-las, como passar informações através de parâmetros e argumentos
(posicionais, com valores padrão e nomeados), como retornar valores com return, a
importância da documentação com docstrings, o funcionamento do escopo de variáveis
(local e global) e uma introdução às concisas funções lambda. Os exercícios e o projeto a
seguir são projetados para solidificar seu entendimento e aplicação prática desses conceitos
cruciais para a escrita de código modular, reutilizável e legível.

Exercícios
1. E10.1: Funções Simples sem Parâmetros nem Retorno (Prático)
478

(a) Crie uma função chamada mostrar_cabecalho_programa. Quando chamada,


esta função deve imprimir no console um cabeçalho textual simples para um
programa, por exemplo:
****************************************
* *
* Sistema de Análise de Dados V1.0 *
* *
****************************************

(b) Crie uma segunda função chamada mostrar_dados_desenvolvedor. Esta


função deve imprimir seu nome completo e uma breve descrição (ex: “Alan Afif
Helal - Desenvolvedor do Guia Python Descomplicado”).
(c) Em seu script, após definir ambas as funções, chame
mostrar_cabecalho_programa() e, em seguida, chame
mostrar_dados_desenvolvedor().

2. E10.2: Funções com Parâmetros, mas sem Retorno Explícito (Prático)

(a) Crie uma função chamada saudacao_com_horario que receba dois parâmetros:
nome_pessoa (uma string) e horario_atual (uma string, ex: “manhã”, “tarde”,
“noite”). Dentro da função, imprima uma saudação personalizada usando esses
parâmetros. Por exemplo, se chamada com ("Maria", "tarde"), deveria
imprimir algo como: “Boa tarde, Maria! Espero que seu dia esteja ótimo.”
(b) Chame a função saudacao_com_horario pelo menos três vezes com diferentes
nomes e horários.
(c) Crie uma função chamada exibir_dados_sensor que receba três parâmetros:
id_sensor (string), leitura_valor (float) e unidade_medida (string). A
função deve imprimir uma mensagem formatada como: “Sensor [id_sensor]:
Leitura atual = [leitura_valor] [unidade_medida].”
(d) Chame a função exibir_dados_sensor com dados de pelo menos dois sensores
diferentes (ex: um de temperatura em Celsius, outro de pressão em Pascal).

3. E10.3: Funções com Parâmetros e Valores de Retorno (Prático)

(a) Crie uma função chamada calcular_area_triangulo que receba a base e a


altura de um triângulo como parâmetros (ambos podem ser float) e retorne
a área calculada (Lembre-se: Área = (base * altura) / 2).
(b) No seu script, chame esta função com alguns valores de base e altura, armazene
o resultado retornado em uma variável e, em seguida, imprima o valor dessa
variável (a área).
(c) Crie uma função chamada eh_maior_que_limite que receba dois parâmetros:
um numero e um limite. A função deve retornar True se o numero for
estritamente maior que o limite, e False caso contrário.
(d) Teste a função eh_maior_que_limite com diferentes números e limites, impri-
mindo os valores booleanos retornados.
479

(e) Crie uma função chamada concatenar_com_espaco que receba duas strings,
str1 e str2, como parâmetros. A função deve retornar uma nova string que
é a concatenação de str1, um espaço em branco, e str2.
(f) Teste esta função com diferentes pares de strings.
4. E10.4: Docstrings e Escopo de Variáveis (Prático e Conceitual)
(a) Escolha uma das funções que você criou no Exercício E10.3 (por exemplo,
calcular_area_triangulo). Adicione uma docstring completa e informativa
a ela. A docstring deve ter um resumo na primeira linha, seguido por uma linha
em branco e, se necessário, uma explicação mais detalhada. Documente também
os parâmetros (usando a seção Args:) indicando o nome do parâmetro, o tipo
esperado (ex: base (float ou int)) e uma descrição. Documente também o
valor de retorno (usando a seção Returns:) indicando o tipo retornado e o que
ele representa.
(b) Após adicionar a docstring, use a função help(nome_da_sua_funcao) no seu
script (ou no console Python) para visualizar a ajuda gerada a partir da sua
docstring. Você também pode imprimir nome_da_sua_funcao.__doc__.
(c) Análise de Escopo: Considere o seguinte trecho de código:
1 v ar i av e l _g l ob a l _n u m = 200
2

3 def m i n h a _ f u n c a o _ d e _ e s c o p o ( parametro_func ) :
4 v a ri a v el _ lo c a l_ f un c = 10
5 result ado_inter no = parametro_func *
v ar i a ve l _l o ca l _ fu n c + v ar i av e l_ g l ob a l_ n u m
6 print ( f " Dentro da fun ç ã o - parametro_func :
{ parametro_func } " )
7 print ( f " Dentro da fun ç ã o -
v ar i a ve l _l o ca l _ fu n c : { v ar i av e l _l o ca l _f u n c } " )
8 print ( f " Dentro da fun ç ã o - va r i av e l_ g lo b a l_ n um
( lida ) : { va r i av e l_ g l ob a l_ n um } " )
9 # v a ri a ve l _ gl o ba l _n u m = 500 # O que
aconteceria se esta linha fosse
descomentada ?
10 return re sultado_ interno
11
12 print ( f " Antes da fun ç ã o - v a ri a v el _ gl o b al _ nu m :
{ v a ri a ve l _ gl o ba l _ nu m } " )
13 saida_funcao = m i n h a _ f u n c a o _ d e _ e s c o p o (5)
14 print ( f " Sa í da da fun ç ã o : { saida_funcao } " )
15 print ( f " Depois da fun ç ã o - v a ri a v el _ gl o ba l _ nu m :
{ v a ri a ve l _ gl o ba l _ nu m } " )
16 # print ( v a r ia v el _ l oc a l_ f un c ) # Esta linha daria
erro . Por qu ê ?
17 # print ( parametro_func ) # Esta linha tamb é m daria
erro . Por qu ê ?

Código 21.39: Código para análise de escopo E10.4c

Copie este código para o seu script. Sem executar ainda:


• Quais variáveis são globais?
• Quais são locais à função minha_funcao_de_escopo?
• O que você espera que seja impresso para cada print()?
480

• O que aconteceria se a linha # variavel_global_num = 500 fosse desco-


mentada sem usar a palavra-chave global?
• O valor da variavel_global_num fora da função seria alterado?
• Por que as duas últimas linhas comentadas (print(variavel_local_func)
e print(parametro_func)) causariam um erro se fossem descomentadas?
Execute o código para verificar suas respostas.
(d) Modificando Global: Agora, modifique a função minha_funcao_de_escopo
para que ela realmente altere o valor da variavel_global_num para 500.
Lembre-se de usar a palavra-chave global apropriadamente dentro da fun-
ção. Chame a função e imprima o valor de variavel_global_num antes e
depois da chamada para confirmar a alteração.

5. E10.5: Argumentos com Valores Padrão e Argumentos Nomeados (Prá-


tico)

(a) Crie uma função chamada gerar_relatorio que aceite os seguintes parâmetros:
• titulo_relatorio (obrigatório, string)
• autor (obrigatório, string)
• versao (opcional, string, com valor padrão "1.0")
• confidencial (opcional, booleano, com valor padrão False)
A função deve imprimir os detalhes do relatório de forma formatada. Por
exemplo:
--- Relatório: [Título] ---
Autor: [Autor]
Versão: [Versão]
Confidencial: [Sim/Não]
--------------------------

(Converta o booleano confidencial para "Sim"ou "Não"na impressão).


(b) Chame a função gerar_relatorio:
• Uma vez, fornecendo apenas os argumentos obrigatórios (titulo_relatorio
e autor).
• Outra vez, fornecendo o título, autor e definindo confidencial=True
(usando argumento nomeado para pular ‘versao‘).
• Uma terceira vez, fornecendo todos os argumentos, mas usando argumentos
nomeados para autor e versao em uma ordem diferente da definição.
(c) Relembre a função saudacao_completa(nome, periodo_dia="dia") do Exem-
plo 2 da Seção 10.6. Chame esta função para uma pessoa chamada "Ricardo",
especificando que o periodo_dia é "noite", mas passe os argumentos usando
a técnica de argumentos nomeados, e na ordem periodo_dia primeiro, depois
nome.
(d) Explique brevemente (em um comentário) por que os argumentos nomeados são
particularmente úteis quando uma função tem vários parâmetros, especialmente
se muitos deles têm valores padrão.
481

6. E10.6: Funções Lambda (Prático)


(a) Crie uma função lambda que receba um único argumento numérico x e retorne
x elevado à quarta potência (x4 ). Atribua esta função lambda a uma variável cha-
mada potencia_quarta e teste-a imprimindo o resultado de potencia_quarta(2)
e potencia_quarta(3).
(b) Crie uma função lambda que receba dois argumentos, a e b, e retorne True se
a for maior que b, e False caso contrário. Atribua-a a uma variável eh_maior
e teste-a com alguns pares de números.
(c) Dada a lista de strings: nomes = ["Carlos", "ana", "Beatriz", "daniel"].
Use a função embutida sorted() junto com uma função lambda como o argu-
mento key para ordenar a lista nomes de forma insensível a maiúsculas/minús-
culas (ou seja, "ana"deve vir antes de "Beatriz"). Imprima a lista ordenada.
(Dica: a lambda pode converter cada nome para minúsculo antes da comparação
para ordenação).
(d) (Desafio) Dada a lista de tuplas pontos = [(1, 5), (3, 2), (8, 10), (4,
7)], onde cada tupla é (x, y), use sorted() com uma lambda para ordenar a
lista de pontos com base na soma de suas coordenadas x + y. Imprima a lista
ordenada.

Projeto do Capítulo 10: Conversor Universal de Unidades (Modularizado com


Funções)
Título do Projeto: Conversor Universal de Unidades de Engenharia (Modularizado).
Objetivos:
1. Praticar intensivamente a criação e o uso de funções com parâmetros e valores de
retorno.
2. Utilizar docstrings claras e completas para documentar todas as funções criadas.
3. Aplicar estruturas condicionais (if-elif-else) dentro das funções para lidar com
diferentes tipos de conversão ou para validação de entrada.
4. Criar um menu interativo principal que chame as funções de conversão apropriadas
com base na escolha do usuário.
5. Organizar o código de forma modular, com funções bem definidas e com responsabi-
lidade única para cada tipo de conversão.
6. Praticar o tratamento básico de entrada do usuário e a formatação da saída.
Descrição: Crie um programa Python que funcione como um conversor de unidades
para diversas grandezas físicas comuns em engenharia e ciências. O programa deve
apresentar um menu principal ao usuário, permitindo que ele escolha qual tipo de grandeza
deseja converter (ex: Temperatura, Comprimento, Massa, Pressão). Após escolher a
grandeza, um submenu deve aparecer com as opções de conversão específicas para aquela
grandeza. O usuário então fornecerá o valor a ser convertido, e o programa exibirá o
resultado.
Funcionalidades Sugeridas (Grandezas e Conversões): Você deve implementar
funções separadas para cada conversão.
482

1. Temperatura:

• Celsius para Fahrenheit (F = C × 95 + 32)


• Fahrenheit para Celsius (C = (F − 32) × 59 )
• Celsius para Kelvin (K = C + 273.15)
• Kelvin para Celsius (C = K − 273.15)
• (Opcional) Fahrenheit para Kelvin e Kelvin para Fahrenheit.

Crie funções como celsius_para_fahrenheit(temp_c),


fahrenheit_para_celsius(temp_f), etc.

2. Comprimento:

• Metros para Pés (1 metro ≈ 3.28084 pés)


• Pés para Metros
• Polegadas para Centímetros (1 polegada = 2.54 cm)
• Centímetros para Polegadas
• (Opcional) Quilômetros para Milhas (1 km ≈ 0.621371 milhas) e vice-versa.

Crie funções como metros_para_pes(m), polegadas_para_cm(pol), etc.

3. Massa:

• Quilogramas para Libras (1 kg ≈ 2.20462 libras)


• Libras para Quilogramas
• (Opcional) Gramas para Onças (1 g ≈ 0.035274 onças) e vice-versa.

Crie funções como kg_para_libras(kg), etc.

4. Pressão (Opcional, um pouco mais de pesquisa para fatores):

• Pascal (Pa) para psi (pound per square inch) (1 psi ≈ 6894.76 Pa)
• psi para Pascal
• atm (atmosfera padrão) para Pascal (1 atm = 101325 Pa)
• Pascal para atm
• (Opcional) bar para Pascal (1 bar = 100000 Pa) e vice-versa.

Crie funções como pascal_para_psi(pa), etc.

Estrutura do Programa:

1. Funções de Conversão Específicas:

• Para cada par de conversão listado acima (ex: Celsius para Fahrenheit), crie
uma função dedicada.
• Cada função deve receber o valor a ser convertido como parâmetro.
• Cada função deve retornar o valor convertido.
483

• Todas as funções devem ter docstrings claras explicando o que fazem, seus
parâmetros e o que retornam.

2. Funções de Menu Secundário (uma para cada Grandeza):

• Crie uma função para cada tipo de grandeza


(ex: menu_conversao_temperatura(), menu_conversao_comprimento()).
• Dentro de cada uma dessas funções de menu:
– Apresente ao usuário as opções de conversão específicas para aquela gran-
deza (ex: para temperatura, "1. Celsius para Fahrenheit", "2. Fahrenheit
para Celsius", etc., "Voltar ao menu principal").
– Peça ao usuário para escolher uma sub-opção.
– Peça o valor a ser convertido.
– Chame a função de conversão específica apropriada.
– Imprima o resultado da conversão de forma clara (ex: “[ValorEntrada]
[UnidadeEntrada] equivalem a [ValorSaida] [UnidadeSaida].”).
– Lide com entradas inválidas para as opções do submenu.

3. Menu Principal:

• Crie um laço while True: para o menu principal do programa.


• Dentro do laço, apresente as opções de grandezas disponíveis para conversão
(Temperatura, Comprimento, Massa, Pressão (opcional), Sair).
• Peça ao usuário para escolher uma grandeza.
• Use uma estrutura if-elif-else para chamar a função de menu secundário
correspondente à escolha do usuário.
• Se o usuário escolher "Sair", imprima uma mensagem de despedida e use break
para encerrar o laço principal e o programa.
• Lide com opções inválidas no menu principal.

4. Tratamento Básico de Entrada do Usuário:

• Ao pedir valores numéricos (como a temperatura a ser convertida), tente


converter a entrada para float. Se a conversão falhar (ex: o usuário digita
texto), o ideal seria usar um bloco try-except ValueError (Capítulo 17)
para tratar o erro e pedir a entrada novamente. Se você ainda não se sentir
confortável com try-except, você pode, por enquanto, apenas assumir que
o usuário digitará um número ou adicionar uma verificação simples (como
[Link](’.’, ”, 1).isdigit()) e pedir para tentar de novo se for
inválido, sabendo que o programa pode quebrar com entradas muito ruins. O
foco principal deste projeto é a modularização com funções.

Desafios Extras (Opcionais):

1. Mais Unidades e Grandezas: Expanda o conversor adicionando mais grandezas


(ex: Velocidade, Volume, Área, Ângulo, Energia, Potência) e mais unidades dentro
de cada grandeza.
484

2. Precisão do Resultado: Permita que o usuário, talvez através de uma opção de


configuração no início, defina com quantas casas decimais os resultados das conversões
devem ser exibidos.
3. Funções Lambda para Fórmulas Muito Simples: Se alguma de suas funções de
conversão for apenas uma fórmula matemática de uma linha muito direta, considere
se faria sentido (ou não, pela clareza) usar uma função lambda para ela, atribuída a
uma variável. Compare com a definição def normal em termos de legibilidade para
o seu caso. (Lembre-se que funções def permitem docstrings, o que é uma grande
vantagem).
4. Interface Amigável: Pense em como tornar as mensagens para o usuário e a
navegação pelos menus o mais clara e intuitiva possível.

Capítulo 11: Módulos e Pacotes (Exercícios e Projeto)


Neste capítulo, aprendemos como organizar nosso código Python em unidades reu-
tilizáveis chamadas módulos (arquivos .py) e como agrupar módulos relacionados em
pacotes (diretórios com um arquivo __init__.py). Vimos diferentes formas de importar
funcionalidades, como o Python encontra os módulos, exploramos alguns módulos úteis
da Biblioteca Padrão, entendemos o propósito do bloco if __name__ == "__main__",
e tivemos uma introdução à instalação de pacotes externos com pip. Os exercícios e o
projeto a seguir ajudarão você a praticar esses conceitos essenciais para escrever código
Python bem estruturado e escalável.

Exercícios
1. E11.1: Criando e Usando um Módulo Próprio para Cálculos Geométricos
(Prático)
(a) Crie um novo arquivo Python chamado geometria_plana.py. Dentro deste
arquivo, defina as seguintes três funções, cada uma com uma docstring apropri-
ada:
• calcular_area_retangulo(base, altura): Deve receber a base e a al-
tura de um retângulo e retornar sua área.
• calcular_perimetro_retangulo(base, altura): Deve receber a base e
a altura de um retângulo e retornar seu perímetro.
• calcular_area_circulo(raio): Deve receber o raio de um círculo e
retornar sua área. Para o valor de π, use a constante [Link] do mó-
dulo math (você precisará importar o módulo math dentro do seu arquivo
geometria_plana.py para isso).
(b) Em um outro arquivo Python (por exemplo, teste_geometria.py), que deve
estar no mesmo diretório que geometria_plana.py:
• Importe o módulo geometria_plana que você acabou de criar usando a
instrução import geometria_plana.
• Peça ao usuário para fornecer os valores da base e da altura de um retângulo.
• Use as funções importadas do seu módulo para calcular e imprimir a área
e o perímetro desse retângulo. Lembre-se de chamar as funções usando a
notação geometria_plana.nome_da_funcao(...).
485

• Peça ao usuário para fornecer o raio de um círculo.


• Use a função importada para calcular e imprimir a área do círculo.
(c) Crie um terceiro arquivo Python (ex: teste_geometria_from.py), também no
mesmo diretório. Nele, em vez de importar o módulo inteiro, importe apenas
duas funções específicas.
• Peça os dados necessários ao usuário e chame as funções
calcular_area_circulo() e calcular_area_retangulo() diretamente
(sem o prefixo geometria_plana.) para calcular e imprimir os resultados.
• Tente chamar a função calcular_perimetro_retangulo() neste terceiro
script. O que acontece? Por quê? (Comente a linha que causa o erro).
2. E11.2: Criando um Módulo de Utilitários de String (Prático)

• Crie um novo arquivo Python chamado meus_utilitarios_string.py.


• Dentro deste módulo, defina as seguintes funções, cada uma com uma docstring
clara:
– inverter_texto(texto): Recebe uma string texto e retorna essa string
invertida (ex: “python” vira “nohtyp”).
– contar_numero_vogais(texto): Recebe uma string texto e retorna o
número de vogais (a, e, i, o, u, considerando maiúsculas e minúsculas, mas
não precisa se preocupar com acentos para este exercício).
– eh_palindromo_simples(texto): Recebe uma string texto e retorna
True se a string for um palíndromo (lê-se da mesma forma de trás para
frente), e False caso contrário. Para simplificar, considere que a verificação
deve ignorar espaços e diferenças entre maiúsculas e minúsculas (ex: “Ana”,
“Anotaram a data da maratona”, “Socorram-me, subi no ônibus em Marrocos”
são palíndromos).
• Crie um script separado chamado testador_util_string.py no mesmo dire-
tório.
• Neste script, importe seu módulo meus_utilitarios_string (você pode usar
um alias se quiser, ex: import meus_utilitarios_string as util_str).
• Chame cada uma das três funções definidas no seu módulo com diferentes
strings de exemplo e imprima os resultados para testá-las.

3. E11.3: Explorando Módulos da Biblioteca Padrão do Python (Prático)


Crie um script Python e, dentro dele, realize as seguintes tarefas usando os módulos
da biblioteca padrão:

(a) Usando o módulo math:


• Importe o módulo math.
• Peça ao usuário para digitar um ângulo em graus (ex: 45).
• Converta este ângulo para radianos usando [Link]().
• Calcule e imprima o seno, o cosseno e a tangente do ângulo em radianos
usando [Link](), [Link](), e [Link]().
• Imprima o valor da constante math.e (número de Euler).
(b) Usando o módulo random:
486

• Importe o módulo random.


• Gere e imprima 5 números de ponto flutuante aleatórios entre 0.0 (inclusive)
e 1.0 (exclusive) usando [Link]().
• Gere e imprima 3 números inteiros aleatórios entre 50 e 100 (ambos inclusi-
vos) usando [Link]().
• Crie uma lista com os nomes de cinco colegas. Use [Link]() para
escolher e imprimir um nome aleatoriamente da lista.
• Use [Link]() para embaralhar a ordem dos nomes na sua lista
de colegas e, em seguida, imprima a lista embaralhada.
(c) Usando o módulo datetime:
• Importe a classe datetime do módulo datetime (ex: from datetime
import datetime, timedelta).
• Obtenha e imprima a data e hora atuais usando [Link]().
• Imprima apenas a data atual (acessando o atributo .date() do objeto
datetime).
• Imprima apenas a hora atual (acessando o atributo .time() do objeto
datetime).
• Formate e imprima a data e hora atuais no padrão brasileiro: DD/MM/A-
AAA HH:MM:SS (usando strftime()).
• Calcule e imprima qual será a data daqui a exatamente 10 dias e 5 horas a
partir de agora (usando timedelta).

4. E11.4: Praticando com o Bloco if __name__ == "__main__" (Prático)

• Pegue o módulo meus_utilitarios_string.py que você criou no Exercício


E11.2.
• Ao final deste arquivo, adicione um bloco if __name__ == "__main__":.
• Dentro deste bloco, coloque algumas chamadas de exemplo para cada uma das
suas funções (inverter_texto, contar_numero_vogais, palindromo_simples),
imprimindo os resultados para demonstrar e testar as funções. Por exemplo:
1 # ... ( defini ç õ es das fun ç õ es inverter_texto ,
etc .) ...
2
3 if __name__ == " __main__ " :
4 print ( " --- Testando o m ó dulo
m e u s _ u t i l i t a r i o s _ s t r i n g . py ---" )
5
6 original = " Python "
7 invertida = inverter_texto ( original )
8 print ( f " ’{ original } ’ invertido é ’{ invertida } ’ " )
9
10 frase_vogais = " Engenharia de Software "
11 num_vogais = c o n t a r _ n u m e r o _ v o g a i s ( frase_vogais )
12 print ( f " ’{ frase_vogais } ’ tem { num_vogais } vogais . " )
13
14 pal av ra _pa li nd ro mo = " Radar "
15 if e h _ p a l i n d r o m o _ s i m p l e s ( p al av ra_ pa li ndr om o ) :
16 print ( f " ’{ p ala vr a_ pal in dr omo } ’ é um pal í ndromo . " )
17 else :
487

18 print ( f " ’{ p ala vr a_ pal in dr omo } ’ N Ã O é um


pal í ndromo . " )
19
20 print ( " --- Fim dos testes do m ó dulo ---" )

Código 21.40: Bloco main em meus_utilitarios_string.py

• Agora, faça o seguinte:


(a) Execute o arquivo meus_utilitarios_string.py diretamente (ex: python
meus_utilitarios_string.py). Observe a saída. O bloco de teste dentro
do if foi executado?
(b) Abra o seu script testador_util_string.py (do Exercício E11.2, que im-
porta meus_utilitarios_string). Execute testador_util_string.py.
Observe a saída. O bloco de teste dentro do if __name__ == "__main__"
de meus_utilitarios_string.py foi executado desta vez? Por que sim
ou por que não?
5. E11.5: Criando e Utilizando um Pacote Simples (Prático/Estrutural)
Neste exercício, você organizará algumas funções em um pacote.
(a) Crie a seguinte estrutura de diretórios e arquivos no seu computador. Comece
criando uma pasta principal, por exemplo, meu_projeto_com_pacote.
(b) No arquivo pacote_engenharia/__init__.py, você pode deixá-lo completa-
mente vazio por enquanto, ou adicionar um
print("Pacote ’pacote_engenharia’ foi inicializado.") para ver quando
ele é carregado.
(c) No arquivo pacote_engenharia/conversoes_unidades.py, defina pelo menos
duas funções de conversão de unidades que sejam úteis para engenharia. Por
exemplo:
• polegadas_para_milimetros(polegadas) (1 polegada = 25.4 mm)
• libras_para_quilogramas(libras) (1 libra ≈ 0.453592 kg)
Não se esqueça das docstrings.
(d) No arquivo pacote_engenharia/calculos_basicos.py, defina pelo menos
duas funções de cálculos básicos:
• media_lista(lista_numeros) (retorna a média de uma lista de números).
• maior_valor(lista_numeros) (retorna o maior valor de uma lista de
números).
(Você pode usar funções embutidas do Python como sum(), len(), max() aqui
dentro). Adicione docstrings.
(e) Agora, no arquivo script_de_uso.py:
• Importe a função polegadas_para_milimetros do módulo
conversoes_unidades dentro do pacote pacote_engenharia.
• Importe o módulo calculos_basicos inteiro do pacote pacote_engenharia.
Use a sintaxe:
import pacote_engenharia.calculos_basicos as calc_bas.
• No script_de_uso.py, peça ao usuário um valor em polegadas, chame a
função polegadas_para_milimetros() importada e imprima o resultado.
488

• Crie uma lista de números e use a função media_lista() (chamando como


calc_bas.media_lista()) para calcular e imprimir a média.

6. E11.6: Instalando e Usando um Pacote Externo com pip (Prático)

(a) Boa Prática (Opcional, mas Recomendado): Se você ainda não tem um
ambiente virtual (venv) para seus projetos Python, este é um bom momento
para pesquisar como criar um simples para o seu sistema operacional (Seção 11.7
deu uma breve introdução). Ative-o antes de prosseguir. Isso não é estritamente
necessário para o exercício funcionar, mas é uma prática fundamental em
projetos Python.
(b) Abra seu terminal ou prompt de comando (com o ambiente virtual ativado, se
estiver usando um).
(c) Vamos instalar um pacote divertido e simples chamado pyjokes. Ele serve
para obter piadas aleatórias (geralmente sobre programação e em inglês). No
terminal, digite: pip install pyjokes
(d) Crie um novo script Python (ex: mostra_piada.py).
(e) Neste script, importe o pacote que você instalou: import pyjokes.
(f) Use uma função do pacote para obter e imprimir uma piada. A documentação do
pyjokes (ou uma busca rápida online por “pyjokes python example”) mostrará
que você pode usar pyjokes.get_joke() ou pyjokes.get_jokes() para obter
uma ou mais piadas. Experimente!
1 # import pyjokes # Ap ó s ’ pip install pyjokes ’
2 #
3 # piada_unica = pyjokes . get_joke ( language = ’ en ’,
category = ’ neutral ’)
4 # print (" Uma piada para alegrar o dia :")
5 # print ( piada_unica )
6 #
7 # # Para piadas em portugu ê s ( se dispon í veis e de
boa qualidade na lib ) :
8 # # piada_pt = pyjokes . get_joke ( language = ’ pt ’,
category = ’ all ’)
9 # # print ("\ nEm portugu ê s :")
10 # # print ( piada_pt )

Código 21.41: Exemplo de uso do pacote pyjokes

(Nota: A disponibilidade e qualidade das piadas em português podem variar. O


inglês (‘en‘) geralmente tem mais opções.)
(g) (Opcional) Após testar, você pode ver o pacote listado com pip list. Se
quiser, pode desinstalá-lo com pip uninstall pyjokes para retornar ao estado
anterior.

Projeto do Capítulo 11: Mini Biblioteca de Funções de Engenharia e Script de


Demonstração
Título do Projeto: Minha Caixa de Ferramentas de Engenharia (Modularizada).
Objetivos:
489

1. Praticar a criação de múltiplos módulos Python, cada um contendo funções relacio-


nadas a um tema específico (focado em utilidades de engenharia).

2. Organizar esses módulos dentro de um pacote Python personalizado.

3. Utilizar o bloco if __name__ == "__main__": em cada módulo para incluir código


de teste ou demonstração local para as funções daquele módulo.

4. Criar um script principal (fora do pacote) que importa e utiliza funcionalidades de


diferentes módulos do pacote criado, demonstrando a reutilização e a organização.

5. (Opcional) Pesquisar e integrar uma função de um módulo da biblioteca padrão do


Python que seja relevante e útil dentro de uma das suas funções personalizadas.

Descrição do Projeto: Você desenvolverá uma pequena biblioteca Python, organizada


como um pacote, contendo funções utilitárias para cálculos comuns em diferentes áreas da
engenharia (ou em uma área específica de seu maior interesse). Em seguida, você criará
um script de demonstração que mostra como importar e usar as funções da sua biblioteca.
Estrutura do Pacote Sugerida (Exemplo): Crie uma pasta principal para o
projeto, por exemplo, projeto_minha_biblioteca_eng.
Tarefas Detalhadas:

1. Criação da Estrutura de Pastas e Arquivos: Crie os diretórios e os ar-


quivos .py vazios conforme a estrutura sugerida acima. Lembre-se que o ar-
quivo __init__.py (mesmo que inicialmente vazio) é o que define o diretório
eng_utilitarios como um pacote. Você pode adicionar um print("Pacote
’eng_utilitarios’ inicializado.") no __init__.py para ver quando ele é car-
regado.

2. Desenvolvimento dos Módulos do Pacote: Para cada um dos módulos ou os


que você escolheu):

• Defina pelo menos duas a três funções úteis e relevantes para aquela área
específica da engenharia.
– Exemplos para mecanica_dos_fluidos.py:
∗ calcular_velocidade_media()
∗ calcular_numero_reynolds()
∗ converter_pressao(valor, unidade_origem, unidade_destino) (ex:
de "Pa"para "atm", ou "psi"para "kPa"). (Este pode usar um dicioná-
rio de fatores de conversão internamente).
– Exemplos para circuitos_eletricos.py:
∗ calcular_resistencia_equivalente_serie(lista_resistores)
∗ calcular_resistencia_equivalente_paralelo(lista_resistores)
∗ calcular_lei_de_ohm(v=None, i=None, r=None) (recebe dois dos
três e calcula o terceiro).
– Exemplos para estatistica_basica.py:
∗ calcular_media_ponderada(valores, pesos)
∗ calcular_variancia_amostral(lista_dados)
490

∗ (Opcional) Uma função que use algo do módulo math ou numpy (se
já quiser antecipar) para um cálculo mais complexo, como um desvio
padrão (embora NumPy já tenha).
• Adicione docstrings claras e completas para cada função que você criar,
explicando o que ela faz, seus parâmetros (com tipos esperados) e o que ela
retorna.
• Ao final de cada arquivo de módulo, adicione um bloco if __name__ ==
"__main__": que contenha código para testar as funções daquele módulo
específico. Faça chamadas de exemplo para cada função com alguns valores de
entrada e imprima os resultados para verificar se estão corretos.

3. Desenvolvimento do Script de Demonstração (script_demonstracao.py):


Este script estará fora do seu pacote eng_utilitarios, mas dentro da pasta principal
do projeto.

• Neste script, importe as funções que você criou nos diferentes módulos do seu
pacote. Experimente diferentes formas de importação:
– Importar um módulo inteiro com alias.
– Importar funções específicas de um módulo.
• Demonstre o uso de pelo menos uma função de cada um dos seus módulos.
Você pode:
– Definir alguns valores de entrada diretamente no script.
– Ou, para tornar mais interativo, pedir alguns valores ao usuário usando
input().
• Imprima os resultados das chamadas às funções de forma clara e organizada,
indicando qual cálculo está sendo realizado.

4. (Opcional) Usando o __init__.py do Pacote para Facilitar Importações:

• Experimente editar o arquivo eng_utilitarios/__init__.py.


• Dentro dele, você pode importar seletivamente algumas das suas funções mais im-
portantes dos submódulos para o nível do pacote. Por exemplo, se você adicionar
a linha from .mecanica_dos_fluidos import calcular_numero_reynolds
no __init__.py, então no seu script_demonstracao.py, você poderia fazer:
1 import eng_utilitarios # Importa o pacote
2
3 # Agora pode chamar diretamente , se importado no
__init__ . py
4 # reynolds =
eng_utilitarios . c a l c u l a r _ n u m e r o _ r e y n o l d s (...)

Código 21.42: Importação facilitada via __init__.py

Isso pode tornar a interface do seu pacote mais limpa para o usuário, expondo as
funcionalidades principais diretamente. (O . antes de mecanica_dos_fluidos
na importação dentro do __init__.py é para importações relativas dentro do
mesmo pacote).

Desafios Extras (Opcionais):


491

1. Subpacotes: Se você tiver muitas funções em uma área (ex: muitos cálculos de ter-
modinâmica), considere criar um submódulo (ou até um subpacote termo/) dentro de
eng_utilitarios para organizar melhor. Ex: eng_utilitarios/termo/[Link].
Depois, ajuste suas importações.

2. Integração com Biblioteca Padrão: Em alguma das suas funções utilitárias,


tente encontrar uma oportunidade para usar uma função de um módulo da biblioteca
padrão do Python que seja relevante (ex: usar [Link] para um cálculo que
envolva logaritmos, ou [Link] para potências, em vez de ** apenas para praticar
a importação e uso).

3. Interface de Linha de Comando (Antecipando Tópicos Futuros): Se quiser


um desafio maior, pesquise sobre o módulo argparse da biblioteca padrão e tente
fazer com que seu script_demonstracao.py aceite argumentos da linha de comando
para escolher qual função da sua biblioteca executar e com quais valores, em vez de
usar input().

Parte IV: Programação Orientada a Objetos em Python


Capítulo 12: Conceitos Fundamentais de Programação Orientada
a Objetos (Exercícios e Projeto)
Neste capítulo, demos os primeiros passos no paradigma da Programação Orientada a
Objetos (POO). Introduzimos a ideia de classes como moldes e objetos como instâncias
concretas, aprendemos a definir atributos para representar o estado dos objetos (usando
__init__ e self) e métodos para definir seus comportamentos. Também tivemos uma
visão geral dos quatro pilares da POO. Os exercícios e o projeto a seguir ajudarão você a
praticar esses conceitos iniciais, mas fundamentais.

Exercícios
1. E12.1: Conceitos Fundamentais de POO (Conceitual)
Responda com suas palavras às seguintes perguntas, baseando-se no que foi apresen-
tado no Capítulo 12:

(a) O que é uma classe na Programação Orientada a Objetos? Dê uma analogia


do mundo real para uma classe (diferente das usadas no texto do capítulo, se
possível).
(b) O que é um objeto (ou instância) em POO? Dê um exemplo de pelo menos
dois objetos que poderiam ser criados a partir da classe que você usou como
analogia no item anterior.
(c) Qual é o propósito principal do método especial __init__(self, ...) em
uma classe Python? Quando ele é executado?
(d) O que o parâmetro self representa dentro dos métodos de uma classe? Por
que ele é importante?
(e) Os quatro pilares da POO foram brevemente introduzidos como Abstração,
Encapsulamento, Herança e Polimorfismo. Escolha dois desses pilares e descreva
brevemente, com suas palavras, o que você entendeu sobre cada um deles.
492

2. E12.2: Definindo Classes Simples e Criando Objetos (Prático)

(a) Defina uma classe Python chamada Computador. Por enquanto, o corpo da
classe pode conter apenas a instrução pass.
(b) Crie duas instâncias (objetos) diferentes da classe Computador. Atribua a pri-
meira instância à variável meu_desktop e a segunda à variável notebook_trabalho.
(c) Use a função type() para imprimir o tipo do objeto meu_desktop. Qual é a
saída esperada?
(d) Use o operador is para verificar se meu_desktop e notebook_trabalho se
referem ao mesmo objeto na memória. Imprima o resultado booleano.

3. E12.3: Adicionando Atributos de Instância com o Método __init__ (Prá-


tico)

(a) Modifique a classe Computador do exercício anterior. Adicione um método


__init__(self, marca, processador, memoria_ram_gb) que inicialize os
seguintes atributos de instância:
[Link], [Link], e self.memoria_ram.
(b) Crie duas instâncias da sua nova classe Computador, fornecendo informações
diferentes para cada uma ao criá-las. Por exemplo:
• comp1 = Computador("Dell", "Intel i7", 16)
• comp2 = Computador("HP", "AMD Ryzen 5", 8)
(c) Para cada um dos objetos criados (comp1 e comp2), acesse e imprima os valores
de seus atributos marca, processador e memoria_ram.
(d) Suponha que você fez um upgrade na memória RAM do comp1. Modifique o
atributo memoria_ram do objeto comp1 para 32. Imprima novamente o valor
de comp1.memoria_ram para confirmar a alteração.

4. E12.4: Definindo e Usando Métodos de Instância (Prático)

(a) Continue com a classe Computador do exercício anterior. Adicione um método


chamado exibir_especificacoes(self). Este método não deve receber ou-
tros parâmetros além de self. Dentro do método, use print() para exibir
todas as especificações do computador (marca, processador, memória RAM) de
forma organizada.
(b) Chame o método exibir_especificacoes() para os dois objetos Computador
(comp1 e comp2) que você criou no exercício E12.3b.
(c) Adicione outro método à classe Computador chamado
pode_rodar_software_pesado(self, ram_minima_software=16). Este mé-
todo deve receber um parâmetro opcional ram_minima_software com valor
padrão 16. O método deve retornar True se a self.memoria_ram do compu-
tador for maior ou igual à ram_minima_software, e False caso contrário.
(d) Teste o método pode_rodar_software_pesado() para comp1 e comp2. Teste
também chamando o método em comp2 e passando um valor diferente para
ram_minima_software (ex: 4). Imprima os resultados booleanos.
493

5. E12.5: Modelando um Objeto do Mundo Real para Engenharia (Prático)


Escolha um objeto físico ou um conceito simples do mundo da engenharia que você
possa modelar com uma classe. Alguns exemplos poderiam ser: ResistorEletrico,
VigaSimples, SensorDeTemperatura, MotorDCBasico, Ponto2D.

(a) Defina uma classe Python para representar o objeto escolhido. Dê um nome
apropriado à classe.
(b) No método __init__ da sua classe, defina pelo menos três atributos de ins-
tância que sejam características importantes desse objeto. Por exemplo, se
escolheu ResistorEletrico, os atributos poderiam ser resistencia_ohms,
tolerancia_percentual, potencia_watts.
(c) Crie pelo menos um método de instância (além do __init__) que realize alguma
ação ou retorne alguma informação calculada sobre o objeto. Por exemplo, para
ResistorEletrico, você poderia ter um método
calcular_faixa_resistencia(self) que retorna uma tupla com os valores
mínimo e máximo da resistência considerando a tolerância. Para um Ponto2D
com atributos x e y, poderia ter um método distancia_da_origem(self).
(d) Crie duas instâncias (objetos) diferentes da sua classe, fornecendo dados distintos
para seus atributos no construtor.
(e) Chame os métodos que você criou para cada uma das suas instâncias e imprima
os resultados ou observe os efeitos para verificar se sua classe está funcionando
como esperado. Adicione docstrings à sua classe e métodos.

Projeto do Capítulo 12: Cadastro Simples de Equipamentos de Laboratório


Título do Projeto: Sistema Básico de Cadastro de Equipamentos de Laboratório.
Objetivos:

1. Praticar a definição de uma classe Python para modelar uma entidade do mundo
real.

2. Utilizar o método construtor __init__ para inicializar os atributos de objetos.

3. Implementar métodos de instância que manipulem ou exibam os atributos dos


objetos.

4. Criar e interagir com múltiplas instâncias (objetos) de uma classe.

5. Estruturar um pequeno programa que utilize os conceitos básicos de POO para


organizar dados e comportamentos.

Descrição: Você criará um script Python que define uma classe para representar
EquipamentoLaboratorio. Cada equipamento em um laboratório de engenharia possui
certas características (como nome, número de identificação, data de aquisição) e pode ter
certos status (como “Operacional” ou “Em Manutenção”). O objetivo do projeto é modelar
essa entidade e permitir o cadastro e a visualização de alguns equipamentos.
Tarefas Detalhadas:

1. Definição da Classe EquipamentoLaboratorio:


Crie uma classe chamada EquipamentoLaboratorio.
494

• Método __init__(self, nome_equip, id_equip, data_aquisicao,


status_inicial="Operacional"):
– Este método será o construtor da sua classe. Ele deve receber self e os
seguintes parâmetros:
∗ nome_equip: Uma string para o nome do equipamento (ex: “Osciloscó-
pio Digital XYZ”).
∗ id_equip: Uma string ou número para o código de identificação único
do equipamento (ex: “OSC-001A”).
∗ data_aquisicao: Uma string para a data em que o equipamento foi
adquirido (ex: “15/03/2023”).
∗ status_inicial: Uma string para o status inicial do equipamento.
Este parâmetro deve ter um valor padrão de "Operacional".
– Dentro do __init__, crie e inicialize os seguintes atributos de instância:
[Link], [Link], self.data_compra,
self.status_atual. Atribua os valores dos parâmetros aos atributos
correspondentes.
• Método alterar_status(self, novo_status):
– Este método deve receber uma string novo_status como parâmetro (além
de self).
– Ele deve atualizar o atributo self.status_atual do objeto para o valor
de novo_status.
– O método deve imprimir uma mensagem confirmando a alteração, por
exemplo:
O status do equipamento ’[NOME DO EQUIPAMENTO]’ (ID: [ID]) foi
alterado para ’[NOVO STATUS]’.
• Método exibir_detalhes(self):
– Este método não recebe outros parâmetros além de self.
– Ele deve imprimir todas as informações do equipamento (nome, ID, data
de aquisição e status atual) de forma organizada e legível no console. Por
exemplo:
--- Detalhes do Equipamento ---
Nome: Osciloscópio Digital XYZ
ID: OSC-001A
Data de Aquisição: 15/03/2023
Status Atual: Operacional
-------------------------------

• Docstrings: Adicione docstrings claras a todos os métodos e à própria classe,


explicando seu propósito, parâmetros e o que fazem.
2. Criando e Usando Objetos EquipamentoLaboratorio no seu Script: Após a
definição da classe, no mesmo arquivo .py (mas fora da definição da classe), faça o
seguinte:
• Crie pelo menos três instâncias (objetos) diferentes da classe
EquipamentoLaboratorio. Forneça dados distintos (nome, ID, data de aqui-
sição) para cada um. Para pelo menos um dos equipamentos, especifique um
495

status_inicial diferente de “Operacional” quando o estiver criando (ex: “Em


Manutenção”).
• Para cada um dos três objetos equipamento que você criou:
– Chame o método exibir_detalhes() para mostrar suas informações ini-
ciais.
• Escolha um dos seus objetos equipamento e chame o método alterar_status()
nele para mudar seu status (por exemplo, de “Operacional” para “Em Calibração”,
ou de “Em Manutenção” para “Operacional”).
• Após alterar o status, chame novamente o método exibir_detalhes() para
aquele equipamento específico, para verificar se o status foi de fato atualizado.

3. (Opcional) Armazenando Equipamentos em uma Lista e Iterando:

• Crie uma lista Python chamada, por exemplo, inventario_do_laboratorio.


• Adicione os três (ou mais) objetos EquipamentoLaboratorio que você criou a
esta lista.
• Use um laço for para iterar sobre a lista inventario_do_laboratorio. Dentro
do laço, para cada objeto equipamento na lista, chame seu método
exibir_detalhes() para imprimir as informações de todos os equipamentos
do inventário.

Desafio Extra (Opcional):

1. Adicione um novo atributo de instância ao __init__ da classe


EquipamentoLaboratorio, como localizacao_no_lab (uma string, ex: “Bancada
3, Armário A”). Não se esqueça de atualizar o método exibir_detalhes para incluir
essa nova informação.

2. Crie um novo método de instância chamado esta_disponivel(self) que não receba


outros parâmetros. Este método deve retornar True se o self.status_atual do
equipamento for “Operacional”, e False caso contrário. Teste este método com seus
objetos.

Capítulo 13: Trabalhando (Mais) com Classes e Objetos (Exercícios


e Projeto)
Neste capítulo, aprofundamos nosso conhecimento sobre a Programação Orientada a
Objetos em Python. Diferenciamos atributos de classe e de instância, revisamos métodos
de instância, exploramos métodos especiais como __str__ e __repr__, e discutimos as
convenções para “esconder” atributos, introduzindo os conceitos de propriedades e métodos
de classe/estáticos. Os exercícios e o projeto a seguir visam solidificar esses conceitos,
permitindo que você crie classes mais robustas e funcionais.

Exercícios
1. E13.1: Atributos de Classe e Contador de Instâncias (Prático)

(a) Crie uma classe chamada DispositivoEletronico.


496

(b) Adicione um atributo de classe chamado total_dispositivos_ligados e


inicialize-o com 0. Este atributo deve rastrear quantos dispositivos (instâncias
desta classe) estão atualmente ligados.
(c) No método __init__(self, nome_dispositivo, fabricante), inicialize os
atributos de instância [Link] e [Link]. Adicione também um
atributo de instância self.esta_ligado inicializado como False.
(d) Crie um método de instância chamado ligar(self). Se o dispositivo já não
estiver ligado, este método deve definir self.esta_ligado para True e incre-
mentar o atributo de classe
DispositivoEletronico.total_dispositivos_ligados. Imprima uma men-
sagem indicando que o dispositivo foi ligado. Se já estiver ligado, apenas
informe.
(e) Crie um método de instância chamado desligar(self). Se o dispositivo estiver
ligado, este método deve definir self.esta_ligado para False e decrementar
o atributo de classe DispositivoEletronico.total_dispositivos_ligados.
Imprima uma mensagem. Se já estiver desligado, apenas informe.
(f) Crie algumas instâncias de DispositivoEletronico (ex: um celular, um note-
book, um sensor).
(g) Chame os métodos ligar() e desligar() em suas instâncias.
(h) Após cada chamada de ligar() ou desligar(), imprima o valor de
DispositivoEletronico.total_dispositivos_ligados para observar o con-
tador em ação.
(i) Desafio: Adicione um método de classe (usando o decorador @classmethod)
chamado mostrar_total_ligados(cls) que imprima uma frase como: “Atu-
almente, existem [N] dispositivos eletrônicos ligados.” Chame este método de
classe.
2. E13.2: Diferenciando Atributos de Classe e Atributos de Instância (Práti-
co/Conceitual)
Crie uma classe chamada ComponenteCircuito.
(a) Defina um atributo de classe chamado unidade_padrao_resistencia = "Ohm".
(b) No método __init__(self, tipo_componente, valor), defina os atributos
de instância [Link] (ex: "Resistor", "Capacitor") e [Link] (ex: 1000,
0.001).
(c) Crie duas instâncias: resistor1 = ComponenteCircuito("Resistor", 10000)
e capacitor1 = ComponenteCircuito("Capacitor", 0.00001).
(d) Imprima resistor1.unidade_padrao_resistencia,
capacitor1.unidade_padrao_resistencia e
ComponenteCircuito.unidade_padrao_resistencia. Todos devem mostrar
“Ohm”.
(e) Agora, modifique o atributo de classe diretamente através da classe:
ComponenteCircuito.unidade_padrao_resistencia = "Ohms (plural)".
(f) Imprima novamente resistor1.unidade_padrao_resistencia e
capacitor1.unidade_padrao_resistencia. O que aconteceu com o valor que
eles exibem?
497

(g) Agora, crie um atributo de instância especificamente para resistor1 com o


mesmo nome do atributo de classe: resistor1.unidade_padrao_resistencia
= "kOhm".
(h) Imprima resistor1.unidade_padrao_resistencia,
capacitor1.unidade_padrao_resistencia e
ComponenteCircuito.unidade_padrao_resistencia mais uma vez.
(i) Explique (em comentários no seu código) os resultados observados no item (h),
especialmente por que resistor1 mostra um valor diferente das outras duas
formas de acesso, demonstrando o conceito de sombreamento (shadowing) de
um atributo de classe por um atributo de instância.
3. E13.3: Implementando os Métodos Especiais __str__ e __repr__ (Prático)
Crie uma classe chamada Vetor2D para representar um vetor no plano cartesiano.
(a) O método __init__(self, x, y) deve inicializar os atributos de instância
self.x e self.y.
(b) Implemente o método __str__(self). Ele deve retornar uma representação
em string “amigável” do vetor, por exemplo, se x=3 e y=4, a string retornada
poderia ser "Vetor (3, 4)".
(c) Implemente o método __repr__(self). Ele deve retornar uma string que seja
uma representação “oficial” e, idealmente, que poderia ser usada para recriar o
objeto. Por exemplo, "Vetor2D(x=3, y=4)".
(d) Crie uma instância de Vetor2D, por exemplo, v1 = Vetor2D(3, 4).
(e) Teste os métodos:
• Use print(v1). Qual método é chamado?
• Use s = str(v1) e depois print(s).
• Use r = repr(v1) e depois print(r).
• Se estiver usando o console Python interativo, apenas digite v1 e pressione
Enter. Qual representação é mostrada?
(f) (Opcional) Implemente o método __len__(self) para que len(v1) retorne 2
(indicando o número de componentes do vetor).
4. E13.4: Utilizando Convenções para Atributos “Privados” e Métodos
“Internos” (Prático/Conceitual)
Crie uma classe MotorEletrico.
(a) No __init__(self, nome_modelo, potencia_cv, tensao_nominal_V), ini-
cialize:
• [Link] (público)
• self._potencia_hp (atributo “interno” para guardar a potência em HP,
use a convenção do underscore único)
• self.__numero_serie_fabricante (atributo que sofrerá “name mangling”,
inicialize com um valor string fictício).
• self.esta_funcionando (booleano, inicializado como False).
(b) Crie um método público ligar(self) que define self.esta_funcionando =
True e imprime “Motor [modelo] ligado.”.
498

(c) Crie um método público desligar(self) que define self.esta_funcionando


= False e imprime “Motor [modelo] desligado.”.
(d) Crie um método público get_especificacoes(self) que retorna uma string
formatada com o modelo e a potência em HP. Este método deve acessar
self._potencia_hp internamente.
(e) Crie um método “interno” (por convenção) chamado
_verificar_temperatura(self) que apenas imprima “Temperatura do motor
verificada internamente e está OK.”
(f) No método ligar(self), antes de ligar, chame
self._verificar_temperatura().
(g) Crie uma instância da classe MotorEletrico.
(h) Chame os métodos públicos ligar(), desligar() e get_especificacoes().
(i) De fora da classe, tente acessar e imprimir meu_motor._potencia_hp. Isso
funciona? É recomendado?
(j) De fora da classe, tente acessar e imprimir
meu_motor.__numero_serie_fabricante. O que acontece?
(k) Pesquise ou lembre-se qual seria o nome “mutilado” (mangled name) de
__numero_serie_fabricante e tente acessá-lo diretamente usando esse nome.
Isso é recomendado em código de produção?
(l) Chame o método “interno” meu_motor._verificar_temperatura() de fora da
classe. Funciona? É recomendado?

5. E13.5: Implementando uma Classe com Propriedades (@property) (Prá-


tico)
Crie uma classe TermostatoDigital.

(a) No __init__(self, temperatura_alvo_inicial=20.0), crie um atributo “in-


terno” chamado _temperatura_alvo_celsius e inicialize-o com
temperatura_alvo_inicial. (Use a convenção do underscore único).
(b) Crie uma propriedade chamada temperatura_alvo.
• Defina um método getter (decorado com @property) para
temperatura_alvo que simplesmente retorne o valor de
self._temperatura_alvo_celsius. Adicione um print("DEBUG: Getter
de temperatura_alvo chamado.") dentro dele.
(c) Crie uma instância do TermostatoDigital. Tente ler
meu_termostato.temperatura_alvo e imprima o valor. Observe se a mensa-
gem de debug do getter aparece.
(d) Agora, adicione um método setter para a propriedade temperatura_alvo
(decorado com @temperatura_alvo.setter).
• Este setter deve receber um novo_valor.
• Dentro do setter, adicione uma validação: a temperatura alvo só pode ser
definida entre 10.0°C e 30.0°C (inclusive).
• Se o novo_valor estiver dentro desta faixa, atualize
self._temperatura_alvo_celsius.
499

• Se estiver fora da faixa, imprima uma mensagem de erro (ex: “Erro: Tem-
peratura alvo deve estar entre 10.0°C e 30.0°C.”) e não altere o valor de
self._temperatura_alvo_celsius.
• Adicione um print("DEBUG: Setter de temperatura_alvo chamado com
[novo_valor].") no setter.
(e) Modifique seu __init__ para que, em vez de atribuir diretamente a
self._temperatura_alvo_celsius, ele atribua a self.temperatura_alvo =
temperatura_alvo_inicial. Isso garantirá que a validação do setter seja
executada já na criação do objeto.
(f) Teste sua classe:
• Crie uma instância com uma temperatura alvo válida (ex: 22.0).
• Tente definir meu_termostato.temperatura_alvo = 25.0. Verifique o
valor.
• Tente definir meu_termostato.temperatura_alvo = 5.0 (inválido). Veri-
fique o valor (não deve ter mudado).
• Tente definir meu_termostato.temperatura_alvo = 35.0 (inválido). Ve-
rifique o valor.
(g) (Opcional) Adicione uma propriedade somente leitura chamada
temperatura_alvo_fahrenheit (usando apenas @property, sem setter) que
calcule e retorne a temperatura alvo em Fahrenheit com base no valor armaze-
nado em Celsius.

Projeto do Capítulo 13: Sistema de Gerenciamento de Biblioteca Pessoal (com


POO)
Título do Projeto: Organizador de Biblioteca Pessoal com Classes.
Objetivos:

1. Modelar entidades do mundo real (Livro, Biblioteca) usando classes Python.

2. Praticar a definição de atributos de instância e, opcionalmente, atributos de classe.

3. Implementar o método construtor __init__ de forma eficaz.

4. Desenvolver métodos de instância que encapsulem os comportamentos dos objetos.

5. Implementar o método especial __str__ para fornecer uma representação em string


amigável dos objetos.

6. Criar uma classe que gerencia uma coleção de objetos de outra classe, demonstrando
a interação entre objetos.

7. Utilizar convenções para atributos “internos” (com underscore) onde apropriado.

Descrição: Desenvolva um sistema simples em Python para gerenciar uma coleção


pessoal de livros. O sistema será composto por duas classes principais: uma classe Livro
para representar as informações de cada livro individualmente, e uma classe Biblioteca
que irá gerenciar uma coleção de objetos Livro.
Especificações da Classe Livro:
500

• Atributos de Instância (a serem definidos no __init__):

– titulo (string): O título do livro.


– autor (string): O nome do autor do livro.
– ano_publicacao (int): O ano em que o livro foi publicado.
– numero_paginas (int): O número total de páginas do livro.
– _lido (booleano): Um atributo “interno” (use um underscore no início do nome,
ex: self._lido) para indicar se o livro já foi lido. Deve ser inicializado como
False por padrão no construtor.

• Métodos de Instância:

– marcar_como_lido(self): Este método deve alterar o valor do atributo _lido


para True.
– marcar_como_nao_lido(self): Este método deve alterar o valor do atributo
_lido para False.
– obter_status_leitura(self): Este método deve retornar a string “Lido” se
_lido for True, e a string “Não Lido” se _lido for False.
– __str__(self): Este método especial deve retornar uma string formatada repre-
sentando o livro, incluindo todas as suas informações (título, autor, ano, páginas)
e o seu status de leitura (obtido através do método obter_status_leitura()).
Exemplo de formato:
"’O Guia do Mochileiro das Galáxias’ por Douglas Adams (1979) -
224 páginas [Não Lido]"

Especificações da Classe Biblioteca:


• Atributos de Instância:

– nome_da_biblioteca (string): O nome da biblioteca (ex: “Biblioteca Pessoal


do Alan”). Definido no __init__.
– _colecao_de_livros (lista): Uma lista para armazenar os objetos Livro que
pertencem a esta biblioteca. Deve ser inicializada como uma lista vazia no
__init__. Use um underscore para indicar que é para uso interno.

• Atributo de Classe (Opcional, para Prática):

– generos_suportados = ["Ficção Científica", "Fantasia", "Técnico",


"Biografia", "História", "Romance"] (uma lista de strings). Este atributo
de classe pode ser usado em futuras expansões (não é obrigatório usá-lo ativa-
mente no projeto base).

• Métodos de Instância:

– adicionar_livro(self, objeto_livro):
∗ Recebe um objeto_livro como parâmetro.
∗ Verificação de Tipo: Antes de adicionar, verifique se objeto_livro é
realmente uma instância da classe Livro (use isinstance(objeto_livro,
Livro)). Se não for, imprima uma mensagem de erro e não adicione.
501

∗ Se for um objeto Livro válido, adicione-o à lista interna _colecao_de_livros.


∗ Imprima uma mensagem confirmando que o livro foi adicionado à biblioteca.
– remover_livro_por_titulo(self, titulo_do_livro_para_remover):
∗ Recebe uma string titulo_do_livro_para_remover.
∗ Percorra a lista _colecao_de_livros. Se encontrar um objeto Livro cujo
atributo titulo corresponda (ignore diferenças de maiúsculas/minúsculas
e espaços extras na busca), remova esse objeto da lista.
∗ Imprima uma mensagem indicando se o livro foi removido com sucesso ou
se não foi encontrado. (Cuidado ao remover de uma lista enquanto itera
sobre ela; pode ser mais fácil encontrar o índice e usar pop(), ou construir
uma nova lista sem o item a ser removido).
– buscar_livros_por_autor(self, nome_do_autor):
∗ Recebe uma string nome_do_autor.
∗ Crie uma nova lista contendo todos os objetos Livro da _colecao_de_livros
cujo atributo autor corresponda (parcialmente ou totalmente, e de forma
insensível a maiúsculas/minúsculas) ao nome_do_autor fornecido.
∗ Retorne esta lista de livros encontrados (pode ser uma lista vazia se nenhum
for encontrado).
– listar_todos_os_livros(self):
∗ Se a _colecao_de_livros estiver vazia, imprima “A biblioteca ’[Nome da
Biblioteca]’ está vazia.”
∗ Caso contrário, imprima “— Livros na Biblioteca ’[Nome da Biblioteca]’
—”. Em seguida, itere sobre a _colecao_de_livros e, para cada objeto
Livro, use seu método __str__() (ou seja, apenas print(livro)) para
exibir seus detalhes.
– listar_livros_por_status(self, lido=True):
∗ Recebe um parâmetro booleano lido (com valor padrão True).
∗ Se lido for True, lista apenas os livros da coleção que estão marcados
como lidos.
∗ Se lido for False, lista apenas os livros marcados como não lidos.
∗ A impressão deve ser similar à de listar_todos_os_livros(), mas fil-
trada.

Script Principal de Teste (Fora das Definições de Classe):

1. Crie pelo menos três objetos Livro diferentes, com informações variadas.

2. Crie um objeto Biblioteca, dando um nome a ela.

3. Adicione os objetos Livro à sua instância de Biblioteca usando o método


adicionar_livro().

4. Teste todas as funcionalidades da sua classe Biblioteca:

• Liste todos os livros.


• Busque livros por um autor específico e imprima os resultados.
502

• Marque um ou dois livros como lidos usando o método marcar_como_lido()


dos objetos Livro (você pode precisar acessar os livros da biblioteca para fazer
isso, por exemplo, pegando o primeiro livro da lista interna da biblioteca).
• Liste os livros lidos.
• Liste os livros não lidos.
• Remova um livro da biblioteca pelo título.
• Liste todos os livros novamente para ver o efeito da remoção.

Desafio Extra (Opcional):

1. Propriedades na Classe Livro: Modifique a classe Livro. Em vez de acessar


_lido diretamente para marcar como lido/não lido, crie uma propriedade chamada
lido.

• O getter (@property def lido(self)) retornaria o valor de self._lido.


• O setter (@[Link] def lido(self, status)) receberia um valor boole-
ano status e o atribuiria a self._lido. No setter, você poderia adicionar um
print() para indicar que o status de leitura foi alterado.

Adapte os métodos marcar_como_lido e marcar_como_nao_lido para usar este


novo setter da propriedade (ex: [Link] = True).

2. Contador de Livros na Biblioteca (Atributo de Classe): Adicione um atributo


de classe à Biblioteca para contar o número total de livros em todas as bibliotecas
criadas, ou um método de classe que retorne uma descrição geral do tipo de objeto
que a biblioteca armazena.

3. Métodos Estáticos Utilitários: Pense se haveria alguma função utilitária relaci-


onada a livros ou bibliotecas que não precisaria de self ou cls e poderia ser um
método estático (ex: uma função que valida um formato de ISBN, embora isso seja
mais complexo).

Capítulo 14: Herança e Polimorfismo (Exercícios e Projeto)


Neste capítulo, exploramos dois dos pilares mais poderosos da Programação Orientada
a Objetos: a herança, que nos permite criar hierarquias de classes e reutilizar código,
e o polimorfismo, que confere flexibilidade ao modo como objetos de diferentes classes
respondem a chamadas de métodos. Os exercícios e o projeto a seguir têm como objetivo
solidificar seu entendimento sobre como criar subclasses, sobrescrever e estender métodos,
e aplicar o polimorfismo.

Exercícios
1. E14.1: Conceitos de Herança e Polimorfismo (Conceitual)
Responda com suas palavras às seguintes perguntas, baseando-se no conteúdo do
Capítulo 14:

(a) O que é herança em Programação Orientada a Objetos? Qual é a principal


vantagem ou propósito de usar herança?
503

(b) Diferencie os termos superclasse (ou classe mãe/base) e subclasse (ou classe
filha/derivada) no contexto da herança.
(c) O que significa sobrescrever um método (method overriding)? Por que essa
capacidade é útil ao trabalhar com herança?
(d) Qual é o propósito da função super() em Python, especialmente quando usada
dentro do método __init__ de uma subclasse?
(e) Explique o conceito de polimorfismo em POO. Dê um exemplo simples (pode
ser apenas conceitual, sem código) de como o polimorfismo poderia ser aplicado
em uma situação do mundo real modelada com classes.
(f) O que é “Duck Typing” em Python e como ele se relaciona com o conceito de
polimorfismo?

2. E14.2: Herança Simples – Modelando Veículos (Prático)

(a) Crie uma classe base chamada Veiculo. Esta classe deve ter um método
__init__(self, marca, modelo, ano) que inicialize os atributos de instância
[Link], [Link] e [Link].
(b) Adicione um método chamado exibir_informacoes(self) à classe Veiculo
que imprima a marca, o modelo e o ano do veículo de forma formatada.
(c) Adicione também dois métodos genéricos à classe Veiculo: acelerar(self)
que imprima a mensagem “Veículo genérico acelerando...” e frear(self) que
imprima “Veículo genérico freando...”.
(d) Crie uma subclasse chamada Carro que herde da classe Veiculo.
(e) O método __init__ da classe Carro deve aceitar os parâmetros marca, modelo,
ano e um novo atributo específico para carros: numero_portas. Dentro deste
__init__, certifique-se de chamar o construtor da superclasse
(super().__init__(...)) para inicializar os atributos herdados, e então inici-
alize self.numero_portas.
(f) Na classe Carro, sobrescreva (override) o método acelerar(self) para que
ele imprima uma mensagem mais específica, como “Carro [modelo do carro]
acelerando: Vrumm Vrumm!”.
(g) Adicione um novo método exclusivo à classe Carro chamado
abrir_porta_malas(self) que imprima uma mensagem como “Porta-malas
do carro [modelo do carro] foi aberto.”.
(h) Crie uma instância da classe Veiculo (ex: veiculo1 = Veiculo("FabricanteX",
"ModeloY", 2020)) e uma instância da classe Carro
(ex: meu_carro = Carro("Toyota", "Corolla", 2023, 4)).
(i) Para ambos os objetos (veiculo1 e meu_carro), chame os métodos
exibir_informacoes(), acelerar() e frear(). Observe as diferenças na
saída do método acelerar().
(j) Chame o método abrir_porta_malas() apenas no objeto meu_carro. Tente
chamá-lo no objeto veiculo1. O que acontece? (Comente a linha que daria
erro).

3. E14.3: Herança e Especialização – Figuras Geométricas (Prático)


504

(a) Crie uma classe base chamada FiguraGeometrica.


• Seu __init__(self, nome_da_figura) deve inicializar o atributo
[Link].
• Adicione um método calcular_area(self). Na classe base, este método
pode simplesmente usar pass ou, para ser mais explícito sobre a neces-
sidade de sobrescrita, raise NotImplementedError("Subclasses devem
implementar o método calcular_area.").
• Adicione um método descrever(self) que imprima uma frase como: “Eu
sou uma figura geométrica chamada ’[nome da figura]”.’
(b) Crie uma subclasse chamada Retangulo que herde de FiguraGeometrica.
• Seu __init__(self, nome_retangulo, base, altura) deve chamar o
construtor da superclasse (usando super()) para inicializar o nome, e
então inicializar os atributos de instância [Link] e [Link].
• Sobrescreva o método calcular_area(self) na classe Retangulo para
que ele retorne a área correta (base * altura).
(c) Crie uma subclasse chamada Circulo que herde de FiguraGeometrica.
• Seu __init__(self, nome_circulo, raio) deve chamar o construtor da
superclasse e inicializar o atributo de instância [Link].
• Sobrescreva o método calcular_area(self) na classe Circulo para que
ele retorne a área correta (π × raio2 ). Você precisará importar o módulo
math para usar [Link].
(d) Crie uma instância de Retangulo (ex: Retangulo("Meu Retângulo", 10, 5))
e uma instância de Circulo (ex: Circulo("Meu Círculo", 7)).
(e) Para cada objeto criado, chame o método descrever() e o método
calcular_area(), imprimindo a área calculada.

4. E14.4: Polimorfismo em Ação com Animais (Prático)


Reutilize as classes Animal, Cachorro e Gato dos exemplos do capítulo ou da
Seção 14.4 (onde Cachorro e Gato herdam de Animal e sobrescrevem o método
fazer_som()).

(a) Crie uma lista chamada meus_animais_estimacao. Adicione a esta lista pelo
menos:
• Duas instâncias diferentes de Cachorro.
• Duas instâncias diferentes de Gato.
• Uma instância da classe Animal genérica.
(b) Escreva um laço for que itere sobre cada animal na lista
meus_animais_estimacao.
(c) Dentro do laço, para cada animal, chame o método animal.fazer_som().
(d) Observe a saída. Como a mesma chamada de método (animal.fazer_som())
produziu resultados diferentes dependendo do tipo real do objeto na variável
animal? Explique brevemente este comportamento polimórfico.

5. E14.5: Entendendo Duck Typing (Prático/Conceitual)


505

(a) Crie duas classes, PatoReal e RoboPato, que não herdam uma da outra nem
de uma superclasse comum.
• A classe PatoReal deve ter um método fazer_quack(self) que imprima
“Pato Real faz: Quack! Quack!” e um método nadar(self) que imprima
“Pato Real está nadando graciosamente.”
• A classe RoboPato deve ter um método fazer_quack(self) que imprima
“Robô Pato emite: Quack! (som sintetizado)” e um método nadar(self)
que imprima “Robô Pato ativa hélices e nada.”
(b) Crie uma função chamada interagir_com_pato(objeto_pato). Esta função
deve receber um objeto como parâmetro. Dentro da função, ela deve tentar
chamar objeto_pato.fazer_quack() e objeto_pato.nadar().
(c) Crie uma instância de PatoReal e uma instância de RoboPato.
(d) Chame a função interagir_com_pato() passando primeiro o objeto PatoReal
e depois passando o objeto RoboPato.
(e) Observe se a função interagir_com_pato() funcionou para ambos os tipos de
objeto, mesmo eles não tendo uma relação de herança. Explique por que isso
acontece em Python, relacionando com o conceito de Duck Typing ("Se anda
como um pato e faz quack como um pato, então é um pato.").
(f) O que aconteceria se você criasse uma terceira classe, digamos
CachorroDeBorracha, que tivesse apenas um método apertar() mas não
fazer_quack() nem nadar(), e você passasse um objeto dessa classe para
interagir_com_pato()? (Não precisa codificar esta parte, apenas prever e
explicar o resultado em termos de Duck Typing e a ausência dos métodos
esperados).

Projeto do Capítulo 14: Mini Sistema de Hierarquia de Instrumentos Musicais


e Orquestra Polimórfica
Título do Projeto: Orquestra de Instrumentos Musicais Virtuais.
Objetivos:

1. Praticar a criação de uma hierarquia de classes significativa usando herança simples.

2. Implementar e sobrescrever métodos em subclasses para especializar comportamentos.

3. Adicionar atributos e métodos específicos às subclasses, demonstrando a extensão de


funcionalidades.

4. Demonstrar o conceito de polimorfismo ao tratar objetos de diferentes subclasses de


forma uniforme através de uma interface comum da superclasse.

5. Utilizar a função super() para chamar métodos da superclasse de forma eficaz.

Descrição: Você modelará diferentes tipos de instrumentos musicais usando Progra-


mação Orientada a Objetos. Haverá uma classe base InstrumentoMusical que definirá
características e comportamentos comuns a todos os instrumentos. A partir dela, você cri-
ará subclasses para tipos específicos de instrumentos (por exemplo, InstrumentoDeCorda,
InstrumentoDeSopro, InstrumentoDePercussao). Cada tipo de instrumento terá uma
506

forma particular de "tocar"(produzir som). Finalmente, você criará uma "orquestra"(uma


lista de diferentes objetos de instrumento) e fará todos eles tocarem.
Classes a Implementar:

1. Classe InstrumentoMusical (Superclasse):

• __init__(self, nome_instrumento, marca="Desconhecida",


familia="Indefinida"):
– Deve inicializar os atributos de instância: [Link] (string), [Link]
(string, com valor padrão), [Link] (string, com valor padrão).
• Método afinar(self):
– Deve imprimir uma mensagem genérica como: “Afinando o instrumento
[nome do instrumento]... (afinação padrão).”
• Método tocar(self):
– Deve imprimir uma mensagem genérica indicando que o instrumento está
produzindo um som, por exemplo: “O instrumento [nome do instrumento]
produz um som musical genérico.”
– Este método é um candidato ideal para ser sobrescrito pelas subclasses.
• Método __str__(self):
– Deve retornar uma string formatada descrevendo o instrumento, por exem-
plo:
"Instrumento: Violão (Marca: Yamaha, Família: Corda)".

2. Classe InstrumentoDeCorda(InstrumentoMusical) (Subclasse):

• __init__(self, nome_instrumento, marca, numero_de_cordas,


material_das_cordas="Nylon"):
– Deve chamar o construtor da superclasse (super().__init__(...)) pas-
sando o nome, marca e definindo a família como "Corda".
– Deve inicializar os atributos de instância específicos: self.numero_cordas
(int) e self.material_cordas (string, com valor padrão).
• Método tocar(self) (Sobrescrito):
– Deve imprimir um som característico de instrumento de corda, mencionando
o nome e o material das cordas. Ex: “O [nome] com cordas de [material]
soa: Plim! Plim! Diiiing!”
• Método Específico (Opcional): trocar_corda(self, qual_corda):
– Imprime uma mensagem como: “Trocando a corda número [qual_corda]
do [nome].”

3. Classe InstrumentoDeSopro(InstrumentoMusical) (Subclasse):

• __init__(self, nome_instrumento, marca, material_do_corpo="Metal",


tipo_de_chave="Pistão"):
– Chame super().__init__(...) definindo a família como "Sopro".
– Inicialize os atributos específicos: self.material_corpo e self.tipo_chave.
507

• Método tocar(self) (Sobrescrito):


– Imprima um som característico. Ex: “O [nome] ([material do corpo]) soa:
Fooooooooo! Paaaaaam!”
• Método Específico (Opcional): limpar_bocal(self):
– Imprime: “Limpando o bocal do [nome].”

4. Classe InstrumentoDePercussao(InstrumentoMusical) (Subclasse):

• __init__(self, nome_instrumento, marca, tem_pele_membrana=True):


– Chame super().__init__(...) definindo a família como "Percussão".
– Inicialize self.tem_pele.
• Método tocar(self) (Sobrescrito):
– Se self.tem_pele for True, imprima algo como “O [nome] soa: BUM!
TUM! TÁ!”.
– Caso contrário (ex: um xilofone, pratos), imprima algo como “O [nome]
soa: CLANG! TING! Xilo!”.

Script Principal de Teste e Demonstração do Polimorfismo:

1. No seu script principal (fora das definições de classe):

• Crie pelo menos uma instância de cada uma das suas subclasses de instrumento
(ex: um violão, uma flauta, uma bateria).
• Crie também uma instância da classe InstrumentoMusical genérica
(ex: InstrumentoMusical("Apito Desconhecido")).
• Adicione todos esses objetos de instrumento a uma lista chamada minha_orquestra.

2. Demonstre o Polimorfismo:

• Escreva um laço for que itere sobre cada instrumento na lista minha_orquestra.
• Dentro do laço, para cada instrumento:
– Imprima o objeto instrumento diretamente (isso chamará o método __str__
que você definiu/herdou).
– Chame o método [Link]().
– Chame o método [Link]().
– Adicione um print(-"* 30) para separar visualmente a saída de cada
instrumento.

3. Teste Métodos Específicos:

• Fora do laço, pegue uma instância específica de InstrumentoDeCorda e chame


seu método trocar_corda() (se você o implementou).
• Faça o mesmo para um método específico de InstrumentoDeSopro.

Desafio Extra (Opcional):


508

1. Herança Múltipla (com cautela): Pense em uma classe InstrumentoEletronico


que poderia ter um atributo voltagem_operacao e um método ligar_na_tomada().
Agora, crie uma classe como TecladoEletronico que herde tanto de
InstrumentoDeCorda (se pensar nas teclas como análogas a cordas de um piano)
quanto de InstrumentoEletronico. Como o método tocar() e o __init__()
seriam afetados? Como você usaria super() neste caso? (Lembre-se que herança
múltipla pode ser complexa, este é mais um exercício de pensamento e exploração).

2. Variedade nos Sons: Torne o método tocar() de cada instrumento um pouco


mais interessante, talvez fazendo com que ele retorne uma string representando o
som, ou até mesmo usando o módulo random para adicionar uma pequena variação
ao som produzido a cada chamada.

Capítulo 15: Tópicos Avançados em POO (Exercícios e Projeto)


Neste capítulo opcional, exploramos conceitos mais avançados da Programação Orien-
tada a Objetos em Python que nos permitem criar classes com maior controle, flexibilidade
e capacidade de definir contratos claros para hierarquias. Vimos propriedades (@property)
para gerenciar acesso a atributos, métodos de classe (@classmethod), métodos estáticos
(@staticmethod) e classes base abstratas (ABCs) com métodos abstratos. Os exercícios e
o projeto a seguir visam proporcionar uma prática mais aprofundada desses tópicos.

Exercícios
1. E15.1: Propriedades (@property) para Validação e Atributos Calculados
(Prático)
Crie uma classe chamada CirculoAvançado.

(a) No método __init__(self, raio_inicial), armazene o valor do raio em um


atributo “interno” chamado _raio (use um underscore no início).
(b) Crie uma propriedade chamada raio usando o decorador @property para o
método getter. Este getter deve simplesmente retornar o valor de self._raio.
(c) Crie um setter para a propriedade raio (usando @[Link]). Este setter
deve receber um novo_raio. Dentro do setter, adicione uma validação: se
novo_raio for menor ou igual a zero, levante um ValueError com uma mensa-
gem apropriada (ex: “O raio deve ser um número positivo.”). Caso contrário
(se válido), atualize o valor de self._raio.
(d) Modifique o __init__ para que, em vez de atribuir diretamente a self._raio,
ele atribua a [Link] = raio_inicial. Isso fará com que a validação do
setter seja executada já na criação do objeto.
(e) Crie uma propriedade somente leitura chamada area (usando apenas @property,
sem um setter correspondente). Este método getter deve calcular e retornar a
área do círculo (π × raio2 ). Importe math e use [Link].
(f) Crie uma propriedade somente leitura chamada diametro. Este getter deve
calcular e retornar o diâmetro do círculo (2 × raio).
(g) Teste sua classe:
509

• Tente criar uma instância de CirculoAvançado com um raio negativo (ex:


-5). Use um bloco try-except ValueError para capturar o erro e imprimir
a mensagem.
• Crie uma instância com um raio válido (ex: CirculoAvançado(10)).
• Acesse e imprima as propriedades raio, diametro e area deste objeto.
• Tente atribuir um novo valor válido à propriedade raio (ex: meu_circulo.raio
= 7). Verifique se diametro e area refletem essa mudança.
• Tente atribuir um valor negativo à propriedade raio (ex: meu_circulo.raio
= -2). Use try-except para capturar o ValueError.
• Tente atribuir um valor à propriedade area (ex: meu_circulo.area =
100). O que acontece? Por quê? (Comente a linha que causa erro).
2. E15.2: Métodos de Classe (@classmethod) como Construtores Alternativos
(Prático)
Crie uma classe chamada ProdutoQuimico.
• O método __init__(self, nome_comum, formula_molecular,
massa_molar_g_mol) deve inicializar os atributos de instância correspondentes.
• Adicione um método de instância __str__(self) que retorne uma representação
amigável do produto (ex: “Água (H2O) - Massa Molar: 18.015 g/mol”).
Agora, adicione os seguintes métodos de classe como construtores alternativos:
(a) Crie um método de classe @classmethod chamado de_formula_e_massa(cls,
formula, massa). Este método deve receber a fórmula molecular (string) e
a massa molar (float). Para o nome comum, ele pode usar a própria fórmula
molecular como padrão se nenhum outro nome for mais apropriado. Ele
deve retornar uma nova instância da classe ProdutoQuimico (usando return
cls(nome_derivado, formula, massa)).
(b) Crie outro método de classe @classmethod chamado criar_agua(cls). Este
método não recebe argumentos (além de cls) e deve retornar diretamente uma
instância de ProdutoQuimico representando a água (Nome: “Água”, Fórmula:
“H2O”, Massa Molar: ≈ 18.015 g/mol).
(c) Crie um terceiro método de classe @classmethod chamado de_dicionario(cls,
dados_dict). Este método deve receber um dicionário como {’nome’: ’Ácido
Sulfúrico’, ’formula’: ’H2SO4’, ’massa_molar’: 98.079}. Ele deve
extrair os valores do dicionário e retornar uma instância de ProdutoQuimico.
Teste sua classe:
• Crie um produto químico usando o __init__ normal.
• Crie um produto químico usando
ProdutoQuimico.de_formula_e_massa("NaCl", 58.44).
• Crie uma instância de água usando ProdutoQuimico.criar_agua().
• Crie um produto químico usando ProdutoQuimico.de_dicionario(...) com
um dicionário apropriado.
• Imprima cada um dos objetos criados para verificar se foram inicializados
corretamente (o __str__ ajudará aqui).
510

3. E15.3: Métodos Estáticos (@staticmethod) para Funções Utilitárias de


uma Classe (Prático)
Crie uma classe chamada UtilitariosEngenharia. Esta classe não precisará de um
método __init__ se todos os seus métodos forem estáticos e ela não tiver atributos
de instância.

(a) Adicione um método estático @staticmethod chamado


converter_fahrenheit_para_celsius(temp_f). Este método deve receber
uma temperatura em Fahrenheit e retornar a temperatura equivalente em
Celsius (C = (F − 32) × 5/9).
(b) Adicione outro método estático @staticmethod chamado
calcular_delta_v(velocidade_final, velocidade_inicial). Este método
deve retornar a variação de velocidade.
(c) Adicione um atributo de classe (não de instância) à UtilitariosEngenharia
chamado ACELERACAO_GRAVIDADE_TERRA = 9.80665 (m/s²).
(d) Crie um método estático @staticmethod chamado calcular_peso(massa_kg).
Este método deve receber a massa em kg e retornar o peso em Newtons na
Terra, usando o atributo de classe ACELERACAO_GRAVIDADE_TERRA (lembre-se
que para acessar um atributo de classe de um método estático, você precisa usar
o nome da classe: UtilitariosEngenharia.ACELERACAO_GRAVIDADE_TERRA).

Teste seus métodos estáticos chamando-os diretamente através da classe:

• celsius =
UtilitariosEngenharia.converter_fahrenheit_para_celsius(68)
• delta_v = UtilitariosEngenharia.calcular_delta_v(100, 20)
• peso_obj = UtilitariosEngenharia.calcular_peso(70)
• Imprima os resultados.

4. E15.4: Definindo e Usando Classes Abstratas e Métodos Abstratos (Prá-


tico)
Você irá modelar um sistema simples para diferentes tipos de atuadores em um
sistema de controle.

(a) Importe ABC e abstractmethod do módulo abc.


(b) Defina uma classe base abstrata chamada AtuadorBase que herde de ABC.
(c) No __init__(self, id_atuador, tipo_atuador), inicialize os atributos
[Link] = id_atuador e [Link] = tipo_atuador.
(d) Defina um método abstrato @abstractmethod chamado
ativar(self, parametro_controle).
(e) Defina outro método abstrato @abstractmethod chamado desativar(self).
(f) Defina um método concreto (não abstrato) chamado obter_informacoes(self)
que retorne uma string formatada com o ID e o tipo do atuador.
(g) Crie duas subclasses concretas que herdem de AtuadorBase: MotorPasso e
ValvulaSolenoide.
• MotorPasso:
511

– No __init__(self, id_atuador, resolucao_passos), chame


super().__init__() passando o ID e o tipo como “Motor de Passo”.
Armazene resolucao_passos.
– Implemente o método ativar(self, numero_de_passos): deve im-
primir algo como “Motor [ID] movendo [numero_de_passos] passos
com resolução [resolucao].”
– Implemente o método desativar(self): deve imprimir “Motor [ID]
desenergizado.”
• ValvulaSolenoide:
– No __init__(self, id_atuador, tipo_fluido), chame
super().__init__() passando o ID e o tipo como “Válvula Solenoide”.
Armazene tipo_fluido.
– Implemente o método ativar(self, estado_abertura): onde
estado_abertura pode ser um booleano (True para abrir, False para
fechar, ou um percentual de 0 a 100). Imprima uma mensagem apro-
priada.
– Implemente o método desativar(self): deve chamar
[Link](False) (ou 0%) e imprimir “Válvula [ID] garantidamente
fechada.”
(h) Tente criar uma instância direta de AtuadorBase. O que acontece? (Comente
para não dar erro).
(i) Crie instâncias de MotorPasso e ValvulaSolenoide.
(j) Para cada instância, chame obter_informacoes(), ativar() (com argumentos
apropriados) e desativar().

Projeto do Capítulo 15: Sistema de Validação de Dados de Formulário com


Classes Avançadas
Título do Projeto: Framework Simplificado de Validação de Campos de Formulário.
Objetivos:

1. Praticar o uso de classes abstratas (ABC, @abstractmethod) para definir um “contrato”


(interface) para diferentes tipos de validadores de campo.

2. Implementar subclasses concretas que herdam da classe abstrata e fornecem lógicas


de validação específicas (ex: para texto, números, e-mail).

3. Utilizar propriedades (@property, @*.setter) para gerenciar o acesso e a validação


de atributos de configuração dos validadores (ex: comprimento mínimo/máximo
para texto, valor mínimo/máximo para números).

4. (Opcional) Utilizar métodos de classe ou estáticos para funcionalidades auxiliares ou


de fábrica, se aplicável e se parecer natural para o design.

5. Criar uma classe Formulario que possa agregar e gerenciar múltiplos campos, cada
um associado a um objeto validador, e que possa validar um conjunto de dados
submetidos contra esses validadores.
512

Descrição Detalhada: Você desenvolverá um pequeno framework em Python para


validar dados que tipicamente seriam submetidos através de um formulário (web ou de
aplicação). A ideia central é ter uma classe base abstrata ValidadorCampo que define
a interface que todos os validadores de campo devem seguir. A partir dela, você criará
subclasses concretas para validar tipos específicos de dados, como texto (com restrições de
comprimento), números (com restrições de intervalo) e, opcionalmente, formatos específicos
como e-mail. Finalmente, uma classe Formulario usará esses validadores para verificar
um dicionário de dados submetidos.
Classes a Implementar:

1. Classe Abstrata ValidadorCampo(ABC):

• Importe ABC e abstractmethod do módulo abc.


• __init__(self, nome_do_campo, eh_obrigatorio=True):
– Deve armazenar nome_do_campo (string) como um atributo público.
– Deve armazenar eh_obrigatorio (booleano) em um atributo “interno” (ex:
self._eh_obrigatorio).
• Propriedade eh_obrigatorio:
– Crie um getter (@property) para eh_obrigatorio que retorne
self._eh_obrigatorio.
– Crie um setter (@eh_obrigatorio.setter) para eh_obrigatorio que re-
ceba um novo_valor e levante um TypeError se novo_valor não for um
booleano.
• Método Abstrato validar(self, valor_do_campo):
– Decorado com @abstractmethod.
– Este método receberá o valor_do_campo a ser validado.
– Na classe base, pode apenas conter pass ou raise NotImplementedError().
– As subclasses concretas devem implementar este método. Ele deve retornar
uma tupla: (True, None) se o valor for válido, ou (False, "mensagem
de erro específica") se o valor for inválido.
• Método __str__(self):
– Deve retornar uma string descritiva, como: “Validador para o campo
obrigatório ’[nome_do_campo]”. (Ajuste a mensagem se eh_obrigatorio
for False).

2. Subclasse Concreta ValidadorTexto(ValidadorCampo):

• Herda de ValidadorCampo.
• __init__(self, nome_do_campo, eh_obrigatorio=True, comprimento_min
=None, comprimento_max=None):
– Chame super().__init__(nome_do_campo, eh_obrigatorio).
– Armazene comprimento_min e comprimento_max em atributos internos
(ex: self._comprimento_min).
• Propriedades para comprimento_min e comprimento_max:
– Crie getters e setters para eles.
513

– Nos setters, valide se os valores são inteiros não negativos (ou None).
– O setter de comprimento_max também deve garantir que, se ambos min e
max estiverem definidos, max não seja menor que min. Levante ValueError
se as condições não forem atendidas.
• Sobrescreva o método validar(self, valor_do_campo):
– Primeiro, verifique se o campo é obrigatório e se o valor é None ou uma
string vazia (após strip()). Se for obrigatório e vazio, retorne (False,
"Este campo é obrigatório.").
– Se um valor foi fornecido (ou se não é obrigatório e está vazio), verifique se
é uma string (use isinstance()). Se não for (e não for None para campos
não obrigatórios), retorne (False, "O valor deve ser texto.").
– Se for uma string, verifique as restrições de comprimento_min e
comprimento_max (se definidas). Retorne (False, "mensagem de erro
apropriada") se violadas.
– Se todas as verificações passarem, retorne (True, None).

3. Subclasse Concreta ValidadorNumero(ValidadorCampo):

• Herda de ValidadorCampo.
• __init__(self, nome_do_campo, eh_obrigatorio=True, tipo_esperado=
float, valor_min=None, valor_max=None):
– Chame super().__init__(...).
– Armazene tipo_esperado (que deve ser int ou float), valor_min,
valor_max.
• Propriedades para valor_min e valor_max (opcional, mas bom para
prática): Com validação.
• Sobrescreva o método validar(self, valor_do_campo):
– Verifique se é obrigatório e se está vazio/None (similar ao ValidadorTexto).
– Tente converter o valor_do_campo para o self.tipo_esperado. Se a
conversão falhar (pode usar try-except ValueError), retorne (False,
"Não é um número válido do tipo esperado.").
– Se a conversão for bem-sucedida, verifique as restrições de valor_min e
valor_max (se definidas).
– Retorne (True, None) ou (False, "mensagem de erro").

4. (Opcional) Subclasse ValidadorEmail(ValidadorTexto):

• Herda de ValidadorTexto.
• O __init__ pode simplesmente chamar o da superclasse.
• Sobrescreva validar(self, valor_do_campo):
– Primeiro, chame
valido, mensagem = super().validar(valor_do_campo). Se não for
valido, retorne esses resultados.
514

– Se passou na validação de texto base, adicione uma verificação simples


de formato de e-mail (ex: se contém um "@" e se tem um "." após o
"@"). Esta não precisa ser uma validação de e-mail perfeita, apenas uma
demonstração.
– Retorne (True, None) ou (False, "Formato de e-mail inválido.").

5. Classe Formulario:

• __init__(self):
– Crie um atributo interno (ex: self._regras_de_validacao) que seja um
dicionário para armazenar os validadores, onde a chave será o nome_do_campo
e o valor será o objeto ValidadorCampo correspondente.
• Método adicionar_validador(self, validador_obj):
– Recebe um objeto validador_obj (que deve ser uma instância de
ValidadorCampo ou suas subclasses).
– Adiciona este validador ao dicionário self._regras_de_validacao, usando
o validador_obj.nome_campo como chave.
• Método validar(self, dados_submetidos_dict):
– Recebe um dicionário dados_submetidos_dict no formato {nome_campo1:
valor_submetido1, nome_campo2: valor_submetido2, ...}.
– Crie um dicionário vazio chamado erros_encontrados.
– Itere sobre todos os validadores armazenados em
self._regras_de_validacao. Para cada nome_campo e validador_obj
correspondente:
∗ Obtenha o valor_submetido para o nome_campo a partir de
dados_submetidos_dict (use .get() para obter None se o campo não
foi submetido).
∗ Se o validador_obj.eh_obrigatorio for True e o valor_submetido
for None ou uma string vazia (após strip(), se for string), adicione
uma mensagem de erro para este campo em erros_encontrados.
∗ Se um valor foi submetido (ou se não é obrigatório e está vazio mas o
validador lida com isso), chame
validador_obj.validar(valor_submetido).
∗ Se a validação retornar False (ou seja, valido == False), adicione a
mensagem_de_erro retornada ao dicionário erros_encontrados para
aquele nome_campo.
– Ao final, retorne o dicionário erros_encontrados. Se este dicionário estiver
vazio, significa que todos os dados são válidos.

Script Principal de Teste (Fora das Definições de Classe):

1. Crie instâncias dos seus diferentes validadores (pelo menos um ValidadorTexto e


um ValidadorNumero, e o ValidadorEmail se você o implementou). Configure-os
com diferentes regras (obrigatório, comprimentos, intervalos, etc.).

2. Crie uma instância da classe Formulario.


515

3. Adicione os validadores que você criou ao objeto Formulario usando o método


adicionar_validador().

4. Crie alguns dicionários de “dados submetidos” para simular diferentes cenários:

• Um dicionário com todos os dados válidos.


• Um dicionário com alguns campos obrigatórios faltando.
• Um dicionário com dados que violam as regras de validação (ex: texto muito
curto, número fora do intervalo, e-mail mal formatado).

5. Para cada dicionário de dados submetidos, chame o método


textttmeu_formulario.validar(...) e imprima os resultados (o dicionário de erros).
Verifique se os erros corretos são reportados para os dados inválidos e se um dicionário
vazio de erros é retornado para os dados válidos.

Desafio Extra (Opcional):

1. Métodos de Classe/Estáticos em Validadores: Existe alguma lógica de va-


lidação genérica ou função utilitária (ex: uma função para verificar se uma string
representa um inteiro, ou para limpar uma string de forma padrão) que poderia ser
implementada como um método estático em ValidadorCampo ou em uma de suas
subclasses, para ser reutilizada?

2. Validador de Senha Complexa: Crie uma subclasse


ValidadorSenha(ValidadorTexto) que, além das verificações de texto, também
verifique se a senha atende a critérios de complexidade (ex: comprimento mínimo,
presença de letras maiúsculas, minúsculas, números e/ou símbolos especiais).

3. Mensagens de Erro Mais Detalhadas nas Propriedades: Nos setters das


propriedades (ex: para comprimento_min em ValidadorTexto), em vez de apenas
levantar ValueError, levante exceções personalizadas mais específicas (que você
pode definir, herdando de ValueError ou Exception) se quiser praticar o conteúdo
da Seção 17.7.

Parte V: Manipulação de Arquivos e Tratamento de Erros


Capítulo 16: Leitura e Escrita de Arquivos (Exercícios e Projeto)
Neste capítulo, exploramos como os programas Python podem interagir com o sistema
de arquivos para ler dados de arquivos existentes e para escrever novos dados, garantindo a
persistência da informação. Vimos a importância da função open(), dos diferentes modos
de abertura (’r’, ’w’, ’a’, etc.), do parâmetro encoding, e da forma segura de trabalhar
com arquivos usando a instrução with. Também aprendemos sobre os métodos de leitura
(read(), readline(), readlines(), iteração direta) e escrita (write(), writelines()),
a manipulação de caminhos com [Link], e tivemos uma breve introdução a arquivos
binários. Os exercícios e o projeto a seguir ajudarão a consolidar essas habilidades
essenciais.
516

Exercícios
1. E16.1: Escrevendo e Lendo um Arquivo de Texto Simples (Prático)

(a) Crie um script Python. Neste script, use a instrução with open(...) para
criar e abrir um novo arquivo chamado meu_texto_inicial.txt no modo de
escrita (’w’). Certifique-se de especificar encoding="utf-8".
(b) Dentro do bloco with, escreva as seguintes três linhas de texto no arquivo, cada
uma terminando com um caractere de nova linha (\n):
• “Primeira linha do meu arquivo de teste.”
• “Python facilita a manipulação de arquivos.”
• “Esta é a terceira e última linha inicial.”
(c) Após o bloco with (garantindo que o arquivo foi fechado), adicione código no
mesmo script para abrir o arquivo meu_texto_inicial.txt novamente, desta
vez no modo de leitura (’r’) e com o mesmo encoding.
(d) Use o método .read() do objeto arquivo para ler todo o conteúdo do arquivo
de uma só vez em uma única string. Imprima esta string no console.
(e) Abra o arquivo meu_texto_inicial.txt uma terceira vez, novamente para
leitura. Desta vez, use um laço for para ler e imprimir o arquivo linha por linha.
Lembre-se de usar o método .strip() em cada linha lida antes de imprimi-la,
para remover os caracteres de nova linha (\n) extras que podem ser incluídos
por print() e que já estão no arquivo.

2. E16.2: Anexando Conteúdo a um Arquivo e Lendo Linhas para uma Lista


(Prático)

(a) Utilize o arquivo meu_texto_inicial.txt criado no exercício anterior. Escreva


código Python para abrir este arquivo no modo de anexação (’a’), usando
with open(...) e encoding="utf-8".
(b) Dentro do bloco with, adicione as duas seguintes linhas ao final do arquivo
(não se esqueça do \n):
• “Nova linha adicionada com o modo ’a’.”
• “Anexar é útil para logs e históricos.”
(c) Após anexar, abra o arquivo meu_texto_inicial.txt novamente para leitura.
(d) Use o método .readlines() para ler todas as linhas do arquivo e armazená-las
em uma lista de strings. Imprima esta lista no console para ver seu conteúdo.
(e) Itere sobre a lista de linhas que você obteve no item d). Para cada linha,
imprima o número da linha (começando em 1) seguido pelo conteúdo da linha
(use .strip() para limpá-la). Exemplo de saída para uma linha: 1: Primeira
linha do meu arquivo de teste.

3. E16.3: Contando Palavras e Frequência em um Arquivo (Prático)

(a) Usando um editor de texto simples, crie manualmente um arquivo chamado


analise_texto.txt. Coloque nele um parágrafo de texto com algumas frases
(pelo menos 20-30 palavras, com algumas palavras repetidas). Pode ser um
trecho de um livro, uma notícia, ou algo que você mesmo escreva.
517

(b) Escreva um script Python que abra o arquivo analise_texto.txt para leitura
(com encoding="utf-8").
(c) Leia todo o conteúdo do arquivo em uma única string.
(d) Converta todo o texto para letras minúsculas para facilitar a contagem de
palavras (use .lower()).
(e) Use o método split() (sem argumentos, para dividir por espaços em branco e
novas linhas) para transformar o conteúdo em uma lista de palavras.
(f) Conte e imprima o número total de palavras no arquivo (o comprimento da
lista de palavras).
(g) Desafio de Frequência: Crie um dicionário para armazenar a frequência de
cada palavra. Itere sobre sua lista de palavras. Para cada palavra:
• Limpeza (opcional, mas recomendado): Remova pontuações comuns (como
., ,, ?, !) do final ou início das palavras antes de contá-las. Uma forma
simples é verificar se o último/primeiro caractere é uma pontuação e fatiar
a string.
• Se a palavra (limpa) já estiver no dicionário de frequência, incremente sua
contagem.
• Se não estiver, adicione-a ao dicionário com contagem 1.
(h) Ao final, imprima o dicionário de frequência de palavras, ou itere sobre ele para
imprimir cada palavra e sua contagem de forma organizada.

4. E16.4: Trabalhando com Caminhos de Arquivo usando [Link] (Prático)


Crie um script Python e importe o módulo os (ou [Link]).

(a) Defina uma variável string com o nome de um arquivo que você criou nos exercí-
cios anteriores e que está no mesmo diretório do seu script (ex: nome_arquivo
= "meu_diario.txt").
(b) Use [Link](nome_arquivo) para verificar se este arquivo realmente
existe no caminho atual. Imprima uma mensagem indicando se ele existe ou
não.
(c) Use [Link](nome_arquivo) para verificar se é um arquivo regular.
Imprima o resultado.
(d) Use [Link](nome_arquivo) para verificar se é um diretório. Imprima
o resultado (deve ser False para um arquivo).
(e) Use [Link](nome_arquivo) para obter e imprimir o caminho abso-
luto completo para este arquivo no seu sistema.
(f) Crie uma string representando um caminho fictício mais longo, por exemplo:
caminho_ficticio = [Link]("meus_documentos",
"projetos_de_engenharia", "relatorio_final.pdf").
(Lembre-se, [Link] constrói o caminho de forma portável).
(g) Use [Link](caminho_ficticio) para extrair e imprimir apenas o
nome do arquivo final (“relatorio_final.pdf”).
(h) Use [Link](caminho_ficticio) para extrair e imprimir apenas a
parte do diretório do caminho.
518

(i) Use [Link](caminho_ficticio) para separar o nome do arquivo


de sua extensão. Imprima a tupla resultante e também cada uma das duas
partes separadamente.

5. E16.5: Copiando o Conteúdo de um Arquivo de Texto para Outro (Prático)

(a) Crie um arquivo de texto chamado arquivo_origem.txt e adicione algumas


linhas de texto a ele (pelo menos 4-5 linhas).
(b) Escreva um script Python que:
• Abra arquivo_origem.txt para leitura (modo ’r’).
• Abra (ou crie) um novo arquivo chamado arquivo_destino.txt para
escrita (modo ’w’).
• Leia o conteúdo de arquivo_origem.txt linha por linha.
• Para cada linha lida do arquivo de origem, escreva-a imediatamente no
arquivo_destino.txt. (Lembre-se que readline() ou a iteração sobre o
arquivo incluem o \n, então write() deve preservá-lo).
(c) Após o script rodar, abra manualmente os dois arquivos para verificar se
arquivo_destino.txt é uma cópia exata de arquivo_origem.txt.
(d) Desafio: Modifique seu script para que, ao copiar para arquivo_destino.txt,
cada linha seja prefixada com o número da linha seguido por um hífen e um
espaço (ex: “1- [conteúdo da linha 1]”, “2- [conteúdo da linha 2]”, etc.).

6. E16.6: Leitura e Escrita Binária Simples – Copiando um Pequeno Arquivo


(Prático)
Este exercício visa praticar a abertura e manipulação de arquivos em modo binário.

(a) Encontre um arquivo pequeno não textual no seu computador. Pode ser um
ícone .ico, um arquivo .gif pequeno, ou qualquer outro arquivo binário
pequeno que você tenha (evite arquivos muito grandes para este primeiro teste).
Coloque uma cópia deste arquivo no mesmo diretório do seu script Python
e chame-o de arquivo_binario_original.dat (ou use a extensão original se
preferir, ex: minha_imagem.gif).
(b) Escreva um script Python que:
• Abra o arquivo_binario_original.dat no modo de leitura binária (’rb’)
usando with open(...).
• Use o método .read() (sem argumentos) para ler todo o conteúdo binário
do arquivo de origem. Armazene esses bytes em uma variável.
• Abra um novo arquivo chamado copia_arquivo_binario.dat (ou com a
mesma extensão do original) no modo de escrita binária (’wb’) usando
with open(...).
• Use o método .write() para escrever o conteúdo binário (os bytes que
você leu) no novo arquivo.
(c) Após executar o script, tente verificar se a cópia foi bem-sucedida. Se for uma
imagem, tente abri-la com um visualizador de imagens. Se for outro tipo de
arquivo, você pode comparar os tamanhos dos arquivos original e cópia para
uma verificação básica.
519

(d) Importante: Não tente usar print() diretamente no conteúdo binário lido do
arquivo de imagem, pois isso pode exibir caracteres estranhos no seu console
ou até mesmo causar problemas, já que não é texto.
(e) Desafio (Para Arquivos Maiores): Em vez de ler todo o arquivo binário
de uma vez com .read(), modifique seu script para ler o arquivo de origem
em “pedaços” (blocos de bytes, ex: 4096 bytes de cada vez) dentro de um laço
while, e escrever cada pedaço no arquivo de destino, até que todo o arquivo de
origem tenha sido lido. (Isso é mais eficiente para arquivos grandes).

Projeto do Capítulo 16: Mini Diário Pessoal em Arquivo de Texto


Título do Projeto: Meu Diário Digital Simples.
Objetivos:

1. Praticar intensivamente a leitura e escrita em arquivos de texto, com foco no modo


de anexação (’a’) para adicionar novas entradas e o modo de leitura (’r’) para
visualização.

2. Utilizar a instrução with open(...) de forma consistente para garantir o gerencia-


mento adequado dos arquivos.

3. Trabalhar com o módulo datetime para registrar automaticamente a data e a hora


de cada entrada do diário.

4. Criar um programa interativo simples com um menu de opções que persista dados
entre execuções.

5. Reforçar o uso de laços, condicionais e manipulação de strings em um contexto de


aplicação prática.

Descrição: Crie um programa Python que funcione como um diário pessoal simples.
O programa permitirá ao usuário adicionar novas entradas ao diário (que serão salvas em
um arquivo de texto) e visualizar todas as entradas anteriores. Cada entrada do diário
será prefixada com a data e a hora em que foi criada.
Funcionalidades Detalhadas:

1. Arquivo do Diário: O programa deve usar um arquivo de texto chamado, por


exemplo, meu_diario_pessoal.txt, para armazenar todas as entradas. Se este
arquivo não existir quando o programa iniciar, ele deve ser criado (o modo ’a’ ou
’w’ ao escrever pela primeira vez pode cuidar disso).

2. Menu de Opções Interativo: O programa deve apresentar continuamente um


menu ao usuário (usando um laço while True:) com as seguintes opções:

*** Meu Diário Digital ***


1. Adicionar nova entrada
2. Visualizar todas as entradas
3. Sair
--------------------------
Escolha uma opção:
520

3. Opção 1: Adicionar Nova Entrada ao Diário

• Se o usuário escolher “1”:


– Importe o módulo datetime (from datetime import datetime).
– Obtenha a data e hora atuais usando [Link]().
– Formate esta data e hora em uma string legível
(ex: "DD/MM/AAAA HH:MM:SS" usando strftime()).
– Peça ao usuário para digitar sua entrada de diário. O usuário pode digitar
várias linhas. Uma forma de fazer isso é pedir ao usuário para digitar o texto
e informá-lo que, para finalizar a entrada, ele deve digitar uma palavra-
chave específica (ex: ":FIM:") em uma linha sozinha, ou simplesmente ler
uma única linha com input() para uma entrada mais simples.
– Abra o arquivo meu_diario_pessoal.txt no modo de anexação (’a’)
usando with open(...) e encoding="utf-8".
– Escreva a data/hora formatada no arquivo, seguida pela(s) linha(s) da
entrada do usuário.
– Adicione um separador claro (como uma linha de -––\n") após cada entrada
completa para facilitar a leitura do arquivo.
– Imprima uma mensagem de sucesso (ex: “Entrada adicionada ao diário!”).

4. Opção 2: Visualizar Todas as Entradas do Diário

• Se o usuário escolher “2”:


– Use [Link]() para verificar se o arquivo
meu_diario_pessoal.txt existe. Se não existir, imprima “O diário está
vazio ou ainda não foi criado.” e volte ao menu.
– Se o arquivo existir, abra-o no modo de leitura (’r’) com encoding="utf-8"
usando with open(...).
– Leia todo o conteúdo do arquivo (ex: com .read() ou iterando sobre o
objeto arquivo).
– Imprima o conteúdo lido no console. Se o arquivo estiver vazio (após a
abertura), informe que “O diário está vazio.”

5. Opção 3: Sair

• Se o usuário escolher “3”, imprima uma mensagem de despedida (ex: “Até


a próxima, diário!”) e use break para sair do laço principal, encerrando o
programa.

6. Opção Inválida: Se o usuário digitar uma opção que não está no menu, imprima
uma mensagem como “Opção inválida. Por favor, escolha um número do menu.”
Desafios Extras (Opcionais):
1. Busca por Data ou Palavra-Chave: Adicione uma nova opção ao menu que
permita ao usuário buscar entradas no diário. O usuário poderia digitar uma data
(como string no formato “DD/MM/AAAA”) ou uma palavra-chave. Seu programa
leria o arquivo e imprimiria apenas as entradas que contêm a data ou a palavra-chave
fornecida.
521

2. Entradas Multilinha Melhoradas: Ao adicionar uma nova entrada, em vez de


uma palavra-chave para finalizar, permita que o usuário digite múltiplas linhas de
texto pressionando Enter após cada linha, e considere a entrada finalizada quando
ele pressionar Enter em uma linha completamente vazia. Colete todas essas linhas e
salve-as como parte da mesma entrada no diário.

3. Backup Automático (Simples): Antes de anexar uma nova entrada ao


meu_diario_pessoal.txt, faça uma cópia de backup do arquivo existente (se ele
existir) para um arquivo como meu_diario_pessoal.[Link]. Você pode usar as
técnicas de leitura e escrita de arquivos que aprendeu (ler todo o conteúdo do original
e escrever no arquivo de backup no modo ’w’, ou até mesmo tentar copiar em modo
binário para ser mais genérico).

Capítulo 17: Tratamento de Exceções (Exercícios e Projeto)


Neste capítulo, aprendemos a arte de escrever programas Python mais robustos e
resilientes através do tratamento de exceções. Vimos a diferença entre erros de sintaxe e
exceções, exploramos a estrutura fundamental try-except para capturar erros específicos,
como lidar com múltiplas exceções, e o uso das cláusulas else (para código que executa
quando não há erro) e finally (para código que sempre executa, ideal para limpeza).
Também aprendemos a levantar nossas próprias exceções com raise e a definir exceções
personalizadas para erros específicos de nossas aplicações. Os exercícios e o projeto a
seguir visam solidificar essas habilidades cruciais.

Exercícios
1. E17.1: Identificando e Prevendo Tipos de Exceções (Conceitual/Prático)

(a) Para cada um dos seguintes trechos de código Python, preveja qual tipo
de exceção (se houver) você espera que seja levantada durante a execução.
Justifique brevemente sua previsão. Após fazer suas previsões, execute cada
trecho em um interpretador Python ou em um script para verificar suas respostas
e observar a mensagem de erro e o traceback.
i. resultado = 5 / 0
ii. minha_lista = [10, 20, 30]; print(minha_lista[3])
iii. meu_dicionario = {"nome": "Python"};
print(meu_dicionario["idade"])
iv. numero_convertido = int("cinco")
v. print(nome_variavel_que_nao_existe)
vi. calculo = "Resultado: "+ 100
vii. arquivo = open("arquivo_imaginario.txt", "r") (Supondo que o ar-
quivo não existe)
(b) Relembrando a Seção 17.1, qual é a diferença fundamental entre um erro
de sintaxe (SyntaxError) e uma exceção (erro em tempo de execução) em
Python? Dê um exemplo simples de um erro de sintaxe.

2. E17.2: Tratamento Básico de Exceções com try-except (Prático)


522

(a) Escreva um script Python que peça ao usuário para digitar dois números. O
script deve então tentar dividir o primeiro número pelo segundo e imprimir o
resultado formatado. Use um bloco try-except para capturar especificamente
a exceção ZeroDivisionError. Se esta exceção ocorrer, o programa deve
imprimir uma mensagem amigável como “Erro: Divisão por zero não é permitida!”
em vez de parar.
(b) Modifique o script do item (a) para que ele também capture a exceção ValueError.
Isso pode ocorrer se o usuário digitar um texto que não pode ser convertido
para um número (ex: “abc” quando se espera um float ou int). Se um
ValueError ocorrer durante a conversão das entradas para números, imprima
uma mensagem como “Erro: Por favor, insira apenas números válidos.”

3. E17.3: Múltiplas Cláusulas except e Capturando a Instância do Erro


(Prático)
Crie uma função chamada processar_entrada_numerica(entrada_str) que:

• Tente converter a string entrada_str para um número de ponto flutuante


(float).
• Em seguida, tente calcular o inverso desse número (1 / número).
• Se ambas as operações forem bem-sucedidas, a função deve retornar o inverso
calculado.

No seu script principal, peça uma entrada ao usuário e chame a função


processar_entrada_numerica() dentro de um bloco try. Implemente as seguintes
cláusulas except para tratar os erros que podem ocorrer dentro da função:

• Uma cláusula para ValueError (se a conversão para float falhar).


• Uma cláusula para ZeroDivisionError (se o número convertido for zero e a
divisão 1/número for tentada).
• Uma cláusula except Exception as e para capturar qualquer outra exceção
inesperada que possa ocorrer.

Para cada cláusula except, imprima uma mensagem informativa que inclua o tipo
do erro e, se você capturou a instância do erro (usando as e), imprima também a
mensagem da exceção (o objeto e). Se a função retornar um resultado com sucesso,
imprima esse resultado. Teste seu script com entradas que causem cada um desses
cenários de erro, e também com uma entrada válida.

4. E17.4: Usando a Cláusula else com try-except (Prático)


Escreva um script que tente o seguinte:

(a) Peça ao usuário para fornecer o nome de um arquivo de texto.


(b) No bloco try, tente abrir este arquivo no modo de leitura (’r’) e leia seu
conteúdo completo para uma variável.
(c) No bloco except FileNotFoundError, imprima uma mensagem amigável in-
formando que o arquivo não foi encontrado.
523

(d) No bloco else (que só executa se o bloco try for bem-sucedido e nenhuma
FileNotFoundError ocorrer), imprima a mensagem “Arquivo encontrado e
lido com sucesso!” e, em seguida, imprima os primeiros 100 caracteres do
conteúdo do arquivo (se o arquivo tiver menos de 100 caracteres, imprima todo
o conteúdo).
Teste seu script fornecendo o nome de um arquivo que existe e depois o nome de um
arquivo que não existe.
5. E17.5: Garantindo a Execução de Código com a Cláusula finally (Práti-
co/Conceitual)
Considere o seguinte esqueleto de código. O objetivo é simular uma operação que
pode falhar (como uma comunicação de rede ou acesso a um recurso que precisa ser
liberado).
1 # recurso _adquiri do = False # Para rastrear se o recurso
foi " aberto "
2 # try :
3 # print (" Tentando adquirir e usar um recurso
importante ...")
4 # # Simule a aquisi ç ã o de um recurso aqui ( ex :
arquivo , conex ã o )
5 # # recur so_adqui rido = True # Se a aquisi ç ã o for
bem - sucedida
6 # print (" Recurso adquirido com sucesso .")
7 #
8 # operacao = input (" Digite ’ ok ’ para sucesso ou ’ erro ’
para simular falha na opera ç ã o : ")
9 # if operacao . lower () == ’ erro ’:
10 # raise RuntimeError (" Simula ç ã o de erro durante a
opera ç ã o com o recurso !")
11 #
12 # print (" Opera ç ã o com o recurso realizada com
sucesso .")
13 #
14 # except RuntimeError as e :
15 # print ( f " ERRO DURANTE A OPERA Ç Ã O : { e }")
16 # except Exception as e_geral :
17 # print ( f " Outro erro inesperado no try : { e_geral }")
18 # else :
19 # print (" Nenhuma exce ç ã o ocorreu durante a tentativa e
opera ç ã o .")
20 # finally :
21 # print ("\ n - - - BLOCO FINALLY - - -")
22 # # Aqui viria o c ó digo para liberar o recurso , se ele
foi adquirido
23 # # if recurso _adquiri do :
24 # # print (" Liberando o recurso importante no bloco
finally ...")
25 # # recur so_adqui rido = False # Simula a libera ç ã o
26 # # print (" Recurso liberado .")
27 # # else :
28 # # print (" Nenhum recurso havia sido adquirido para
liberar .")
29 # print (" - - - Fim do programa - - -")

Código 21.43: Esqueleto para teste da cláusula finally


524

Copie este esqueleto para o seu editor (descomente as partes internas conforme
necessário para os testes).
(a) Execute o script digitando “ok” quando solicitado. Observe a ordem das
mensagens impressas, especialmente a do bloco finally.
(b) Execute o script novamente, mas desta vez digite “erro” quando solicitado (para
simular o RuntimeError). Observe a ordem das mensagens. O bloco finally
foi executado mesmo com o erro?
(c) (Conceitual) Modifique o exemplo para simular que a "aquisição do recurso"(a
linha antes do input()) falha, talvez levantando uma IOError antes que
recurso_adquirido se torne True. O bloco finally ainda executaria?
(d) Explique com suas palavras por que a cláusula finally é tão importante,
especialmente para garantir a liberação de recursos externos (como arquivos,
conexões de rede, travas, etc.), mesmo que a instrução with open(...) seja a
forma preferida para gerenciar arquivos especificamente.
6. E17.6: Levantando Exceções Deliberadamente com raise (Prático)
Crie uma função chamada validar_parametro_engenharia(valor,
nome_parametro, unidade_esperada, valor_min=None, valor_max=None). Esta
função deve realizar as seguintes validações:
• Se valor não for um tipo numérico (int ou float), ela deve levantar um
TypeError com uma mensagem como: “O parâmetro ’[nome_parametro]’ espe-
rava um valor numérico, mas recebeu [tipo do valor].”
• Se valor_min for fornecido e valor for menor que valor_min, ela deve levantar
um ValueError com uma mensagem como: “O valor para ’[nome_parametro]’
([valor]) é menor que o mínimo permitido de [valor_min] [unidade_esperada].”
• Se valor_max for fornecido e valor for maior que valor_max, ela deve levantar
um ValueError com uma mensagem similar.
• Se todas as validações passarem, a função deve imprimir:
“Parâmetro ’[nome_parametro]’ com valor [valor] [unidade_esperada] é válido.”
No seu script principal, chame esta função várias vezes dentro de blocos try-except
para testar todos os cenários de erro e o cenário de sucesso. Capture TypeError e
ValueError separadamente e imprima a mensagem da exceção capturada. Exemplo
de chamada: validar_parametro_engenharia(25, "Temperatura", "°C",
valor_min=0, valor_max=100)
7. E17.7: Criando e Usando Exceções Personalizadas (Opcional/Avançado)
(a) Defina uma exceção personalizada base chamada ErroDeSimulacao(Exception).
Adicione uma docstring a ela.
(b) Crie duas subclasses que herdem de ErroDeSimulacao:
• ErroDeConvergencia(ErroDeSimulacao): Para quando um cálculo numé-
rico não converge.
• ParametroDeSimulacaoInvalidoError(ErroDeSimulacao): Para quando
um parâmetro de entrada da simulação é inválido. No __init__ desta
exceção, aceite uma mensagem e o nome do parâmetro inválido, e armazene
o nome do parâmetro como um atributo da exceção.
525

(c) Crie uma função rodar_passo_simulacao(parametro_x, parametro_y) que:


• Se parametro_x < 0, levante ParametroDeSimulacaoInvalidoError
("Parâmetro X não pode ser negativo.", "parametro_x").
• Se parametro_y == 0, levante um ErroDeConvergencia("Divisão por
Y (zero) indica não convergência.").
• Caso contrário, calcule e retorne parametro_x / parametro_y (simulando
um passo bem-sucedido).
(d) No seu script principal, chame rodar_passo_simulacao dentro de um bloco
try com diferentes valores que causem cada uma das exceções personalizadas,
e também um caso de sucesso.
(e) Adicione cláusulas except para capturar ParametroDeSimulacaoInvalidoError
as e_param, ErroDeConvergencia as e_conv, e ErroDeSimulacao as e_base
(nesta ordem).
(f) Dentro dos blocos except, imprima mensagens apropriadas, incluindo a men-
sagem da exceção e, para ParametroDeSimulacaoInvalidoError, o nome do
parâmetro com problema (acessando e_param.parametro_invalido).

Projeto do Capítulo 17: Validador Robusto de Dados de Entrada para Arquivo


de Configuração de Engenharia
Título do Projeto: Validador Robusto para Arquivo de Configuração de Parâmetros
de Engenharia.
Objetivos:

1. Aplicar extensivamente o tratamento de exceções (try-except-else-finally) para


lidar com diversos tipos de erros potenciais na leitura e processamento de um arquivo.

2. Levantar exceções (raise) de forma apropriada para sinalizar condições de erro


específicas durante a validação dos dados.

3. (Fortemente Recomendado) Definir e utilizar exceções personalizadas para categorizar


e tratar erros de validação de forma mais semântica e organizada.

4. Combinar as habilidades de manipulação de arquivos (leitura de arquivo de texto)


com um tratamento de erros robusto.

5. Criar um módulo de validação que possa ser, em teoria, reutilizável.

Descrição do Problema: Muitas aplicações de engenharia e simulação dependem


de arquivos de configuração para definir parâmetros de entrada, condições de contorno,
ou outras configurações importantes. Estes arquivos podem ser editados manualmente e,
portanto, estão sujeitos a erros de formato ou a valores inválidos. O objetivo deste projeto
é criar um sistema Python que leia um arquivo de configuração, valide seus parâmetros de
acordo com regras predefinidas, e reporte de forma clara quaisquer erros encontrados, ou
processe os dados se estiverem todos corretos.
Formato do Arquivo de Configuração (Exemplo: config_simulacao.txt): O
arquivo será um texto simples com pares chave-valor, um por linha. Linhas começando
com # são comentários e devem ser ignoradas. Linhas em branco também devem ser
ignoradas.
526

# Arquivo de Configuração para Simulação de Estruturas


# Formato: NOME_PARAMETRO = VALOR

TITULO_PROJETO = Análise de Viga Bi-apoiada


MATERIAL_VIGA = Aço ASTM A36
COMPRIMENTO_VIGA_M = 5.0 # Em metros
CARGA_APLICADA_KN = 150.7 # Em KiloNewtons
NUMERO_DE_ELEMENTOS = cem # Erro de formato proposital aqui
FATOR_SEGURANCA = 1.5
PONTO_ANALISE_M = 2.5
MODO_CALCULO = Avancado # Valores esperados: "Simples" ou "Avancado"
EMAIL_NOTIFICACAO = contato@engenharia
# TEMPERATURA_OPERACAO_C = # Valor ausente proposital

Regras de Validação (Exemplos – você pode definir mais):

• TITULO_PROJETO: String, obrigatório, não pode ser vazio.

• MATERIAL_VIGA: String, obrigatório.

• COMPRIMENTO_VIGA_M: Float, obrigatório, deve ser positivo.

• CARGA_APLICADA_KN: Float, obrigatório, deve ser positivo.

• NUMERO_DE_ELEMENTOS: Inteiro, obrigatório, deve ser positivo.

• FATOR_SEGURANCA: Float, opcional (se ausente, assume 1.0), deve ser >= 1.0 se
presente.

• PONTO_ANALISE_M: Float, obrigatório, deve ser positivo e menor ou igual ao


COMPRIMENTO_VIGA_M.

• MODO_CALCULO: String, opcional (padrão "Simples"), se presente, deve ser "Sim-


ples"ou "Avancado"(insensível a maiúsculas/minúsculas).

• EMAIL_NOTIFICACAO: String, opcional. Se presente, deve conter um "@".

• TEMPERATURA_OPERACAO_C: Float, opcional. Se presente, deve estar entre -50 e 200.

Estrutura Sugerida (Módulo de Validação e Script Principal):

1. Módulo validador_config_engenharia.py:

• Exceções Personalizadas (Fortemente Recomendado):


– Defina uma classe base ErroConfigSimulacao(Exception).
– Defina subclasses como ErroFormatoLinhaConfig(ErroConfigSimulacao),
ErroCampoObrigatorioAusente(ErroConfigSimulacao),
ErroTipoDadoInvalido(ErroConfigSimulacao),
ErroValorForaDaFaixa(ErroConfigSimulacao).
– Faça com que essas exceções possam armazenar informações úteis (como o
nome do campo, o valor problemático, a mensagem).
• Função ler_e_processar_config(caminho_do_arquivo):
527

– Será a função principal do módulo.


– Deve abrir e ler o arquivo especificado.
Use try-except FileNotFoundError.
– Leia linha por linha. Ignore comentários e linhas em branco.
– Para cada linha de dados, parseie a chave e o valor (ex: dividindo no
primeiro =). Se o formato for inválido, levante ErroFormatoLinhaConfig.
– Armazene os pares chave-valor lidos em um dicionário.
– Após ler todas as linhas, chame funções de validação específicas para
cada parâmetro esperado (veja abaixo). Essas funções de validação devem
levantar as exceções personalizadas apropriadas se a validação falhar.
– Use um bloco try-except ao redor das chamadas às funções de validação
para capturar ErroConfigSimulacao (ou suas subclasses). Se um erro de
validação ocorrer, você pode optar por:
∗ Parar na primeira falha e re-levantar a exceção (ou uma nova
ErroConfigSimulacao resumindo o problema).
∗ (Mais avançado) Coletar todos os erros de validação em uma lista e, se
houver erros, levantar uma exceção que contenha todos eles.
– Se todas as validações passarem, a função deve retornar o dicionário de
configuração com os valores já convertidos para os tipos corretos (ex: floats,
ints).
– Use uma cláusula finally para garantir que o arquivo seja fechado, mesmo
que você use with open() internamente (o finally aqui seria para o caso
de a própria função ler_e_processar_config ser interrompida de forma
inesperada antes do with terminar).
• Funções de Validação Específicas Auxiliares: Crie pequenas funções para
validar cada tipo de parâmetro, conforme as regras definidas. Exemplos:
– validar_string_obrigatoria(valor, nome_campo)
– validar_float_positivo(valor_str, nome_campo)
– validar_inteiro_positivo(valor_str, nome_campo)
– validar_escolha_limitada(valor_str, nome_campo, opcoes_validas)
– validar_email_simples(valor_str, nome_campo) (para o opcional)
Essas funções devem levantar suas respectivas exceções personalizadas (ou
ValueError/TypeError se preferir não criar muitas exceções personalizadas)
se a validação falhar.
2. Script Principal executar_simulacao.py:
• Importe a função ler_e_processar_config do seu módulo
validador_config_engenharia.
• Defina o nome do arquivo de configuração (ex: "simulacao_config.txt").
• Chame ler_e_processar_config(nome_arquivo) dentro de um try-except.
• No bloco except (capturando ErroConfigSimulacao ou a exceção mais gené-
rica que sua função de leitura levanta em caso de falha de validação):
– Imprima uma mensagem clara indicando que a configuração é inválida e
exiba os detalhes do erro (a mensagem da exceção).
528

• No bloco else (se o try para ler_e_processar_config foi bem-sucedido):


– Imprima “Configuração carregada e validada com sucesso!”
– Exiba os parâmetros da simulação que foram lidos.
– Simule uma “execução da simulação” (ex: imprima “Iniciando simulação
com os seguintes parâmetros...” seguido pelos parâmetros).

Testes: Crie diferentes versões do arquivo simulacao_config.txt para testar todos


os cenários:

• Um arquivo completamente correto.

• Um arquivo com erros de formato de linha (ex: sem o =).

• Um arquivo com valores de tipo incorreto (ex: texto onde se espera número).

• Um arquivo com valores fora das faixas permitidas.

• Um arquivo com campos obrigatórios faltando.

• Um arquivo inexistente.

Execute seu executar_simulacao.py com cada um para garantir que os erros são captu-
rados, reportados de forma útil, e que o programa não quebra inesperadamente.

Parte VI: Bibliotecas Essenciais para Engenharia


Capítulo 18: NumPy para Computação Numérica (Exercícios e
Projeto)
Neste capítulo, introduzimos a poderosa biblioteca NumPy, fundamental para a com-
putação numérica em Python. Aprendemos sobre o objeto ndarray, como criar arrays de
diversas formas, seus atributos principais, técnicas de indexação e fatiamento, o poder da
vetorização e do broadcasting, funções matemáticas e estatísticas básicas, e uma introdução
à álgebra linear e à persistência de arrays. Os exercícios e o projeto a seguir ajudarão você
a começar a usar o NumPy de forma prática. Lembre-se de importar o NumPy em seus
scripts, geralmente com import numpy as np.

Exercícios
1. E18.1: Criação de Arrays NumPy – Diversas Formas (Prático)

(a) Importe a biblioteca NumPy usando o alias np.


(b) Crie um array NumPy unidimensional chamado vetor1 a partir da lista Python
[5, 10, 15, 20, 25]. Imprima vetor1 e seu tipo.
(c) Crie uma matriz (array NumPy bidimensional) chamada matriz1 de dimensões
2x3 (2 linhas, 3 colunas) a partir da seguinte lista de listas: [[10, 20, 30],
[40, 50, 60]]. Imprima matriz1 e também o tipo de dado de seus elementos
(o atributo dtype).
529

(d) Crie um array NumPy unidimensional chamado vetor_zeros contendo 6 ele-


mentos, todos preenchidos com o valor zero, usando a função [Link]().
Imprima-o.
(e) Crie uma matriz NumPy 3x2 chamada matriz_cinco onde todos os elementos
são o número 5, usando a função [Link](). Imprima-a.
(f) Crie um array NumPy chamado sequencia_0_a_8 contendo os números inteiros
de 0 até 8 (inclusive) usando a função [Link](). Imprima-o.
(g) Crie um array NumPy chamado pontos_espacados contendo 7 números de
ponto flutuante espaçados uniformemente entre 0 e 10 (ambos inclusivos),
usando a função [Link](). Imprima-o.
(h) Crie uma matriz identidade 4x4 chamada matriz_id_4 usando a função [Link]().
Imprima-a.
(i) Crie um array NumPy unidimensional chamado vetor_aleatorio_int com 5
números inteiros aleatórios escolhidos no intervalo entre 1 e 50 (inclusive 1, exclu-
sivo 50, ou como randint definir), usando [Link](). Imprima-o.

2. E18.2: Explorando Atributos de Arrays NumPy (Prático)


Crie a seguinte matriz NumPy:
1 import numpy as np
2 matriz_info = np . array ([[2.5 , 3.0 , 4.5 , 1.0] ,
3 [6.0 , 7.5 , 8.0 , 5.5] ,
4 [1.2 , 0.5 , 2.3 , 3.8]] , dtype = np . float32 )

Código 21.44: Matriz para E18.2

Em seguida, imprima:

(a) A matriz matriz_info em si.


(b) O número de dimensões da matriz (usando o atributo ndim).
(c) A forma (shape) da matriz (usando o atributo shape).
(d) O número total de elementos na matriz (usando o atributo size).
(e) O tipo de dado dos elementos da matriz (usando o atributo dtype).
(f) O tamanho em bytes de cada elemento da matriz (usando o atributo itemsize).
(g) O número total de bytes consumidos pelos dados da matriz (usando o atributo
nbytes).

3. E18.3: Indexação e Fatiamento Simples em Arrays NumPy (Prático)


Crie o seguinte array NumPy unidimensional: vetor_principal = [Link](0,
15, 2) (ou seja, [0, 2, 4, 6, 8, 10, 12, 14]).

(a) Imprima o vetor_principal.


(b) Imprima o primeiro elemento do vetor.
(c) Imprima o último elemento do vetor usando indexação negativa.
(d) Imprima os elementos do índice 1 até o índice 3 (inclusive o elemento no índice
3).
(e) Imprima todos os elementos a partir do índice 4 até o final.
530

(f) Imprima todos os elementos do vetor em ordem invertida usando fatiamento.

Agora, crie a seguinte matriz NumPy:


1 m at riz _p ar a_f at ia r = np . array ([[11 , 12 , 13 , 14] ,
2 [21 , 22 , 23 , 24] ,
3 [31 , 32 , 33 , 34]])

Código 21.45: Matriz para fatiamento em E18.3

(a) Imprima a matriz_para_fatiar.


(b) Imprima o elemento que está na segunda linha (índice 1) e terceira coluna
(índice 2). (Deve ser o número 23). Use a notação matriz[linha, coluna].
(c) Imprima a primeira linha inteira da matriz.
(d) Imprima a segunda coluna inteira da matriz.
(e) Crie uma fatia que seja uma visão da submatriz 2x2 composta pelas duas
primeiras linhas e as duas primeiras colunas. Chame-a de fatia_visao_2x2.
(f) Modifique um elemento dentro de fatia_visao_2x2 (ex: fatia_visao_2x2[0,
0] = 99).
(g) Imprima a matriz_para_fatiar original novamente. Ela foi alterada pela
modificação na fatia? Explique o porquê.
(h) Crie uma cópia da mesma submatriz 2x2 e chame-a de fatia_copia_2x2
(usando .copy()). Modifique um elemento nesta cópia. A matriz original foi
alterada desta vez?

4. E18.4: Operações Vetorizadas e Broadcasting Simples (Prático)


Crie os seguintes arrays NumPy: arr1 = [Link]([10, 20, 30, 40])
arr2 = [Link]([1, 2, 3, 4])

(a) Calcule e imprima a soma de arr1 e arr2 (elemento a elemento).


(b) Calcule e imprima a diferença arr1 - arr2.
(c) Calcule e imprima o produto arr1 * arr2 (elemento a elemento).
(d) Calcule e imprima arr1 dividido por arr2 (elemento a elemento).
(e) Multiplique todos os elementos de arr1 por 5 e imprima o resultado.
(f) Crie uma matriz matriz_op = [Link]((3, 4)). Some o arr1 a esta matriz
(note que arr1 é 1D e matriz_op é 2D). Imprima o resultado. Explique
brevemente como o broadcasting do NumPy permitiu essa operação.

5. E18.5: Funções Estatísticas Básicas do NumPy (Prático)


Crie o seguinte array NumPy representando um conjunto de medições: medicoes =
[Link]([12.5, 15.3, 11.0, 13.7, 16.0, 14.5, 12.9, 15.3, 13.2])

(a) Calcule e imprima a soma de todas as medições usando [Link]().


(b) Encontre e imprima o valor mínimo e o valor máximo nas medições usando
[Link]() e [Link]().
(c) Calcule e imprima a média aritmética das medições usando [Link]().
531

(d) Calcule e imprima o desvio padrão das medições usando [Link]().


(e) (Opcional) Crie uma matriz 2x3 com números inteiros aleatórios entre 0 e 10.
Use o parâmetro axis nas funções sum(), min(), max(), mean() para calcular
essas estatísticas para cada coluna e para cada linha da sua matriz.

6. E18.6: Operações de Álgebra Linear Simples com [Link] (Prático)


Crie as seguintes matrizes NumPy:
matrizA = [Link]([[4, 2], [9, 5]])
matrizB = [Link]([[1, 7], [6, 3]])

(a) Imprima a transposta da matrizA (usando o atributo .T).


(b) Calcule e imprima o produto matricial entre matrizA e matrizB. Use o operador
@.
(c) Calcule e imprima a inversa da matrizA usando [Link](). (Esta
matriz é invertível).
(d) Calcule e imprima o determinante da matrizB usando [Link]().
(e) Considere o sistema de equações lineares: 4x+2y = 10 e 9x+5y = 20. Reescreva
este sistema na forma Ax = b. Use [Link]() para encontrar os
valores de x e y. Imprima a solução.

Projeto do Capítulo 18: Análise Simplificada de Dados de Múltiplos Sensores


Título do Projeto: Análise Básica e Comparativa de Leituras de Múltiplos Sensores
com NumPy.
Objetivos:
1. Praticar a criação de arrays NumPy bidimensionais (matrizes) para representar
conjuntos de dados.

2. Utilizar indexação e fatiamento para selecionar e analisar subconjuntos de dados de


sensores.

3. Aplicar operações vetorizadas para realizar cálculos e conversões nos dados.

4. Usar funções estatísticas do NumPy para resumir e comparar as leituras dos sensores.

5. (Opcional) Aplicar conceitos básicos de álgebra linear se apropriado.


Descrição do Cenário: Imagine que você está monitorando três sensores de tempe-
ratura (Sensor 1, Sensor 2, Sensor 3) em um processo industrial. Ao longo de um período
de 5 horas, você coletou uma leitura de temperatura por hora de cada sensor. Seus dados
estão organizados da seguinte forma:

• Sensor 1: [25.5, 25.7, 26.0, 25.8, 25.5] °C

• Sensor 2: [26.1, 26.3, 26.5, 26.4, 26.0] °C

• Sensor 3: [24.8, 24.9, 25.0, 25.3, 25.1] °C

(As leituras correspondem à Hora 1, Hora 2, Hora 3, Hora 4, Hora 5 para cada sensor).
Tarefas a Realizar com NumPy:
532

1. Criação do Array de Dados:

• Crie um array NumPy bidimensional (uma matriz) chamado leituras_sensores


onde cada linha representa um sensor e cada coluna representa uma leitura
horária. A matriz terá dimensões 3x5.

1 import numpy as np
2
3 # leitura s_sensor es = np . array ([
4 # [25.5 , 25.7 , 26.0 , 25.8 , 25.5] , # Sensor 1
5 # [26.1 , 26.3 , 26.5 , 26.4 , 26.0] , # Sensor 2
6 # [24.8 , 24.9 , 25.0 , 25.3 , 25.1] # Sensor 3
7 # ])
8 # print (" Leituras dos Sensores ( Celsius ) :\ n " ,
leitu ras_sens ores )

Código 21.46: Estrutura inicial do array para o projeto

(Por favor, descomente e preencha o array no seu código.)

2. Conversão de Unidade (Celsius para Fahrenheit):

• Crie um novo array NumPy chamado leituras_fahrenheit que contenha


todas as temperaturas convertidas para Fahrenheit. Use a fórmula F = C× 95 +32
e aplique-a de forma vetorizada a todo o array leituras_sensores.
• Imprima o array leituras_fahrenheit formatado para duas casas decimais.

3. Análise Estatística por Sensor: Para cada sensor (cada linha da matriz
leituras_sensores):

• Calcule e imprima a temperatura média registrada.


• Encontre e imprima a temperatura mínima e máxima registrada.
• (Opcional) Calcule e imprima o desvio padrão das leituras.

(Dica: Use o parâmetro axis=1 nas funções estatísticas do NumPy para operar ao
longo das linhas).

4. Análise Estatística por Hora (Instante de Tempo): Para cada instante de


tempo (cada coluna da matriz leituras_sensores):

• Calcule e imprima a temperatura média entre todos os sensores.


• Encontre e imprima a temperatura máxima registrada naquele instante.

(Dica: Use o parâmetro axis=0 nas funções estatísticas do NumPy para operar ao
longo das colunas).

5. Identificando Leituras Acima de um Limite:

• Suponha que qualquer temperatura acima de 25.7 °C é considerada um alerta.


• Use indexação booleana para criar um novo array (ou apenas exibir) contendo
todas as leituras de temperatura (em Celsius) que excederam esse limite.
• Conte e imprima quantas leituras de alerta ocorreram no total.
533

6. Selecionando Dados Específicos:

• Imprima todas as leituras do Sensor 2 (segunda linha).


• Imprima todas as leituras ocorridas na Hora 3 (terceira coluna).
• Imprima a leitura específica do Sensor 1 na Hora 4.

Desafios Extras (Opcionais):

1. Diferença Máxima de Temperatura: Para cada sensor, encontre a diferença


entre sua temperatura máxima e mínima registrada ao longo do período. Qual sensor
teve a maior variação?

2. Correlação (Conceitual/Pesquisa): Embora não tenhamos abordado [Link]()


em detalhes, pesquise como você poderia calcular uma matriz de correlação entre as
séries temporais dos sensores para ver se eles tendem a variar juntos. (Isso antecipa
um pouco o Pandas, mas é um bom exercício de pensamento).

3. Salvando Resultados: Salve o array leituras_fahrenheit em um arquivo .npy


e também em um arquivo .txt formatado.

Capítulo 19: Matplotlib e Seaborn para Visualização de Dados


(Exercícios e Projeto)
Neste capítulo, mergulhamos no mundo da visualização de dados com Python, utilizando
Matplotlib como nossa biblioteca fundamental e Seaborn para gráficos estatísticos mais
elaborados. Aprendemos a criar gráficos de linha, dispersão, barras, histogramas e pizza,
além de como trabalhar com subplots, personalizar a aparência e salvar nossas visualizações.
Os exercícios e o projeto a seguir ajudarão você a praticar essas habilidades essenciais
para transformar dados em insights visuais.
Lembrete de Importações Comuns: Para a maioria dos exercícios, você precisará
das seguintes importações no início do seu script:
1 import matplotlib . pyplot as plt
2 import numpy as np
3 import seaborn as sns # Para exerc í cios espec í ficos de Seaborn
4 import pandas as pd # Para exerc í cios espec í ficos de Seaborn que
usam DataFrames

Código 21.47: Importações padrão para Capítulo 19

Aplique um tema Seaborn no início dos seus scripts, se desejar, para uma estética consistente
(ex: sns.set_theme(style="whitegrid")).

Exercícios
1. E19.1: Seu Primeiro Gráfico de Linha com Matplotlib (Prático)

(a) Crie um array NumPy chamado x_valores contendo 100 pontos espaçados
uniformemente entre 0 e 4π (use [Link](0, 4*[Link], 100)).
(b) Crie um array NumPy chamado y_valores que seja o resultado de cos(x_valores).
(c) Use [Link](x_valores, y_valores) para gerar o gráfico de linha.
534

(d) Adicione um título apropriado ao gráfico, como “Gráfico da Função Cosseno de


0 a 4π”.
(e) Adicione rótulos aos eixos: “Eixo X (radianos)” para o eixo x e “Cosseno(x)”
para o eixo y.
(f) Adicione uma grade ao gráfico usando [Link](True).
(g) Exiba o gráfico usando [Link]().
2. E19.2: Personalizando Gráficos de Linha e Desenhando Múltiplas Linhas
(Prático)
(a) Use os mesmos dados x_valores do exercício anterior. Crie dois arrays y: y1
= [Link](x_valores) e y2 = [Link](x_valores + [Link]/2).
(b) Em uma nova figura ([Link]()), plote y1 vs x_valores. Personalize
esta linha para que seja:
• Cor: azul (color=’blue’)
• Estilo de linha: tracejado (linestyle=’–’)
• Marcador: círculo (marker=’o’)
• Largura da linha: 2 (linewidth=2)
• Adicione um rótulo para a legenda: label=’sin(x)’
(c) No mesmo gráfico, plote y2 vs x_valores. Personalize esta linha para que
seja:
• Cor: verde (color=’green’)
• Estilo de linha: ponto-traço (linestyle=’-.’)
• Marcador: ’x’ (marker=’x’)
• Largura da linha: 1.5
• Adicione um rótulo para a legenda: label=’sin(x + pi/2)’
(d) Adicione um título ao gráfico (ex: “Comparação de Funções Senoidais”).
(e) Adicione rótulos aos eixos X e Y.
(f) Ative a legenda usando [Link](). Experimente diferentes localizações
para a legenda usando o parâmetro loc (ex: loc=’upper right’).
(g) Defina os limites do eixo Y para irem de -1.2 a 1.2 usando [Link](-1.2,
1.2).
(h) Adicione uma grade ao gráfico.
(i) Salve a figura resultante como um arquivo chamado
grafico_senos_comparados.png com uma resolução de 150 DPI (dpi=150) e
usando bbox_inches=’tight’. Lembre-se de salvar antes de [Link]().
(j) Exiba o gráfico.
3. E19.3: Gráfico de Dispersão Simples com Matplotlib (Prático)
(a) Gere dois arrays NumPy, x_dispersao_dados e y_dispersao_dados, cada um
contendo 50 números aleatórios. Para x_dispersao_dados, use uma distribui-
ção uniforme entre 0 e 10 ([Link](50) * 10). Para y_dispersao_dados,
crie uma relação linear com algum ruído, por exemplo: y_dispersao_dados =
2.5 * x_dispersao_dados + [Link](50) * 5.
535

(b) Crie um gráfico de dispersão desses pontos usando


[Link](x_dispersao_dados, y_dispersao_dados).
(c) Adicione um título (ex: “Relação entre X e Y (Dados Simulados)”) e rótulos
apropriados para os eixos X e Y.
(d) Personalize a aparência dos pontos:
• Cor: ’purple’
• Marcador: triângulo para cima (’ˆ
’)
• Tamanho dos pontos (s): 60
• Transparência (alpha): 0.6
• Cor da borda dos marcadores (edgecolors): ’black’
(e) Adicione uma grade e exiba o gráfico.

4. E19.4: Gráfico de Barras com Matplotlib para Comparação Categórica


(Prático)

(a) Suponha que você tem dados de produção (em toneladas) de três tipos de grãos
em uma fazenda: tipos_graos = [’Milho’, ’Soja’, ’Trigo’, ’Feijão’]
producao_toneladas = [1200, 2100, 850, 500]
(b) Use [Link]() para criar um gráfico de barras verticais mostrando a produção
de cada tipo de grão.
(c) Adicione um título (ex: “Produção Anual de Grãos por Tipo”), um rótulo para
o eixo X (“Tipo de Grão”) e um rótulo para o eixo Y (“Produção (toneladas)”).
(d) Para cada barra, adicione o valor numérico da produção no topo da barra.
(Dica: itere sobre as barras retornadas por [Link]() e use [Link]().)
(e) Experimente criar um gráfico de barras horizontais com os mesmos dados usando
[Link](). Adapte os rótulos dos eixos e a posição dos textos com os valores.
(f) Exiba ambos os gráficos (use [Link]() para separá-los ou subplots).

5. E19.5: Histograma com Matplotlib para Visualizar Distribuição (Prático)

(a) Gere um conjunto de 500 dados representando, por exemplo, o tempo de


resposta (em milissegundos) de um sistema, seguindo uma distribuição aproxi-
madamente normal com média 150ms e desvio padrão 25ms: tempos_resposta
= [Link](loc=150, scale=25, size=500).
(b) Use [Link]() para criar um histograma desses tempos_resposta.
(c) Experimente com diferentes valores para o parâmetro bins (ex: 8, 15, 30) para
ver como a aparência do histograma muda. Escolha um que pareça razoável.
(d) Adicione um título (ex: “Distribuição dos Tempos de Resposta do Sistema”),
um rótulo para o eixo X (“Tempo de Resposta (ms)”) e um rótulo para o eixo
Y (“Frequência”).
(e) Adicione edgecolor=’black’ ao [Link]() para que as bordas das barras
fiquem visíveis.
(f) Adicione uma grade vertical ([Link](axis=’y’)) e exiba o histograma.

6. E19.6: Trabalhando com Subplots no Matplotlib (Prático)


536

(a) Crie uma única figura com um tamanho adequado


(ex: [Link](figsize=(12, 8))).
(b) Configure uma grade de subplots 2x2 usando [Link]().
(c) No primeiro subplot (2, 2, 1): Plote a função y = x3 para x no intervalo
de -3 a 3. Adicione um título “y = x3 ”.
(d) No segundo subplot (2, 2, 2): Plote a função y = e−x para x no intervalo
2

de -3 a 3 (use [Link]()). Adicione um título “y = e−x ”.


2

(e) No terceiro subplot (2, 2, 3): Crie um gráfico de barras simples com 3
categorias e alguns valores. Adicione um título.
(f) No quarto subplot (2, 2, 4): Crie um gráfico de dispersão com 20 pontos
aleatórios. Adicione um título.
(g) Adicione um título geral para a figura inteira usando [Link]("Painel
de Gráficos Diversos").
(h) Use plt.tight_layout(rect=[0, 0, 1, 0.95]) para ajustar o espaçamento
e dar espaço ao supertítulo.
(i) Exiba a figura com todos os subplots.

7. E19.7: Primeiros Passos com Seaborn – histplot e kdeplot (Prático)

(a) Gere um conjunto de dados de 200 pontos seguindo uma distribuição qui-
quadrado com 5 graus de liberdade: dados_chi2 = [Link](df=5,
size=200).
(b) Defina um tema Seaborn de sua preferência no início do script
(ex: sns.set_theme(style="darkgrid", palette="crest")).
(c) Usando [Link](), crie um histograma para dados_chi2. Adicione uma
curva KDE sobreposta (kde=True). Dê um título ao gráfico (ex: “Distribuição
Qui-Quadrado com KDE (Seaborn)”). Exiba o gráfico.
(d) Em uma nova figura, use [Link]() para plotar apenas a estimativa de
densidade do kernel para os mesmos dados_chi2. Preencha a área sob a curva
(fill=True). Adicione um título. Exiba o gráfico.

8. E19.8: Gráficos Categóricos e de Relação com Seaborn (Prático)

(a) Crie um DataFrame Pandas simples (ou use arrays NumPy e passe-os para
as funções Seaborn) com dados sobre, por exemplo, o tipo de material e sua
resistência ao impacto.
1 # import pandas as pd
2 # dados_impacto = pd . DataFrame ({
3 # ’ Material ’: [ ’ Pol í mero A ’, ’ Pol í mero A ’,
’ Pol í mero B ’, ’ Pol í mero B ’, ’ Comp ó sito X ’,
’ Comp ó sito X ’] ,
4 # ’ T em per at ur aTe st e_ C ’: [20 , 20 , 20 , 50 ,
50 , 50] ,
5 # ’ R e s i s t en c i a I m p a c t o _ J ’: [12 , 15 , 25 , 22 ,
40 , 43]
6 # })

Código 21.48: Dados para E19.8


537

(Você pode criar este DataFrame ou adaptar o exemplo para usar arrays NumPy
se ainda não estiver confortável com Pandas).
(b) Use [Link]() para criar um gráfico de barras mostrando a resistência
média ao impacto por tipo de material. Use o parâmetro hue para diferenciar
por TemperaturaTeste_C.
(c) Use [Link]() para mostrar a distribuição da ResistenciaImpacto_J
para cada Material.
(d) Crie um novo conjunto de dados (ou use os mesmos) para um [Link]().
Por exemplo, duas propriedades de diferentes amostras, com uma terceira pro-
priedade categórica para o hue.
(e) Exiba cada gráfico com títulos e rótulos apropriados.

Projeto do Capítulo 19: Dashboard Simples de Análise de Dados de Ensaios


de Materiais
Título do Projeto: Painel de Visualização de Dados de Ensaios de Materiais.
Objetivos:
1. Gerar ou simular dados que poderiam ser provenientes de ensaios de materiais (ex:
ensaio de tração, testes de dureza para diferentes amostras).
2. Utilizar Matplotlib para criar múltiplos subplots organizados em uma única figura,
formando um pequeno “dashboard”.
3. Criar diferentes tipos de gráficos (linha, dispersão, barras, histograma) para visualizar
diferentes aspectos e comparações dos dados simulados.
4. Personalizar os gráficos com títulos, rótulos de eixos claros (com unidades), legendas
e estilos apropriados para garantir a clareza e a interpretabilidade.
5. (Opcional, mas recomendado) Utilizar o Seaborn para um ou dois dos gráficos para
explorar suas funcionalidades e melhorar a estética.
6. Praticar o salvamento da figura final gerada em um arquivo de imagem.
Descrição dos Dados (Simulados para o Projeto): Você precisará gerar alguns
conjuntos de dados NumPy para simular os resultados de ensaios.

1. Dados para Curva Tensão vs. Deformação (Ensaio de Tração):


• Crie um array NumPy chamado deformacao com cerca de 50 a 100 pontos
espaçados uniformemente, representando a deformação específica (adimensional)
de, por exemplo, 0 até 0.05 (ou 5%). Use [Link]().
• Crie um array NumPy chamado tensao_MPa que simule um comportamento
de material dúctil:
– Uma região elástica linear inicial (Tensão = E * Deformação, onde E é o
módulo de Young, ex: 200e3 MPa).
– Um ponto de escoamento.
– Uma região de encruamento (aumento da tensão com a deformação, mas
com uma inclinação menor).
538

– Um ponto de tensão máxima.


– Uma região de estricção (queda de tensão) até a fratura.
Simplificação: Para facilitar, você pode modelar isso com [Link] ou criar
seções. Uma forma ainda mais simples seria uma relação como T = A·d+B ·d2 −
C ·d3 com constantes A, B, C escolhidas para dar uma forma razoável, ou apenas
uma linha elástica seguida de um patamar plástico. Adicione um pouco de
ruído aleatório aos dados de tensão (+ [Link](len(deformacao))
* fator_ruido).

2. Resultados de Testes de Dureza Brinell para Diferentes Ligas Metálicas:

• Crie uma lista de nomes de ligas (strings): ligas = [’Aço Carbono’, ’Alumínio
7075’, ’Bronze Fosforoso’, ’Titânio Gr.5’].
• Crie uma lista ou array NumPy com valores médios de dureza Brinell (HB)
fictícios para cada liga: dureza_hb = [120, 150, 110, 330].

3. Distribuição da Resistência ao Escoamento de um Lote de Amostras:

• Gere um array NumPy com 200 amostras de resistência ao escoamento (em MPa)
para um determinado material, simulando que elas seguem uma distribuição
aproximadamente normal. Use [Link](loc=media_resistencia,
scale=desvio_padrao_resistencia, size=200). Escolha valores razoáveis
para média e desvio (ex: média 350 MPa, desvio 15 MPa).

Tarefas de Visualização (em uma Figura com Subplots): Crie uma figura
Matplotlib com um layout de 2x2 subplots (usando [Link]() e [Link](), ou
a interface orientada a objetos com fig, axs = [Link](2, 2, ...)).

1. Subplot 1 (Superior Esquerdo): Curva Tensão vs. Deformação

• Use [Link]() (ou axs[0,0].plot()) para desenhar a curva com deformacao


no eixo X e tensao_MPa no eixo Y.
• Adicione um título claro: “Curva Tensão-Deformação Típica”.
• Rotule os eixos: “Deformação Específica (adimensional)” e “Tensão Nominal
(MPa)”.
• Adicione uma grade (grid(True)).
• (Opcional) Marque um ponto de escoamento aproximado no gráfico usando
[Link]() ou [Link]().

2. Subplot 2 (Superior Direito): Gráfico de Barras da Dureza Média

• Use [Link]() (ou axs[0,1].bar()) para criar um gráfico de barras compa-


rando a dureza Brinell média das diferentes ligas metálicas.
• Adicione um título: “Dureza Brinell Média de Ligas Metálicas”.
• Rotule o eixo X com os nomes das ligas e o eixo Y com “Dureza Brinell (HB)”.
• Adicione os valores numéricos no topo de cada barra.

3. Subplot 3 (Inferior Esquerdo): Histograma da Resistência ao Escoamento


539

• Use [Link]() (ou axs[1,0].hist()) para mostrar a distribuição das 200


amostras de resistência ao escoamento. Escolha um número apropriado de bins.
• Adicione edgecolor=’black’ para melhor visualização das barras.
• Adicione um título: “Distribuição da Resistência ao Escoamento do Lote”.
• Rotule o eixo X como “Resistência ao Escoamento (MPa)” e o eixo Y como
“Frequência”.
• (Opcional Seaborn) Use [Link](..., kde=True, ax=axs[1,0]) para
adicionar uma curva KDE.
4. Subplot 4 (Inferior Direito): Gráfico de Dispersão (Escolha Livre ou
Sugestão)
• Crie dois arrays NumPy, x_dados_dispersao e y_dados_dispersao, com al-
guma relação simulada (ex: y é uma função linear de x com algum ruído, ou
uma relação quadrática, ou dados de duas propriedades de materiais que você
queira correlacionar).
• Use [Link]() (ou axs[1,1].scatter()) para plotar esses pontos.
• Adicione um título e rótulos apropriados para os eixos.
• (Opcional Seaborn) Se você tiver uma terceira variável categórica ou numé-
rica, use [Link](..., hue=..., size=..., ax=axs[1,1]) para
enriquecer o gráfico.
5. Ajustes Finais da Figura:
• Adicione um título geral para a figura inteira usando [Link]() (ou
[Link]()).
• Use plt.tight_layout() (ou fig.tight_layout()) para ajustar automati-
camente o espaçamento entre os subplots e evitar sobreposições. Pode ser
necessário usar o argumento rect se o supertítulo estiver sendo cortado.
• Salve a figura completa em um arquivo (ex: dashboard_ensaios.png) usando
[Link]() com uma resolução adequada (ex: dpi=300) e
bbox_inches=’tight’.
• Exiba a figura final com [Link]().
Desafio Extra (Opcional):
1. Mais Seaborn: Converta mais um dos gráficos Matplotlib para sua versão Seaborn,
explorando as opções de personalização do Seaborn (ex: [Link] para o gráfico
de dureza, ou [Link] para a curva tensão-deformação se você tiver múltiplas
curvas para comparar).
2. Anotações Detalhadas: No gráfico tensão-deformação, use [Link]() (ou
[Link]()) para marcar e rotular pontos específicos de interesse, como o limite
de proporcionalidade (aproximado), o limite de escoamento (aproximado), e o limite
de resistência à tração (ponto máximo da curva).
3. Dados Reais (se disponíveis): Se você tiver acesso a dados reais de ensaios de
materiais ou de outros experimentos de engenharia (mesmo que em formato CSV
simples), tente adaptar o projeto para visualizar esses dados em vez dos simulados.
540

Capítulo 20: Pandas para Análise e Manipulação de Dados (Exer-


cícios e Projeto)
Neste capítulo, mergulhamos na biblioteca Pandas, a principal ferramenta em Python
para manipulação e análise de dados tabulares. Exploramos suas duas estruturas de dados
fundamentais, a Series (unidimensional) e o DataFrame (bidimensional), e aprendemos a
criar, inspecionar, selecionar, filtrar, limpar, transformar, agrupar e combinar dados, além
de ler e escrever dados em formatos comuns como CSV. Os exercícios e o projeto a seguir
têm como objetivo consolidar seu conhecimento prático com Pandas.
Lembrete de Importações Comuns: Para a maioria dos exercícios, você precisará
das seguintes importações:
1 import pandas as pd
2 import numpy as np # Frequentemente usado com Pandas , ex : para
criar NaN

Código 21.49: Importações padrão para Capítulo 20

Exercícios
1. E20.1: Criando e Inspecionando Pandas Series (Prático)

(a) Crie uma Series Pandas chamada populacao_cidades a partir de uma lista
contendo a população aproximada de 5 cidades brasileiras à sua escolha (use
números inteiros). Use os nomes das cidades (strings) como o index da Series.
Dê um nome à Series usando o parâmetro name (ex: "População Estimada
2025"). Imprima a Series completa.
(b) Crie uma Series Pandas chamada propriedades_material a partir de um
dicionário Python. As chaves do dicionário devem ser nomes de propriedades de
um material de engenharia (ex: “Densidade”, “Limite de Escoamento”, “Módulo
de Young”), e os valores devem ser os respectivos valores numéricos (podem ser
float) acompanhados de suas unidades (como strings, ex: “7.85 g/cm3̂”, “350
MPa”). Imprima a Series.
(c) A partir da Series populacao_cidades criada no item (a), acesse e imprima
a população de uma cidade específica usando seu rótulo de índice.
(d) Para a Series propriedades_material do item (b), imprima seus values (os
dados como array NumPy), seu index (os rótulos) e seu dtype (o tipo de dado
dos valores).

2. E20.2: Criando e Inspecionando Pandas DataFrames (Prático)

(a) Crie um DataFrame Pandas chamado df_estudantes a partir do seguinte dici-


onário de listas, que contém informações sobre alguns estudantes de engenharia:
1 dados_estudantes_engenharia = {
2 ’ Nome ’: [ ’ Juliana Paes ’ , ’ Ricardo Pinto ’ ,
’ Beatriz Alves ’ , ’ Fernando Costa ’] ,
3 ’ Curso ’: [ ’ Eng . Qu í mica ’ , ’ Eng . Mec â nica ’ ,
’ Eng . Civil ’ , ’ Eng . de Produ ç ã o ’] ,
4 ’ AnoIngresso ’: [2021 , 2020 , 2022 , 2021] ,
5 ’ C o e f i c i e n t e R e n d i m e n t o ’: [8.2 , 7.5 , 9.0 , 7.9]
6 }
541

Código 21.50: Dados para DataFrame de estudantes (E20.2a)

Use uma lista de IDs personalizados como índice para as linhas (ex: [’ID01’,
’ID02’, ’ID03’, ’ID04’]). Imprima o DataFrame completo.
(b) Use o método .head(2) para visualizar apenas as duas primeiras linhas do
df_estudantes.
(c) Use o método .info() para obter um resumo conciso do df_estudantes,
incluindo os tipos de dados de cada coluna e a contagem de valores não nulos.
(d) Use o método .describe() para obter estatísticas descritivas das colunas
numéricas do df_estudantes.
(e) Imprima os nomes das colunas do DataFrame (usando o atributo .columns) e
o índice das linhas (usando o atributo .index).

3. E20.3: Seleção e Indexação em DataFrames (Prático)


Utilize o DataFrame df_estudantes criado no Exercício E20.2.

(a) Selecione e imprima apenas a coluna ’Curso’ do DataFrame. Qual o tipo do


objeto retornado?
(b) Selecione e imprima as colunas ’Nome’ e ’CoeficienteRendimento’. Qual o tipo
do objeto retornado?
(c) Use o indexador .loc[] para selecionar e imprimir a linha inteira correspondente
ao estudante com o rótulo de índice ’ID03’.
(d) Use o indexador .iloc[] para selecionar e imprimir a última linha do Data-
Frame (lembre-se da indexação negativa).
(e) Use o indexador .loc[] para selecionar as linhas dos estudantes ’ID01’ e ’ID04’
e, para estas linhas, apenas as colunas ’Curso’ e ’AnoIngresso’. Imprima o
sub-DataFrame resultante.
(f) Use o indexador .iloc[] para selecionar as duas primeiras linhas (posições 0 e
1) e as três primeiras colunas (posições 0, 1 e 2). Imprima o resultado.
(g) Crie uma condição booleana (uma máscara) para selecionar todos os estudantes
cujo ’AnoIngresso’ foi 2021. Use esta máscara para imprimir o sub-DataFrame
correspondente.
(h) Crie uma condição booleana para selecionar todos os estudantes do curso
’Eng. Elétrica’ (se não houver, escolha um curso que exista) E que tenham
um ’CoeficienteRendimento’ maior que 8.0. Imprima o DataFrame filtrado.
(Lembre-se de usar & para E lógico e parênteses para cada condição).

4. E20.4: Lidando com Dados Ausentes (NaN) (Prático)


Crie o seguinte DataFrame, que contém alguns valores ausentes ([Link]):
1 # ( import pandas as pd , import numpy as np )
2 dados_com_nan_exemplo = {
3 ’ Sensor_A ’: [22.1 , 23.5 , np . nan , 22.9 , 24.0 , np . nan ] ,
4 ’ Sensor_B ’: [1010 , np . nan , 1012 , 1011 , np . nan , 1010] ,
5 ’ Sensor_C ’: [5 , 6 , 7 , 8 , 9 , 10] ,
6 ’ Sensor_D_Falha ’: [ np . nan , np . nan , np . nan , np . nan ,
np . nan , np . nan ]
542

7 }
8 df_com_nan = pd . DataFrame ( d a d o s _ c o m _ n a n _ e x e m p l o )

Código 21.51: DataFrame com dados ausentes para E20.4

(a) Imprima o df_com_nan original.


(b) Use .isnull().sum() para contar e imprimir quantos valores NaN existem em
cada coluna.
(c) Use o método .dropna() para criar um novo DataFrame chamado
df_sem_linhas_nan que remove todas as linhas que contenham pelo menos
um valor NaN. Imprima o resultado.
(d) Use .dropna(axis=1, how=’all’) para criar um novo DataFrame chamado
df_sem_colunas_totalmente_nan que remove apenas as colunas onde todos
os valores são NaN. Imprima o resultado.
(e) Crie um novo DataFrame df_nan_preenchido_zeros preenchendo todos os
NaNs do df_com_nan original com o valor 0, usando .fillna(0). Imprima o
resultado.
(f) Crie um novo DataFrame. Para a coluna ’Sensor_A’, preencha os NaNs com
a média dos valores existentes naquela coluna. Para a coluna ’Sensor_B’,
preencha os NaNs usando o método method=’ffill’ (forward-fill). Imprima o
DataFrame resultante.

5. E20.5: Operações Comuns e Agrupamento (Prático)


Use o DataFrame df_estudantes do Exercício E20.2 (redefina-o se necessário).
1 # df_estudantes = pd . DataFrame ({
2 # ’ Nome ’: [ ’ Juliana Paes ’, ’ Ricardo Pinto ’,
’ Beatriz Alves ’, ’ Fernando Costa ’] ,
3 # ’ Curso ’: [ ’ Eng . Qu í mica ’, ’ Eng . Mec â nica ’, ’ Eng .
Civil ’, ’ Eng . de Produ ç ã o ’] ,
4 # ’ AnoIngresso ’: [2021 , 2020 , 2022 , 2021] ,
5 # ’ C o e f i c i e n t e R e n d i m e n t o ’: [8.2 , 7.5 , 9.0 , 7.9]
6 # } , index =[ ’ ID01 ’, ’ ID02 ’, ’ ID03 ’, ’ ID04 ’])

Código 21.52: DataFrame df_estudantes para E20.5

(a) Adicione uma nova coluna ao df_estudantes chamada ’SemestreAtual_2025’


que seja calculada como (2025 - df_estudantes[’AnoIngresso’]) * 2 + 1
(uma aproximação do semestre atual em 2025, considerando 2 semestres por
ano).
(b) Calcule e imprima a média do ’CoeficienteRendimento’ e do ’AnoIngresso’ de
todos os estudantes.
(c) Use o método groupby(’Curso’) para agrupar os estudantes por curso. Em
seguida, calcule e imprima a média do ’CoeficienteRendimento’ para cada curso.
(d) Conte quantos estudantes existem em cada ’AnoIngresso’ usando groupby() e
o método .size() (ou .count()).
(e) Ordene o DataFrame df_estudantes pelo ’CoeficienteRendimento’ em ordem
decrescente e imprima as primeiras 2 linhas do resultado.
543

6. E20.6: Lendo e Escrevendo Arquivos CSV com Pandas (Prático)

(a) Crie um DataFrame Pandas simples, com pelo menos 3 colunas (ex: ’ID_Peca’,
’Material’, ’Peso_kg’) e 4 linhas de dados fictícios relevantes para engenharia.
(b) Use o método df.to_csv() para salvar este DataFrame em um arquivo chamado
pecas_engenharia.csv. Certifique-se de que o índice do DataFrame não seja
salvo no arquivo CSV (use index=False). Use ponto e vírgula (’;’) como
separador.
(c) Agora, use a função pd.read_csv() para ler o arquivo pecas_engenharia.csv
que você acabou de criar de volta para um novo DataFrame chamado
df_pecas_lido. Lembre-se de especificar o delimitador correto (sep=’;’).
(d) Imprima o df_pecas_lido para verificar se os dados foram carregados correta-
mente.

Projeto do Capítulo 20: Análise Exploratória de um Pequeno Conjunto de


Dados CSV de Emissões Veiculares
Título do Projeto: Análise Exploratória de Dados de Emissões Veiculares com
Pandas.
Objetivos:
1. Praticar a leitura de um arquivo CSV para um DataFrame Pandas.

2. Realizar inspeção básica do DataFrame para entender sua estrutura e tipos de dados
(.head(), .info(), .describe()).

3. Praticar seleção e filtragem de dados usando .loc[], .iloc[] e indexação booleana


para responder a perguntas específicas sobre os dados.

4. Lidar com possíveis dados ausentes (identificar e decidir uma estratégia simples como
remoção ou preenchimento).

5. Realizar cálculos de estatísticas descritivas e agrupamentos (groupby()) para obter


insights.

6. (Opcional) Utilizar os conhecimentos dos capítulos anteriores (Matplotlib/Seaborn)


para criar algumas visualizações simples dos resultados da análise.
Conjunto de Dados (Arquivo emissoes_veiculos.csv): Crie manualmente um
arquivo chamado emissoes_veiculos.csv no mesmo diretório do seu script Python, com
o seguinte conteúdo (ou um similar, com pelo menos 10-15 linhas e algumas variações):
Modelo,Fabricacao,Combustivel,EmissaoCO2,Consumo_km_l,Cilindrada_L
Carro A,2020,Gasolina,120,13.5,1.6
Carro B,2021,Etanol,105,9.8,1.8
Carro C,2020,Diesel,150,11.2,2.0
Carro D,2022,Gasolina,110,14.1,1.4
Carro A,2021,Gasolina,118,13.7,1.6
Carro E,2022,Hibrido,70,20.5,1.5
Carro B,2020,Etanol,108,9.5,1.8
Carro F,2023,Eletrico,0,,1.0
544

Carro C,2021,Diesel,,11.0,2.0
Carro G,2022,Gasolina,130,12.0,2.0
Carro H,2020,Gasolina,125,13.1,1.6
Carro I,2023,Hibrido,65,21.5,1.5
Carro J,2021,Etanol,112,9.2,1.8

Observação: Note que há alguns valores ausentes propositalmente (ex: Consumo para
Carro F, Emissão para Carro C em 2021).
Tarefas da Análise Exploratória:

1. Carregar e Inspecionar os Dados:

• Leia o arquivo emissoes_veiculos.csv para um DataFrame Pandas chamado


df_emissoes.
• Exiba as primeiras 7 linhas do DataFrame usando .head(7).
• Use .info() para obter um resumo dos tipos de dados e da contagem de valores
não nulos por coluna.
• Use .describe(include=’all’) para obter estatísticas descritivas de todas
as colunas. Observe quais colunas têm dados ausentes.

2. Tratamento Básico de Dados Ausentes:

• Conte quantos valores ausentes existem em cada coluna.


• Decida uma estratégia para tratar os valores ausentes. Algumas sugestões (você
pode escolher uma ou combinar):
– Para a coluna EmissaoCO2_g_km do carro elétrico, preencha com 0.
– Para outras colunas numéricas com poucos dados ausentes (como
Consumo_km_l ou EmissaoCO2_g_km em outros casos), você pode optar por
preencher com a média ou mediana daquela coluna, ou remover as linhas com
esses dados ausentes se forem poucas e não impactarem significativamente
a análise.
– Justifique sua escolha em comentários no código.
• Aplique sua estratégia e verifique novamente com .info() se os dados ausentes
foram tratados.

3. Seleção e Filtragem de Dados para Responder Perguntas: Usando as técnicas


de seleção e indexação do Pandas:

• a) Quais modelos de carro foram fabricados em 2022? Imprima a lista de


modelos.
• b) Quais carros a Gasolina têm uma emissão de CO2 (EmissaoCO2_g_km) menor
que 115 g/km? Exiba o Modelo, Ano e Emissão.
• c) Qual é o consumo médio (Consumo_km_l) dos carros a Etanol?
• d) Quais carros são Híbridos ou Elétricos? Exiba todas as suas informações.
• e) Selecione todos os carros cujo ano de fabricação é 2020 ou 2021 e que
são do tipo Gasolina ou Etanol. Mostre apenas as colunas Modelo, Ano,
TipoCombustivel e Consumo_km_l.
545

4. Análise Agrupada com groupby():

• a) Agrupe os dados por TipoCombustivel. Para cada tipo de combustível,


calcule e imprima:
– A média da EmissaoCO2_g_km.
– A média do Consumo_km_l.
– O número de modelos diferentes (.size() ou .count() em uma coluna).
• b) Agrupe os dados por AnoFabricacao. Para cada ano, encontre o carro com
a menor EmissaoCO2_g_km (e qual é esse modelo e emissão). (Dica: agrupe,
depois use .min() ou .idxmin() com seleção).

5. (Opcional) Criação de Novas Colunas e Visualização Simples:

• a) Crie uma nova coluna no DataFrame chamada EficienciaCO2 calculada


como Consumo_km_l / EmissaoCO2_g_km (para carros com emissão > 0). Para
carros elétricos (emissão = 0), você pode definir um valor muito alto ou [Link].
Tenha cuidado com a divisão por zero.
• b) Use Matplotlib ou Seaborn para criar um gráfico de barras mostrando a
emissão média de CO2 por TipoCombustivel.
• c) Crie um gráfico de dispersão de Consumo_km_l versus EmissaoCO2_g_km,
colorindo os pontos pelo TipoCombustivel.

Desafio Extra Avançado (Opcional):

1. Normalização de Dados: Escolha uma coluna numérica (ex: EmissaoCO2_g_km)


e normalize seus valores para que fiquem na escala de 0 a 1 (Min-Max scaling:
Xnorm = (X − Xmin )/(Xmax − Xmin )). Adicione isso como uma nova coluna.

2. Análise por Faixas: Use [Link]() para criar faixas de emissão de CO2 (ex:
"Baixa", "Média", "Alta") e conte quantos carros existem em cada faixa.

Capítulo 21: SciPy para Computação Científica (Introdução) (Exer-


cícios e Projeto)
Neste capítulo, demos uma introdução à biblioteca SciPy, que complementa o NumPy
com um vasto conjunto de algoritmos e ferramentas para computação científica e de
engenharia. Exploramos brevemente submódulos como [Link] (funções espe-
ciais), [Link] (álgebra linear avançada), [Link] (otimização e busca
de raízes), [Link] (interpolação), [Link] (integração numérica),
[Link] (processamento de sinais) e [Link] (estatísticas). Os exercícios e
o projeto a seguir têm como objetivo incentivar a exploração inicial de algumas dessas
funcionalidades.
Lembrete de Importações Comuns: Para os exercícios, você provavelmente pre-
cisará importar numpy as np, e submódulos ou funções específicas do SciPy, como from
scipy import stats, from [Link] import quad, etc. Não se esqueça do
import [Link] as plt para visualizações.
546

Exercícios
1. E21.1: Explorando Funções Especiais com [Link] (Prático)
(a) Importe o submódulo special de scipy.
(b) Calcule e imprima o valor da função Gama para z = 4.5, ou seja, Γ(4.5), usando
[Link]().
(c) Calcule e imprima o valor da função de erro complementar, erfc(0.8), usando
[Link]().
(d) Usando [Link](n, x), calcule e imprima o valor da função de Bessel de
primeira espécie de ordem n = 1 no ponto x = 2.5, ou seja, J1 (2.5).
(e) Obtenha o objeto polinomial para o polinômio de Legendre de grau n = 2
usando [Link](2). Imprima o objeto e também suas raízes (usando
o atributo .roots do objeto polinomial).
2. E21.2: Operações de Álgebra Linear com [Link] (Prático)
Crie as seguintes matrizes NumPy:
1 import numpy as np
2 import scipy . linalg as spla # Alias comum para scipy . linalg
3
4 matriz_A_linalg = np . array ([[1. , 2. , 3.] ,
5 [1. , 1. , 1.] ,
6 [3. , 3. , 1.]])
7 matriz_B_linalg = np . array ([[0. , 1. , 0.] ,
8 [1. , 0. , 0.] ,
9 [0. , 0. , 1.]])

Código 21.53: Matrizes para E21.2

(a) Calcule e imprima a decomposição LU da matriz_A_linalg usando [Link]().


Imprima as matrizes P (permutação), L (triangular inferior) e U (triangular
superior) resultantes. Verifique se P @ L @ U é aproximadamente igual à
matriz_A_linalg original (use [Link]()).
(b) Calcule e imprima a exponencial da matriz_B_linalg (eB ) usando [Link]().
(c) Calcule e imprima os valores singulares (o vetor s) da matriz_A_linalg usando
a decomposição SVD com [Link](). (Lembre-se que [Link]() retorna U,
s, Vh).

3. E21.3: Busca de Raízes e Otimização Escalar com [Link] (Prático)


(a) Defina uma função Python f (x) = x2 − 2x − 8. Use a função root_scalar
do [Link] para encontrar uma raiz desta função. Tente fornecer um
intervalo bracket onde você sabe que a função muda de sinal (ex: para as
raízes -2 e 4, você pode usar intervalos como [−3, 0] e [3, 5]). Imprima a raiz
encontrada e verifique se a busca convergiu.
(b) Defina uma função Python g(x) = x3 − 7x2 + 8x − 0.25. Use a função
minimize_scalar do [Link] para encontrar um mínimo local desta
função. Você pode precisar fornecer um bracket ou bounds para guiar a busca
(ex: bounds=(0, 5)). Imprima o valor de x onde ocorre o mínimo e o valor da
função g(x) nesse ponto.
547

4. E21.4: Interpolação Simples com [Link] (Prático)


Considere os seguintes pontos de dados (x, y): x_conhecidos = [Link]([0, 1,
2.5, 3.6, 5.0, 6.2, 7.0])
y_conhecidos = [Link]([0.2, 0.9, 1.1, 1.5, 0.8, 0.5, 0.3])
(a) Importe interp1d de [Link].
(b) Crie uma função de interpolação linear (usando kind=’linear’) a partir desses
pontos.
(c) Use a função interpoladora criada para estimar os valores de y para os se-
guintes novos pontos x: x_novos = [Link]([0.5, 1.7, 3.0, 4.5, 6.5]).
Imprima os valores estimados.
(d) (Opcional) Crie uma função de interpolação cúbica (kind=’cubic’) com os
mesmos dados e estime os valores para x_novos. Compare com os resultados
da interpolação linear.
(e) (Opcional) Plote os pontos originais e as duas curvas interpoladas (linear e
cúbica) em um mesmo gráfico Matplotlib para visualizar as diferenças.
5. E21.5: Integração Numérica com [Link] (Prático)
(a) Importe quad, trapezoid, simpson de [Link].
(b) Defina uma função Python f (x) = sin(x2 ) + cos(x). Use a função quad() para
calcular a integral definida de f (x) no intervalo de x = 0 a x = π. Imprima o
resultado da integral e a estimativa do erro.
(c) Considere os seguintes dados amostrados y em pontos x uniformemente espaça-
dos (você pode assumir dx = 0.2 ou criar um array x):
y_amostras_integral = [Link]([0.0, 0.5, 0.8, 0.9, 0.7, 0.3, 0.1])
x_amostras_integral = [Link](0, 1.3, 0.2) (são 7 pontos, então de 0
a 1.2 com passo 0.2)
(d) Calcule a integral aproximada desses dados usando a Regra do Trapézio
(trapezoid(y_amostras_integral, x=x_amostras_integral)). Imprima o
resultado.
(e) Calcule a integral dos mesmos dados amostrados usando a Regra de Simp-
son (simpson(y_amostras_integral, x=x_amostras_integral)). Imprima
o resultado e compare com o do trapézio.
6. E21.6: Trabalhando com Distribuições Estatísticas usando [Link]
(Prático)
(a) Importe o submódulo stats de scipy.
(b) Crie um objeto representando uma distribuição t de Student com 10 graus de
liberdade (df=10): dist_t = stats.t(df=10).
(c) Gere 5 amostras aleatórias desta distribuição dist_t usando o método
.rvs(size=5, random_state=42). Imprima as amostras.
(d) Calcule e imprima a PDF (Função Densidade de Probabilidade) desta distribui-
ção dist_t para o valor x = 0 e x = 1.5.
(e) Calcule e imprima a CDF (Função de Distribuição Cumulativa) para x = 1.0
(ou seja, P (X ≤ 1.0)).
548

(f) Encontre o valor x tal que P (X ≤ x) = 0.95 para esta distribuição dist_t (ou
seja, o percentil 95), usando o método .ppf(0.95). Imprima o resultado.
(g) (Conceitual) Para que tipo de situação em engenharia ou análise de dados a
distribuição t de Student é frequentemente utilizada em vez da distribuição
normal?

Projeto do Capítulo 21: Análise Introdutória de um Sinal Simulado com Ruído


Título do Projeto: Análise Introdutória de um Sinal Senoidal com Ruído usando
NumPy e SciPy.
Objetivos:

1. Utilizar o NumPy para gerar um sinal senoidal “limpo” e para adicionar ruído a ele,
criando um sinal “ruidoso”.

2. Aplicar uma função de interpolação do submódulo [Link] para rea-


mostrar (ou obter valores em pontos específicos) do sinal ruidoso.

3. (Opcional, mas recomendado) Explorar e aplicar uma técnica de filtragem muito


básica do submódulo [Link] para tentar atenuar o ruído do sinal.

4. Realizar uma integração numérica simples do sinal (limpo ou ruidoso) sobre um


intervalo usando [Link].

5. Visualizar os sinais original, ruidoso, interpolado e (opcionalmente) filtrado usando


Matplotlib.

Descrição do Problema: Em muitas aplicações de engenharia, os sinais adquiridos


de sensores ou sistemas vêm contaminados com ruído. Este projeto visa simular esse
cenário e aplicar algumas técnicas básicas de NumPy e SciPy para analisar e processar tal
sinal.
Tarefas Detalhadas:

1. Geração do Sinal (com NumPy):

• Crie um vetor de tempo t indo de 0 a 2 segundos, com um número suficiente


de pontos (ex: 200 pontos, usando [Link]()).
• Crie um sinal “limpo” (sem ruído) que seja uma senóide. Por exemplo, slimpo (t) =
A sin(2πf t + ϕ), onde A é a amplitude (ex: 1.0), f é a frequência (ex: 2 Hz), e
ϕ é a fase inicial (ex: 0).
• Gere um array de “ruído” gaussiano com a mesma forma que t, usando
[Link](loc=0, scale=amplitude_ruido, size=[Link]). Es-
colha uma amplitude_ruido (ex: 0.3 ou 0.5) que seja perceptível mas não
domine completamente o sinal.
• Crie o sinal_ruidoso somando o sinal_limpo com o array de ruido.
• Visualização Inicial (Matplotlib): Plote o sinal_limpo e o sinal_ruidoso
no mesmo gráfico contra o tempo t. Use legendas, títulos e rótulos apropriados.

2. Interpolação do Sinal Ruidoso ([Link]):


549

• Suponha que você queira estimar os valores do sinal_ruidoso em um novo


conjunto de pontos de tempo, talvez mais denso ou em pontos específicos. Crie
um novo vetor de tempo t_interpolacao (ex: 400 pontos entre 0 e 2s).
• Use interp1d de [Link] para criar uma função de interpolação
(ex: linear ou cúbica) baseada nos seus dados originais t e sinal_ruidoso.
• Use esta função interpoladora para gerar o sinal_interpolado nos pontos de
t_interpolacao.
• Visualização: Em um novo gráfico, plote o sinal_ruidoso original (talvez
como pontos ou uma linha mais clara) e o sinal_interpolado (como uma
linha) para ver o resultado da interpolação.

3. Integração Numérica do Sinal ([Link]):

• Calcule a integral (área sob a curva) do sinal_limpo ao longo de todo o


intervalo de tempo t usando a função trapezoid(sinal_limpo, x=t) (ou
simpson). Imprima o resultado.
• Faça o mesmo para o sinal_ruidoso. Calcule sua integral usando
trapezoid(sinal_ruidoso, x=t). Imprima e compare com a integral do sinal
limpo. O ruído afetou muito o valor da integral?

4. (Desafio Opcional) Filtragem Simples do Ruído ([Link]):

• Pesquise sobre filtros de média móvel ou o filtro de Savitzky-Golay


([Link].savgol_filter) como uma forma de suavizar o sinal ruidoso.
• Exemplo de média móvel simples (você pode implementar ou usar [Link]
de forma inteligente): Crie uma janela de média (ex: janela = [Link](5)/5.0
para uma média de 5 pontos). Use [Link](sinal_ruidoso, janela,
mode=’same’) para aplicar o filtro. (A convolução é uma operação do [Link]
também, mas [Link] é mais simples para este caso).
• Ou, tente usar [Link](sinal_ruidoso, kernel_size=5) (fil-
tro de mediana).
• Plote o sinal_ruidoso original e o sinal_filtrado no mesmo gráfico para
ver se o ruído foi atenuado.

5. Apresentação dos Resultados: Para cada etapa principal do projeto, imprima


os resultados numéricos importantes e mostre os gráficos gerados. Certifique-se de
que todos os gráficos tenham títulos, rótulos de eixos e legendas claras. Adicione
comentários no seu código explicando cada passo.

Considerações Finais do Projeto: Este projeto é uma introdução. O processamento


de sinais e a análise estatística são campos vastos. O objetivo aqui é dar a você um
primeiro contato prático com a aplicação de algumas ferramentas do NumPy e SciPy em
um problema que simula um cenário de engenharia.
Apêndice A

Guia Rápido de Sintaxe Python

Este apêndice serve como um guia de referência rápida para as principais construções
de sintaxe e comandos da linguagem Python que foram abordados ao longo deste livro.
Ele não substitui as explicações detalhadas dos capítulos, mas pode ser útil para relembrar
rapidamente como escrever certas estruturas ou usar funções comuns.

A.1 Comentários
• Comentário de linha única: Começa com #. Tudo após o # na linha é ignorado.
1 # Este é um coment á rio de linha ú nica
2 x = 10 # Coment á rio ap ó s o c ó digo

• Comentário de múltiplas linhas (docstring para documentação, mas também usado


para blocos): Usando aspas triplas "..." ou ”’...”’.
1 """
2 Este é um coment á rio
3 de m ú ltiplas linhas ( ou uma docstring ) .
4 """
5 x = 5
6 ’ ’ ’ Tamb é m pode ser usado para
7 coment á rios de bloco , embora docstrings
8 tenham um prop ó sito mais formal . ’ ’ ’

A.2 Variáveis, Atribuição e Tipos de Dados Primitivos


• Atribuição de Variáveis:
1 nome_variavel = valor
2 idade = 30
3 preco = 19.99
4 mensagem = " Ol á , Python ! "
5 ativo = True

• Nomes de Variáveis (Convenção snake_case): Letras minúsculas, palavras


separadas por underscore. Deve começar com letra ou underscore.

• Tipos de Dados Primitivos Comuns:

550
A.3. Entrada e Saída de Dados Básica 551

– Inteiros (int): 10, -5, 0


– Ponto Flutuante (float): 3.14, -0.001, 2.0
– Strings (str): "texto", ’outro texto’, "texto longo"
– Booleanos (bool): True, False (com T e F maiúsculos)
– Tipo Nulo (NoneType): None (representa a ausência de valor)

• Verificando o Tipo:
1 x = 10
2 print ( type ( x ) ) # Sa í da : < class ’ int ’>

• Conversão de Tipo (Casting):


1 num_str = " 123 "
2 num_int = int ( num_str )
3
4 num_float = float ( " 3.14 " )
5 num_int_de_float = int (3.9) # Trunca para 3
6
7 resultado_str = str (100)
8 valor_bool = bool (0) # False
9 valor_bool2 = bool ( " Texto " ) # True

A.3 Entrada e Saída de Dados Básica


• Saída com print():
1 print ( " Ol á , mundo " )
2 nome = " Alan "
3 idade = 30
4 print ( " Nome : " , nome , " Idade : " , idade ) # M ú ltiplos
argumentos , separados por espa ç o
5 print ( " Linha 1 " , end = " » " ) # ’ end ’ especifica o que vai
ao final ( padr ã o é ’\ n ’)
6 print ( " Linha 2 " )
7 print ( " Item1 " , " Item2 " , " Item3 " , sep = " | " ) # ’ sep ’
especifica o separador

• F-strings (Strings Literais Formatadas):


1 valor = 42.12345
2 print ( f " O valor é { valor } " )
3 print ( f " O valor formatado com 2 casas decimais é :
{ valor :.2 f } " )
4 print ( f " Uma express ã o : {10 + 5} " )

• Entrada do Usuário com input(): (Sempre retorna uma string)


1 nome_usuario = input ( " Digite seu nome : " )
2 idade _str_usu ario = input ( " Digite sua idade : " )
3 # Lembre - se de converter para o tipo num é rico se
necess á rio :
4 # idade_i nt_usuar io = int ( i dade_str _usuario )
552 Apêndice A. Guia Rápido de Sintaxe Python

A.4 Operadores
Python oferece uma variedade de operadores para realizar operações aritméticas, de
atribuição, comparação, lógicas, de identidade e de associação.

A.4.1 Operadores Aritméticos


Usados para realizar cálculos matemáticos.
• + : Adição (ex: 5 + 3 resulta em 8)
• - : Subtração (ex: 5 - 3 resulta em 2)
• * : Multiplicação (ex: 5 * 3 resulta em 15)
• / : Divisão (sempre resulta em um float, ex: 7 / 2 resulta em 3.5)
• //: Divisão Inteira (descarta a parte fracionária, ex: 7 // 2 resulta em 3)
• %: Módulo (resto da divisão inteira, ex: 7 % 2 resulta em 1)
• **: Potenciação (ex: 5 ** 2 resulta em 25, ou seja, 52 )
1 a = 10
2 b = 3
3 print ( f " { a } + {b} = {a + b}") # 13
4 print ( f " { a } / {b} = {a / b}") # 3.333...
5 print ( f " { a } // { b } = { a // b } " ) # 3
6 print ( f " { a } % {b} = {a % b}") # 1
7 print ( f " { a } ** { b } = { a ** b } " ) # 1000

Código A.1: Exemplos de operadores aritméticos

A.4.2 Operadores de Atribuição


Usados para atribuir valores a variáveis.
• = : Atribuição simples (ex: x = 5)
• +=: Atribuição de adição (ex: x += 3 é o mesmo que x = x + 3)
• -=: Atribuição de subtração (ex: x -= 2 é x = x - 2)
• *=: Atribuição de multiplicação (ex: x *= 4 é x = x * 4)
• /=: Atribuição de divisão (ex: x /= 2 é x = x / 2)
• //=: Atribuição de divisão inteira (ex: x //= 2 é x = x // 2)
• %=: Atribuição de módulo (ex: x %= 3 é x = x % 3)
• **=: Atribuição de potenciação (ex: x **= 2 é x = x ** 2)
1 contador = 0
2 contador += 1 # contador agora é 1
3 print ( f " Contador : { contador } " )

Código A.2: Exemplo de operador de atribuição composta


A.4. Operadores 553

A.4.3 Operadores de Comparação (Relacionais)


Usados para comparar dois valores. Retornam um valor booleano (True ou False).

• ==: Igual a

• !=: Diferente de

• > : Maior que

• < : Menor que

• >=: Maior ou igual a

• <=: Menor ou igual a

1 x = 5
2 y = 10
3 print ( f " { x } == { y }? { x == y } " ) # False
4 print ( f " { x } < { y }? { x < y } " ) # True

Código A.3: Exemplos de operadores de comparação

A.4.4 Operadores Lógicos (Booleanos)


Usados para combinar expressões condicionais (booleanas).

• and: Retorna True se ambas as expressões forem verdadeiras.

• or : Retorna True se pelo menos uma das expressões for verdadeira.

• not: Inverte o valor booleano de uma expressão (retorna True se a expressão for
falsa, e vice-versa).

1 idade = 25
2 t e m _ c a r t e i r a _ m o t o r i s t a = True
3 print ( f " Pode dirigir ? {( idade >= 18) and t e m _ c a r t e i r a _ m o t o r i s t a } " )
# True

Código A.4: Exemplos de operadores lógicos

Lembre-se do comportamento de curto-circuito: para a and b, se a for falso, b não é


avaliado. Para a or b, se a for verdadeiro, b não é avaliado.

A.4.5 Operadores de Identidade


Usados para comparar se duas variáveis se referem ao mesmo objeto na memória
(não apenas se têm o mesmo valor).

• is : Retorna True se ambas as variáveis apontam para o mesmo objeto.

• is not: Retorna True se as variáveis apontam para objetos diferentes.


554 Apêndice A. Guia Rápido de Sintaxe Python

1 lista_a = [1 , 2 , 3]
2 lista_b = [1 , 2 , 3]
3 lista_c = lista_a
4
5 print ( f " lista_a == lista_b ? { lista_a == lista_b } " ) # True ( valores
iguais )
6 print ( f " lista_a is lista_b ? { lista_a is lista_b } " ) # False ( objetos
diferentes )
7 print ( f " lista_a is lista_c ? { lista_a is lista_c } " ) # True ( mesmo
objeto )

Código A.5: Exemplos de operadores de identidade

A.4.6 Operadores de Associação (Pertinência)


Usados para testar se um valor é encontrado em uma sequência (como strings, listas,
tuplas) ou se uma chave existe em um dicionário.

• in : Retorna True se o valor for encontrado na sequência/coleção.

• not in: Retorna True se o valor não for encontrado.

1 numeros = [10 , 20 , 30 , 40]


2 print ( f " 30 in numeros ? {30 in numeros } " ) # True
3 print ( f " 50 not in numeros ? {50 not in numeros } " ) # True
4

5 frase = " Ol á Alan "


6 print ( f " ’ Alan ’ in frase ? { ’ Alan ’ in frase } " ) # True

Código A.6: Exemplos de operadores de associação

A.4.7 Precedência de Operadores (Breve Menção)


Assim como na matemática, os operadores em Python têm uma ordem de precedência
que determina quais operações são realizadas primeiro em uma expressão complexa (ex:
** antes de * e /, que vêm antes de + e -).

• Parênteses () podem ser usados para forçar uma ordem específica de avaliação ou
para melhorar a clareza da expressão.

Exemplo: 2 + 3 * 4 resulta em 14 (multiplicação primeiro), enquanto (2 + 3) * 4


resulta em 20. Em caso de dúvida, ou para tornar o código mais legível, use parênteses.

A.5 Estruturas de Controle Condicional


As estruturas condicionais permitem que seu programa tome decisões e execute dife-
rentes blocos de código com base em certas condições serem verdadeiras ou falsas.
A.5. Estruturas de Controle Condicional 555

A.5.1 A Instrução if (Se)


Executa um bloco de código somente se uma condição especificada for verdadeira.
1 idade = 20
2 if idade >= 18:
3 print ( " É maior de idade . " )
4 # Mais c ó digo aqui , se necess á rio
5 print ( " Fim da verifica ç ã o if . " ) # Esta linha executa
inde pendentem ente

Código A.7: Sintaxe básica do if


O bloco de código dentro do if deve ser indentado.

A.5.2 A Instrução if-else (Se-Senão)


Executa um bloco de código se a condição for verdadeira, e outro bloco de código se a
condição for falsa.
1 temperatura = 15
2 if temperatura > 25:
3 print ( " Est á calor ! " )
4 else :
5 print ( " N ã o est á calor . " )
6 # Pode haver mais linhas no else
7 print ( " Verifica ç ã o de temperatura conclu í da . " )

Código A.8: Sintaxe do if-else

A.5.3 A Instrução if-elif-else (Se-Senão Se-Senão)


Permite verificar múltiplas condições em sequência. O Python executa o bloco da
primeira condição que for verdadeira. Se nenhuma for verdadeira, o bloco else (opcional)
é executado.
1 nota = 75
2 if nota >= 90:
3 print ( " Conceito A")
4 elif nota >= 80:
5 print ( " Conceito B")
6 elif nota >= 70:
7 print ( " Conceito C")
8 elif nota >= 60:
9 print ( " Conceito D")
10 else :
11 print ( " Conceito F ( Reprovado ) " )

Código A.9: Sintaxe do if-elif-else


Você pode ter quantas cláusulas elif forem necessárias. A cláusula else final é opcional.

A.5.4 O Operador Condicional Ternário (Expressão Condicional)


Uma forma concisa de escrever uma instrução if-else em uma única linha, geralmente
para atribuições ou expressões simples. Sintaxe: valor_se_condicao_verdadeira if
condicao else valor_se_condicao_falsa
556 Apêndice A. Guia Rápido de Sintaxe Python

1 idade_motorista = 22
2 status_motorista = " Pode dirigir " if idade_motorista >= 18 else
" N ã o pode dirigir "
3 print ( f " Status do motorista : { status_motorista } " )
4
5 # Outro exemplo
6 saldo = -10
7 tipo_saldo = " Positivo ou Zero " if saldo >= 0 else " Negativo "
8 print ( f " Tipo do saldo : { tipo_saldo } " )

Código A.10: Exemplo de operador ternário

A.5.5 Valores “Truthy” e “Falsy” em Condições


Em Python, não apenas valores booleanos (True, False) são avaliados em contextos
condicionais. Outros valores também podem ser interpretados como verdadeiros ou falsos:
• Valores “Falsy” (considerados False em um contexto booleano):

– O próprio False.
– O valor nulo None.
– Zero de qualquer tipo numérico: 0, 0.0, 0j.
– Sequências e coleções vazias: (string vazia), [] (lista vazia), () (tupla vazia),
{} (dicionário vazio), set() (conjunto vazio).

• Valores “Truthy” (considerados True em um contexto booleano):

– O próprio True.
– A maioria dos outros valores, incluindo números diferentes de zero, strings não
vazias, listas/tuplas/dicionários/conjuntos não vazios.

1 nome_usuario = input ( " Digite seu nome ( ou deixe em branco ) : " )


2 if nome_usuario : # Verdadeiro se a string nome_usuario n ã o for vazia
3 print ( f " Ol á , { nome_usuario }! " )
4 else :
5 print ( " Ol á ! Voc ê n ã o digitou um nome . " )
6
7 lista_de_erros = []
8 # ... c ó digo que pode adicionar erros à lista ...
9 if lista_de_erros : # Verdadeiro se a lista N Ã O estiver vazia
10 print ( " Foram encontrados erros ! " )
11 else :
12 print ( " Nenhum erro encontrado . " )

Código A.11: Exemplos de Truthy e Falsy


Compreender o que é “Truthy” e “Falsy” é útil para escrever condições mais concisas.

A.6 Estruturas de Repetição (Laços)


Os laços de repetição permitem que você execute um bloco de código múltiplas vezes.
Python oferece dois tipos principais de laços: while e for.
A.6. Estruturas de Repetição (Laços) 557

A.6.1 O Laço while (Enquanto)


O laço while executa um bloco de código repetidamente enquanto uma condição
especificada for verdadeira. Sintaxe:
1 while condicao :
2 # Bloco de c ó digo a ser repetido
3 # Instru ç ã o 1
4 # Instru ç ã o 2
5 # ...
6 # Importante : algo dentro do la ç o geralmente deve
7 # modificar a condi ç ã o para evitar um la ç o infinito .
8 # C ó digo ap ó s o la ç o

Código A.12: Sintaxe do laço while

• O bloco de código indentado é executado repetidamente enquanto a condicao for


avaliada como True.

• É crucial que a condição possa, eventualmente, se tornar False, ou o laço será


infinito.

Exemplo:
1 contador = 1
2 while contador <= 5:
3 print ( f " Contador é : { contador } " )
4 contador += 1 # Atualiza a condi ç ã o do la ç o
5 print ( " La ç o while finalizado . " )

Código A.13: Exemplo de laço while para contagem

Também é comum usar while True: com uma instrução break interna para sair sob uma
condição específica.

A.6.2 O Laço for (Para)


O laço for é usado para iterar sobre os itens de uma sequência (como uma lista,
tupla, string) ou qualquer outro objeto iterável, na ordem em que aparecem na sequência.
Sintaxe:
1 for variavel_item in iteravel :
2 # Bloco de c ó digo a ser executado para cada item
3 # ’ variavel_item ’ assume o valor de cada item da sequ ê ncia
4 # a cada itera ç ã o .
5 # ...
6 # C ó digo ap ó s o la ç o

Código A.14: Sintaxe do laço for

• variavel_item: Uma variável que recebe cada item do iteravel a cada iteração.

• iteravel: A sequência ou objeto sobre o qual se está iterando.

Exemplos:
558 Apêndice A. Guia Rápido de Sintaxe Python

1 # Iterando sobre uma lista


2 frutas = [ " ma ç ã " , " banana " , " laranja " ]
3 for fruta in frutas :
4 print ( f " Eu gosto de { fruta } " )
5
6 # Iterando sobre uma string
7 palavra = " Python "
8 for letra in palavra :
9 print ( letra , end = " " ) # Sa í da : P y t h o n
10 print () # Para nova linha
11
12 # Iterando com range ()
13 # range ( fim_exclusivo ) -> 0 at é fim -1
14 for i in range (5) : # i ser á 0 , 1 , 2 , 3 , 4
15 print ( f " N ú mero : { i } " )
16
17 # range ( inicio_inclusivo , fim_exclusivo )
18 for j in range (2 , 6) : # j ser á 2 , 3 , 4 , 5
19 print ( f " Outro n ú mero : { j } " )
20
21 # range ( inicio_inclusivo , fim_exclusivo , passo )
22 for k in range (10 , 0 , -2) : # k ser á 10 , 8 , 6 , 4 , 2 ( contagem
regressiva )
23 print ( f " Contagem regressiva : { k } " )

Código A.15: Exemplos de laço for


A função range() é muito usada com for para repetir um bloco de código um número
específico de vezes ou para gerar índices.

A.6.3 Comandos de Controle de Laço: break e continue


Esses comandos alteram o fluxo normal de execução de um laço.

• break

– Interrompe imediatamente o laço mais interno (while ou for) em que se


encontra.
– A execução do programa continua com a primeira instrução após o bloco do
laço.

• continue

– Interrompe a iteração atual do laço mais interno.


– Pula o restante do código no bloco do laço para a iteração atual e prossegue
para o início da próxima iteração (se a condição do while ainda for verdadeira
ou se houver mais itens no for).

Exemplo:
1 # Exemplo com break
2 print ( " Procurando o n ú mero 5 ( com break ) : " )
3 for i in range (1 , 11) :
4 if i == 5:
5 print ( " N ú mero 5 encontrado ! Saindo do la ç o . " )
A.6. Estruturas de Repetição (Laços) 559

6 break # Interrompe o la ç o for


7 print ( i )
8 print ( " Ap ó s o la ç o com break . " )
9
10 # Exemplo com continue
11 print ( " \ nImprimindo apenas n ú meros í mpares ( com continue ) : " )
12 for i in range (1 , 11) :
13 if i % 2 == 0: # Se for par
14 continue # Pula o print e vai para a pr ó xima itera ç ã o
15 print ( i )
16 print ( " Ap ó s o la ç o com continue . " )

Código A.16: Exemplos de break e continue

A.6.4 A Cláusula else em Laços (Opcional)


Tanto laços while quanto for em Python podem ter uma cláusula else opcional.

• O bloco de código da cláusula else é executado somente se o laço terminar


normalmente, ou seja, se a condição do while se tornar falsa (para while) ou se o
for iterar sobre todos os seus itens (para for).

• O bloco else não é executado se o laço for interrompido por uma instrução break.

Sintaxe:

for item in iteravel:


# ...
if condicao_de_saida:
break
else:
# Executado se o laço for não foi interrompido por break

while condicao_while:
# ...
if condicao_de_saida_while:
break
else:
# Executado se o laço while terminou porque condicao_while se tornou falsa

Exemplo (procurando um número em uma lista):


1 n u me ros _ pa r a _b u sc a r = [11 , 23 , 45 , 56 , 78]
2 valor_procurado = 45
3 # valor_procurado = 100 # Teste com valor n ã o encontrado
4
5 for numero in n u me r os _ p ar a _b u sc a r :
6 if numero == valor_procurado :
7 print ( f " Valor { valor_procurado } encontrado ! " )
8 break
9 else : # Executa se o ’ break ’ N Ã O for atingido
10 print ( f " Valor { valor_procurado } n ã o encontrado na lista . " )

Código A.17: Exemplo de for...else


560 Apêndice A. Guia Rápido de Sintaxe Python

A cláusula else em laços é útil para executar código quando um item não foi encontrado
em uma busca, ou quando um laço completou todas as suas iterações sem uma interrupção
prematura.

A.6.5 Laços Aninhados


Você pode colocar um laço dentro de outro laço. Isso é chamado de laço aninhado. O
laço interno é executado completamente para cada iteração do laço externo.
1 # Imprimindo uma pequena tabuada (1 x1 at é 3 x3 )
2 for i in range (1 , 4) : # La ç o externo ( para i de 1 a 3)
3 print ( f " Tabuada do { i }: " )
4 for j in range (1 , 4) : # La ç o interno ( para j de 1 a 3)
5 print ( f " { i } x { j } = { i * j } " )
6 print ( " -" * 15) # Separador para cada tabuada

Código A.18: Exemplo de laços aninhados (tabuada simples)


Laços aninhados são comuns para trabalhar com estruturas bidimensionais (como listas de
listas para representar matrizes) ou para gerar combinações de itens.

A.7 Estruturas de Dados


Python oferece várias estruturas de dados embutidas para armazenar e organizar
coleções de dados. As mais comuns são listas, tuplas, dicionários e conjuntos.

A.7.1 Listas (list)


Listas são coleções ordenadas e mutáveis de itens. Podem conter itens de tipos
diferentes.

• Criação:
1 lista_vazia = []
2 numeros = [1 , 2 , 3 , 4 , 5]
3 misturada = [1 , " texto " , 3.14 , True ]
4 copia_de_outra = list ( numeros ) # Construtor

Código A.19: Criação de Listas

• Acesso (Indexação e Fatiamento): Baseado em zero, similar a strings.


1 primeiro = numeros [0] # Acessa o primeiro item (1)
2 ultimo = numeros [ -1] # Acessa o ú ltimo item (5)
3 fatia = numeros [1:4] # Itens do í ndice 1 ao 3: [2 , 3 , 4]

Código A.20: Acesso e Fatiamento em Listas

Fatias de listas criam novas listas (cópias rasas).

• Modificação: Por serem mutáveis, listas podem ser alteradas após a criação.
1 numeros [0] = 100 # Altera o primeiro elemento para 100
2 numeros . append (6) # Adiciona 6 ao final : [100 , 2 , 3 , 4 , 5 ,
6]
A.7. Estruturas de Dados 561

3 numeros . insert (1 , 200) # Insere 200 na posi ç ã o 1: [100 ,


200 , 2 , 3 , 4 , 5 , 6]
4 del numeros [2] # Remove o item no í ndice 2 ( que é o n ú mero
2)
5 item_removido = numeros . pop () # Remove e retorna o ú ltimo
item (6)
6 numeros . remove (4) # Remove a primeira ocorr ê ncia do valor 4

Código A.21: Modificando Listas

• Métodos Comuns Principais: append(item), insert(idx, item), remove(valor),


pop(idx_opc), extend(iteravel), sort(reverse=False), reverse(), count(valor),
index(valor), copy().

• Funções Comuns: len(lista), min(lista), max(lista), sum(lista_de_numeros).

• Operador de Associação: item in lista, item not in lista.

• Compreensão de Listas (List Comprehensions): Forma concisa de criar listas.


1 quadrados = [ x **2 for x in range (1 , 6) ] # [1 , 4 , 9 , 16 , 25]
2 pares = [ n for n in range (10) if n % 2 == 0] # [0 , 2 , 4 ,
6 , 8]

Código A.22: Exemplo de List Comprehension

A.7.2 Tuplas (tuple)


Tuplas são coleções ordenadas e imutáveis de itens. Uma vez criadas, não podem ser
alteradas.

• Criação:
1 tupla_vazia = ()
2 ponto = (10 , 20) # Par ê nteses s ã o opcionais na maioria dos
casos
3 # ponto = 10 , 20 # Tamb é m cria uma tupla
4 cor_unica = ( " vermelho " ,) # V Í RGULA OBRIGAT Ó RIA para tupla
de um elemento
5 outra_tupla = tuple ([1 , 2 , 3]) # Construtor a partir de
iter á vel

Código A.23: Criação de Tuplas

• Acesso e Fatiamento: Idêntico às listas.


1 x = ponto [0] # 10
2 sub_tupla = ponto [0:1] # (10 ,)

Código A.24: Acesso e Fatiamento em Tuplas

• Imutabilidade: Tentativas de alterar um item (ex: ponto[0] = 5) resultam em


TypeError.

• Métodos Comuns Principais: count(valor), index(valor). (Possuem menos


métodos que listas devido à imutabilidade).
562 Apêndice A. Guia Rápido de Sintaxe Python

• Operações: Concatenação (+), repetição (*), len(), in, not in, iteração com for.

• Desempacotamento (Unpacking): Atribuir elementos de uma tupla a múltiplas


variáveis.
1 nome , idade = ( " Ana " , 30) # nome =" Ana " , idade =30
2 a , b , * resto = (1 , 2 , 3 , 4 , 5) # a =1 , b =2 , resto =[3 ,4 ,5]

Código A.25: Desempacotamento de Tuplas

A.7.3 Dicionários (dict)


Dicionários são coleções mutáveis e não ordenadas (antes do Python 3.7; a partir do
3.7, mantêm a ordem de inserção) de pares chave-valor. As chaves devem ser únicas e de
tipo imutável.

• Criação:
1 dict_vazio = {} # IMPORTANTE : {} vazio é um dicion á rio ,
n ã o um conjunto
2 aluno = { " nome " : " Carlos " , " idade " : 22 , " curso " :
" Engenharia " }
3 contatos = dict ( ana =1234 , bruno =5678) # Usando argumentos
nomeados
4 capitais = dict ([( " Brasil " , " Bras í lia " ) , ( " Fran ç a " ,
" Paris " ) ]) # Lista de tuplas

Código A.26: Criação de Dicionários

• Acesso a Valores:
1 nome_aluno = aluno [ " nome " ] # Acessa " Carlos "
2 # print ( aluno [" matricula "]) # Geraria KeyError se
" matricula " n ã o existir
3 idade_aluno = aluno . get ( " idade " ) # Retorna valor ou None
se n ã o existir
4 id a de _ al u n o_ d ef a u lt = aluno . get ( " idade_nao_existe " , 0) #
Retorna 0 ( padr ã o )

Código A.27: Acesso a valores em Dicionários

• Adição/Modificação:
1 aluno [ " curso " ] = " Engenharia El é trica " # Modifica valor
existente
2 aluno [ " universidade " ] = " USP " # Adiciona novo par
chave - valor
3 aluno . update ({ " idade " : 23 , " cidade " : " S ã o Paulo " }) #
Adiciona / atualiza m ú ltiplos

Código A.28: Adicionando/Modificando em Dicionários

• Remoção:
1 del aluno [ " cidade " ]
2 idade_removida = aluno . pop ( " idade " ) # Remove e retorna o
valor
A.7. Estruturas de Dados 563

3 # ultimo_par = aluno . popitem () # Remove e retorna um par


( chave , valor ) ( LIFO em 3.7+)
4 # aluno . clear () # Remove todos os itens

Código A.29: Removendo de Dicionários

• Métodos/Operações Comuns: len(dict), chave in dict, [Link]() (visão


das chaves), [Link]() (visão dos valores), [Link]() (visão dos pares
chave-valor). Iteração sobre chaves, valores ou itens.

• Compreensão de Dicionários (Dict Comprehensions):


1 numeros = [1 , 2 , 3]
2 quadrados_dict = { x : x **2 for x in numeros } # {1:1 , 2:4 ,
3:9}

Código A.30: Exemplo de Dict Comprehension

A.7.4 Conjuntos (set)


Conjuntos são coleções não ordenadas de itens únicos e mutáveis. Os próprios itens
devem ser de tipo imutável.

• Criação:
1 conjunto_vazio = set () # IMPORTANTE : {} cria um dicion á rio
vazio
2 frutas = { " ma ç ã " , " banana " , " laranja " , " ma ç ã " } #
Duplicatas s ã o ignoradas
3 # frutas seria { ’ laranja ’, ’ banana ’, ’ ma ç ã ’} ( ordem pode
variar )
4 numeros_set = set ([1 , 2 , 2 , 3 , 1]) # {1 , 2 , 3}

Código A.31: Criação de Conjuntos (set)

• Adição: [Link](item), [Link](iteravel) (adiciona múltiplos


itens).

• Remoção:

– [Link](item) (levanta KeyError se o item não existir).


– [Link](item) (remove o item se existir, não levanta erro caso
contrário).
– [Link]() (remove e retorna um item arbitrário; levanta KeyError se
vazio).
– [Link]() (remove todos os itens).

• Operações de Teoria dos Conjuntos:

– União: set1 | set2 ou [Link](set2)


– Interseção: set1 & set2 ou [Link](set2)
– Diferença: set1 - set2 ou [Link](set2)
564 Apêndice A. Guia Rápido de Sintaxe Python

– Diferença Simétrica: set1 ŝet2 ou set1.symmetric_difference(set2)

• Outras Operações/Métodos: len(set), item in set, iteração com for, issubset(),


issuperset(), isdisjoint().

1 s1 = {1 , 2 , 3 , 4}
2 s2 = {3 , 4 , 5 , 6}
3 print ( f " Uni ã o s1 | s2 : { s1 | s2 } " ) # {1 , 2 , 3 , 4 , 5 , 6}
4 print ( f " Interse ç ã o s1 & s2 : { s1 & s2 } " ) # {3 , 4}

Código A.32: Exemplo de operações com conjuntos

A.7.5 Conjuntos Imutáveis (frozenset)


Um frozenset é uma versão imutável de um set. Uma vez criado, não pode ser
alterado.

• Criação: fs = frozenset(iteravel)
1 fset_ex = frozenset ([10 , 20 , 10 , 30]) # frozenset ({10 , 20 ,
30})

Código A.33: Criação de frozenset

• Características:

– Não possui métodos de modificação (add, remove, update, etc.).


– Suporta operações de conjunto que retornam um novo conjunto (ex: união,
interseção).
– É hasheável, o que significa que pode ser usado como elemento de outro
conjunto ou como chave de dicionário (ao contrário de sets mutáveis).

1 chave_fs = frozenset ({ " a " , " b " })


2 meu_dict_com_fs = { chave_fs : " Valor associado " }
3 print ( meu_dict_com_fs )

Código A.34: frozenset como chave de dicionário

A.8 Funções
Funções são blocos de código nomeados e reutilizáveis que realizam uma tarefa específica.
Elas ajudam a organizar o código, evitar repetição e tornar os programas mais modulares.

A.8.1 Definição de Funções com def


Usa-se a palavra-chave def para definir uma função. Sintaxe básica:
1 def nome_da_funcao ( parametro1 , parametro2 , ...) :
2 " " " Docstring opcional explicando a fun ç ã o . " " "
3 # Corpo da fun ç ã o ( bloco de c ó digo indentado )
4 # Instru ç ã o 1
5 # Instru ç ã o 2
A.8. Funções 565

6 # ...
7 return valor_de_retorno # Opcional

Código A.35: Sintaxe básica para definir uma função

• nome_da_funcao: Segue as regras de nomenclatura de variáveis (convenção snake_case).

• parametro1, parametro2, ...: Nomes das variáveis que a função espera receber
(opcional).

• O corpo da função deve ser indentado.

• A docstring (se presente) é a primeira instrução e documenta a função.

• A instrução return especifica o valor que a função devolve (opcional).

Exemplo simples:
1 def saudacao () :
2 print ( " Ol á , bem - vindo ! " )

Código A.36: Função simples sem parâmetros

A.8.2 Chamando (Invocando) Funções


Para executar o código dentro de uma função, você a chama pelo nome seguido de
parênteses. Sintaxe: nome_da_funcao(argumento1, argumento2, ...)
1 saudacao () # Chama a fun ç ã o definida acima
2 # Sa í da : Ol á , bem - vindo !

Código A.37: Chamando uma função

A.8.3 Parâmetros e Argumentos


• Parâmetros: Variáveis listadas na definição da função.

• Argumentos: Valores reais passados para a função quando ela é chamada.

• Argumentos Posicionais: A ordem dos argumentos na chamada corresponde à


ordem dos parâmetros na definição.

1 def somar (a , b ) : # a e b s ã o par â metros


2 print ( f " A soma é : { a + b } " )
3
4 somar (5 , 3) # 5 e 3 s ã o argumentos posicionais
5 # Sa í da : A soma é : 8

Código A.38: Função com parâmetros e argumentos posicionais


566 Apêndice A. Guia Rápido de Sintaxe Python

A.8.4 Retornando Valores com return


Funções podem devolver um valor para o código que as chamou usando a instrução
return.

• return valor_ou_expressao

• A execução da função termina imediatamente quando um return é encontrado.

• Se uma função não tem uma instrução return explícita, ou tem return sem um
valor, ela retorna None por padrão.

1 def multiplicar (x , y ) :
2 return x * y
3
4 resultado = multiplicar (4 , 7) # resultado ser á 28
5 print ( f " Resultado da multiplica ç ã o : { resultado } " )

Código A.39: Função que retorna um valor

A.8.5 Docstrings (Strings de Documentação)


Uma string literal (geralmente com aspas triplas "...") como a primeira instrução na
definição de uma função. Serve para documentar o que a função faz, seus parâmetros e o
que ela retorna.
1 def calcular_media ( lista_numeros ) :
2 " " " Calcula e retorna a m é dia de uma lista de n ú meros .
3
4 Args :
5 lista_numeros ( list ) : Uma lista de n ú meros ( int ou float ) .
6
7 Returns :
8 float : A m é dia dos n ú meros na lista . Retorna 0 se a lista for
vazia .
9 """
10 if not lista_numeros :
11 return 0
12 return sum ( lista_numeros ) / len ( lista_numeros )
13
14 # Acessando a docstring :
15 # help ( calcular_media )
16 # print ( calcular_media . __doc__ )

Código A.40: Exemplo de função com docstring

A.8.6 Escopo de Variáveis (Local vs. Global)


• Variáveis Locais: Definidas dentro de uma função. Só existem dentro da função.
Parâmetros também são locais.

• Variáveis Globais: Definidas fora de todas as funções. Podem ser lidas de dentro
de funções.
A.8. Funções 567

• Para modificar uma variável global de dentro de uma função, use a palavra-chave
global nome_variavel_global no início da função. (Uso geralmente desencora-
jado).

• Se uma variável local tiver o mesmo nome de uma global, a local “sombra” (esconde)
a global dentro da função.

1 x_global = 10
2
3 def min ha_func_ escopo () :
4 # global x_global # Descomente para modificar a global
5 x_local = 5
6 # x_global = 50 # Sem ’ global ’, cria uma x_global local ( sombra )
7 print ( f " Dentro da func : x_local ={ x_local } , x_global
( lida ) ={ x_global } " )
8
9 minha_fun c_escopo ()
10 print ( f " Fora da func : x_global ={ x_global } " )

Código A.41: Exemplo de escopo local e global

A.8.7 Argumentos com Valores Padrão (Default Arguments)


Permitem que parâmetros tenham um valor padrão se nenhum argumento for fornecido
na chamada. Parâmetros com valores padrão devem vir após os parâmetros sem valor
padrão na definição. Sintaxe na definição:
def func(param1, param_opcional="valor_padrao", outro_opc=0):
1 def sau dacao_co mpleta ( nome , saud = " Ol á " , pontuacao = " ! " ) :
2 print ( f " { saud } , { nome }{ pontuacao } " )
3
4 saudacao_ completa ( " Maria " ) # Usa padr õ es para saud e pontuacao
5 saudacao_ completa ( " Jo ã o " , " Bom dia " ) # Sobrescreve saud
6 saudacao_ completa ( " Ana " , pontuacao = " !!! " , saud = " E a í " ) # Ordem dos
nomeados n ã o importa

Código A.42: Função com argumentos padrão

Cuidado com valores padrão mutáveis (ex: lista=[]). O objeto padrão é criado
uma vez. Use None como padrão e crie o objeto dentro da função se necessário.

A.8.8 Argumentos Nomeados (Keyword Arguments)


Permitem passar argumentos para uma função especificando o nome do parâmetro. Sin-
taxe na chamada: nome_da_funcao(parametro1=valor1, parametro2=valor2, ...)

• A ordem dos argumentos nomeados não importa.

• Melhora a legibilidade, especialmente para funções com muitos parâmetros.

• Argumentos posicionais devem vir antes de quaisquer argumentos nomeados.


568 Apêndice A. Guia Rápido de Sintaxe Python

1 # Usando a fun ç ã o sa udacao_c ompleta do exemplo anterior :


2 saudacao_ completa ( nome = " Carlos " , pontuacao = " . " , saud = " Bem - vindo " )
3 saudacao_ completa ( " Pedro " , saud = " Oi " ) # Misturando posicional e
nomeado

Código A.43: Usando argumentos nomeados

A.8.9 Funções Lambda (Funções Anônimas)


Uma forma concisa de criar pequenas funções anônimas (sem nome formal def) em
uma única expressão. Sintaxe: lambda argumentos: expressao
• A expressão é avaliada e seu resultado é retornado automaticamente.

• Limitada a uma única expressão.

• Frequentemente usada onde uma função pequena é necessária por um curto período
(ex: como argumento para sorted(), map(), filter()).

1 dobro = lambda x : x * 2
2 print ( f " Dobro de 5: { dobro (5) } " ) # Sa í da : 10
3
4 adicao = lambda a , b : a + b
5 print ( f " 3 + 7 = { adicao (3 , 7) } " ) # Sa í da : 10
6
7 # Exemplo com sorted ()
8 pontos = [(1 , 5) , (3 , 2) , (0 , 8) ]
9 # Ordenar pela segunda coordenada da tupla ( y )
10 po nt os_ or de nad os _y = sorted ( pontos , key = lambda ponto : ponto [1])
11 print ( f " Pontos ordenados por y : { po nt os_ or de nad os _y } " )

Código A.44: Exemplos de funções lambda

A.9 Módulos e Pacotes


Módulos e pacotes são a forma como Python organiza e reutiliza código em múltiplos
arquivos e diretórios, respectivamente.

A.9.1 Módulos
Um módulo em Python é simplesmente um arquivo com a extensão .py contendo
definições e instruções Python (funções, classes, variáveis). Módulos permitem organizar o
código e reutilizá-lo em outros scripts.

Importando Módulos
Existem várias formas de importar módulos:
• import nome_do_modulo

– Importa o módulo inteiro. Para acessar seus membros (funções, variáveis,


classes), você usa a notação de ponto: nome_do_modulo.membro.
A.9. Módulos e Pacotes 569

1 # Supondo que existe um arquivo meu_modulo . py


2 # import meu_modulo
3 # resultado = meu_modulo . minha_funcao (10)
4 # print ( meu_modulo . MINHA_CONSTANTE )

Código A.45: Usando import modulo

• from nome_do_modulo import membro1, membro2, ...

– Importa membros específicos do módulo diretamente para o namespace atual.


Você pode usá-los sem o prefixo do nome do módulo.

1 # from meu_modulo import minha_funcao , MINHA_CONSTANTE


2 # resultado = minha_funcao (10) # Chamada direta
3 # print ( MINHA_CONSTANTE )

Código A.46: Usando from modulo import item

• from nome_do_modulo import *

– Importa todos os nomes (que não começam com underscore) do módulo para o
namespace atual.
– Geralmente desencorajado pois pode poluir o namespace e causar conflitos
de nomes, tornando o código menos legível.

• import nome_do_modulo as alias

– Importa o módulo e lhe dá um apelido (alias), que é usado para acessá-lo. Útil
para nomes de módulos longos ou para evitar conflitos.

1 # import m e u _ m o d u l o _ l o n g o _ e _ c o m p l i c a d o as mm
2 # resultado = mm . funcao_dele (5)
3 import numpy as np # Exemplo muito comum
4 # arr = np . array ([1 ,2 ,3])

Código A.47: Importando módulo com alias

Você também pode usar as com from ... import ...: from meu_modulo import
funcao_original as nome_curto.

O Bloco if __name__ == "__main__"


Este bloco de código em um arquivo de módulo é executado apenas quando o arquivo
é executado diretamente como o script principal, e não quando ele é importado como um
módulo por outro script.

• __name__: Variável especial. É "__main__" se o script é executado diretamente. É


o nome do módulo se importado.

Sintaxe:
570 Apêndice A. Guia Rápido de Sintaxe Python

1 # d e fi n i c o e s _ d o _ m od u l o . py
2 # ... fun ç õ es , classes , etc . ...
3

4 # print ( f " M ó dulo { __name__ } carregado .") # Executa na importa ç ã o e


execu ç ã o direta
5
6 if __name__ == " __main__ " :
7 # C ó digo aqui s ó executa quando d e f i ni c o e s _ d o _ m o d u l o . py é
rodado diretamente .
8 # Ex : c ó digo de teste , demonstra ç õ es .
9 print ( " Rodando o m ó dulo como script principal ! " )
10 # m i n h a _ f u n c a o _ d e _ t e s t e _ d o _ m o d u l o ()

Código A.48: Estrutura do bloco if __name__ == "__main__"

A.9.2 Pacotes (Packages)


Um pacote é uma forma de estruturar o namespace de módulos do Python usando
uma hierarquia de diretórios. Um pacote é um diretório que contém:

• Outros módulos (arquivos .py).

• Opcionalmente, outros subpacotes (subdiretórios que também são pacotes).

• Um arquivo especial chamado __init__.py. Este arquivo, mesmo que vazio, in-
forma ao Python que o diretório deve ser tratado como um pacote. O código em
__init__.py é executado quando o pacote (ou um de seus módulos) é importado
pela primeira vez.

Importando de Pacotes
Usa-se a notação de ponto.

• import meu_pacote.modulo_a
(Acesso: meu_pacote.modulo_a.funcao())

• import meu_pacote.modulo_a as mod_a


(Acesso: mod_a.funcao())

• from meu_pacote import modulo_a


(Acesso: modulo_a.funcao())

• from meu_pacote.modulo_a import funcao_especifica


(Acesso: funcao_especifica())

• from meu_pacote.modulo_a import funcao_especifica as f_esp


(Acesso: f_esp())

• Para subpacotes: import meu_pacote.sub_pacote_c.modulo_c1 ou


from meu_pacote.sub_pacote_c.modulo_c1 import item.
A.10. Programação Orientada a Objetos (POO) 571

A.9.3 Biblioteca Padrão do Python (Breve Menção)


Python vem com uma vasta coleção de módulos embutidos, conhecida como Biblioteca
Padrão, que oferece funcionalidades prontas para uso em diversas tarefas. Alguns exemplos
que vimos incluem: math, random, datetime, os (e [Link]), sys, json, csv. Você os
importa da mesma forma que módulos criados por você (ex: import math).

A.9.4 Instalando Pacotes Externos com pip (Breve Menção)


Além da biblioteca padrão, existe um enorme repositório de pacotes de terceiros
chamado PyPI (Python Package Index - [Link]). A ferramenta padrão para instalar
esses pacotes é o pip, que geralmente vem com o Python. Os comandos pip são executados
no terminal ou prompt de comando, não no interpretador Python.
• Instalar um pacote: pip install nome_do_pacote
• Desinstalar: pip uninstall nome_do_pacote
• Listar pacotes instalados: pip list
• Salvar dependências do projeto: pip freeze > [Link]
• Instalar de um arquivo de requisitos: pip install -r [Link]
É uma boa prática usar ambientes virtuais (ex: com o módulo venv) para gerenciar
as dependências de cada projeto Python de forma isolada.

A.10 Programação Orientada a Objetos (POO)


A Programação Orientada a Objetos (POO) é um paradigma que organiza o código
em torno de “objetos”, que combinam dados (atributos) e comportamentos (métodos).

A.10.1 Classes e Objetos


• Classe: Um molde ou planta baixa para criar objetos. Define os atributos e métodos
que os objetos desse tipo terão.
– Convenção de Nomenclatura: NomeEmCamelCase.
• Objeto (Instância): Uma instância concreta de uma classe.
Definindo uma Classe (Sintaxe Básica):
1 class NomeDaClasse : # ( Su p e rc l as s e Op c io n al ) entre par ê nteses para
heran ç a
2 # Atributos de classe ( compartilhados ) aqui
3 pass # Corpo da classe ( m é todos e atributos de inst â ncia s ã o
definidos aqui )

Código A.49: Definição básica de classe


Criando um Objeto (Instanciação):
1 # Supondo que a classe NomeDaClasse est á definida
2 meu_objeto = NomeDaClasse ()

Código A.50: Criando um objeto


572 Apêndice A. Guia Rápido de Sintaxe Python

A.10.2 Construtor (__init__) e Atributos de Instância


• O método __init__(self, parametro1, ...) é o construtor. É chamado auto-
maticamente quando um objeto é criado.

• self é o primeiro parâmetro de métodos de instância e se refere à própria instância


do objeto.

• Atributos de Instância são criados dentro de métodos (geralmente __init__)


usando self.nome_atributo = valor. São específicos de cada objeto.

1 class Ponto :
2 def __init__ ( self , x_coord , y_coord ) :
3 self . x = x_coord # Atributo de inst â ncia
4 self . y = y_coord # Atributo de inst â ncia
5 print ( f " Ponto ({ self . x } ,{ self . y }) criado . " )
6
7 p1 = Ponto (10 , 20) # Cria um Ponto , chamando __init__
8 # p1 . x é 10 , p1 . y é 20

Código A.51: Classe com __init__ e atributos de instância

A.10.3 Métodos de Instância


Funções definidas dentro de uma classe que operam sobre os dados (atributos) de uma
instância.

• O primeiro parâmetro é sempre self.

• São chamados em um objeto: objeto.nome_do_metodo(argumentos...).

1 class Ponto :
2 def __init__ ( self , x , y ) :
3 self . x = x
4 self . y = y
5
6 def mover ( self , dx , dy ) : # M é todo de inst â ncia
7 self . x += dx
8 self . y += dy
9 print ( f " Ponto movido para ({ self . x } ,{ self . y }) " )
10
11 p = Ponto (1 , 2)
12 p . mover (3 , 5) # p . x agora é 4 , p . y é 7

Código A.52: Método de instância na classe Ponto

A.10.4 Atributos de Classe


Atributos definidos diretamente no escopo da classe (fora de qualquer método). São
compartilhados por todas as instâncias da classe.

• Acesso: NomeDaClasse.atributo_classe ou instancia.atributo_classe (se não


sombreado por um atributo de instância com o mesmo nome).
A.10. Programação Orientada a Objetos (POO) 573

1 class Circulo :
2 PI = 3.14159 # Atributo de classe
3

4 def __init__ ( self , raio ) :


5 self . raio = raio # Atributo de inst â ncia
6
7 def area ( self ) :
8 # Acessando atributo de classe via self ou NomeDaClasse
9 return Circulo . PI * ( self . raio ** 2)
10 # return self . PI * ( self . raio ** 2) # Tamb é m funciona aqui
11
12 c1 = Circulo (5)
13 print ( f " Á rea de c1 : { c1 . area () } " )
14 print ( f " Constante PI da classe : { Circulo . PI } " )

Código A.53: Exemplo de atributo de classe

A.10.5 Herança
Permite que uma classe (subclasse) herde atributos e métodos de outra classe (super-
classe).
• Sintaxe de Definição: class Subclasse(Superclasse):

• super().__init__(...): Usado no __init__ da subclasse para chamar o construtor


da superclasse.

• Sobrescrita de Métodos (Method Overriding): A subclasse pode redefinir um


método herdado com o mesmo nome para fornecer uma implementação específica.

• Subclasses podem adicionar novos atributos e métodos.

1 class Animal :
2 def __init__ ( self , nome ) :
3 self . nome = nome
4 def falar ( self ) :
5 return " Som gen é rico de animal "
6
7 class Cachorro ( Animal ) : # Cachorro herda de Animal
8 def __init__ ( self , nome , raca ) :
9 super () . __init__ ( nome ) # Chama o __init__ de Animal
10 self . raca = raca # Atributo espec í fico de Cachorro
11
12 def falar ( self ) : # Sobrescreve o m é todo falar
13 return " Au Au ! "
14
15 def buscar ( self ) : # Novo m é todo
16 return f " { self . nome } est á buscando a bolinha ! "
17
18 rex = Cachorro ( " Rex " , " Labrador " )
19 print ( rex . falar () ) # Sa í da : Au Au !
20 print ( rex . buscar () )
21 # print ( rex . nome ) # Atributo herdado

Código A.54: Exemplo de Herança


574 Apêndice A. Guia Rápido de Sintaxe Python

A.10.6 Métodos Especiais (Dunder Methods)


Métodos com nomes cercados por duplos underscores (ex: __init__) que permitem
que seus objetos interajam com funcionalidades embutidas do Python.

• __str__(self): Retorna uma string para print(objeto) e str(objeto). Repre-


sentação amigável.

• __repr__(self): Retorna uma string para repr(objeto) e para exibição no console


interativo. Representação “oficial” / para desenvolvedor.

• Outros comuns: __len__(self) para len(objeto), __eq__(self, outro) para


objeto == outro.

1 class Livro :
2 def __init__ ( self , titulo , autor ) :
3 self . titulo = titulo
4 self . autor = autor
5 def __str__ ( self ) :
6 return f " ’{ self . titulo } ’ por { self . autor } "
7 def __repr__ ( self ) :
8 return f " Livro ( titulo = ’{ self . titulo } ’ ,
autor = ’{ self . autor } ’) "
9
10 livro1 = Livro ( " Engenharia Total " , " Autor Desconhecido " )
11 print ( livro1 ) # Usa __str__
12 print ( str ( livro1 ) ) # Usa __str__
13 print ( repr ( livro1 ) ) # Usa __repr__

Código A.55: Exemplo de __str__ e __repr__

A.10.7 Encapsulamento (Convenções de Nomenclatura)


Python não tem modificadores de acesso estritos como private ou protected. Usa-se
convenções:

• Underscore Único (_atributo_interno): Indica que o atributo ou método é para


uso interno da classe ou de suas subclasses. É uma convenção, o acesso externo não
é tecnicamente impedido.

• Duplo Underscore
(__atributo_privado): Ativa o mecanismo de name mangling (mutilação de nome),
onde Python altera o nome para _NomeDaClasse__atributo_privado. O principal
objetivo é evitar conflitos de nomes em herança, não criar privacidade estrita.

A.10.8 Propriedades (@property, @*.setter, @*.deleter)


Forma Pythônica de gerenciar o acesso a atributos, permitindo lógica (validação,
cálculo) ao ler, definir ou deletar um atributo, mas mantendo uma sintaxe de acesso
simples.

• Getter: @property def nome_propriedade(self):


... return self._atributo_real
A.10. Programação Orientada a Objetos (POO) 575

• Setter: @nome_propriedade.setter def nome_propriedade(self, valor): ...


self._atributo_real = valor

• Deleter: @nome_propriedade.deleter def nome_propriedade(self): ... del


self._atributo_real

1 class Produto :
2 def __init__ ( self , preco ) :
3 self . preco = preco # Chama o setter
4

5 @property
6 def preco ( self ) : # Getter
7 # print (" Getter chamado ")
8 return self . _preco_interno
9
10 @preco . setter
11 def preco ( self , valor ) : # Setter
12 if valor < 0:
13 raise ValueError ( " Pre ç o n ã o pode ser negativo " )
14 self . _preco_interno = valor
15
16 # p = Produto (50)
17 # p . preco = 60 # Chama o setter
18 # print ( p . preco ) # Chama o getter

Código A.56: Exemplo de propriedade com getter e setter

A.10.9 Métodos de Classe (@classmethod)


Métodos ligados à classe em si, e não a uma instância.

• Decorador: @classmethod.

• Primeiro parâmetro é cls (referência à classe).

• Usos comuns: Métodos de fábrica (construtores alternativos), acessar/modificar


atributos de classe.

1 class Contador :
2 num_instancias = 0 # Atributo de classe
3 def __init__ ( self ) :
4 Contador . num_instancias += 1
5
6 @classmethod
7 def ge t_ nu m_i ns ta nci as ( cls ) :
8 return cls . num_instancias
9
10 # c1 = Contador ()
11 # c2 = Contador ()
12 # print ( Contador . ge t_ num _i ns ta nci as () ) # Sa í da : 2

Código A.57: Exemplo de método de classe


576 Apêndice A. Guia Rápido de Sintaxe Python

A.10.10 Métodos Estáticos (@staticmethod)


Funções utilitárias que residem dentro de uma classe por organização lógica, mas não
operam sobre a instância (self) nem sobre a classe (cls) implicitamente.
• Decorador: @staticmethod.

• Não recebem self ou cls automaticamente como primeiro argumento.

1 class UtilitariosMat :
2 @staticmethod
3 def somar_dois (x , y ) :
4 return x + y
5

6 # print ( UtilitariosMat . somar_dois (5 , 3) ) # Sa í da : 8

Código A.58: Exemplo de método estático

A.10.11 Classes Abstratas (Módulo abc)


Permitem definir “contratos” para subclasses, especificando métodos que elas devem
implementar.
• Importar: from abc import ABC, abstractmethod.

• Classe Abstrata: Herda de ABC.

• Método Abstrato: Decorado com @abstractmethod. Pode ter pass ou raise


NotImplementedError no corpo.

• Classes abstratas não podem ser instanciadas diretamente. Subclasses concretas


devem implementar todos os métodos abstratos.

1 from abc import ABC , abstractmethod


2

3 class VeiculoBase ( ABC ) :


4 @abstractmethod
5 def mover ( self ) :
6 pass
7
8 class Carro ( VeiculoBase ) :
9 def mover ( self ) : # Implementa ç ã o obrigat ó ria
10 print ( " Carro se movendo nas rodas ... " )
11
12 # v_base = VeiculoBase () # Geraria TypeError
13 # meu_carro = Carro ()
14 # meu_carro . mover ()

Código A.59: Exemplo de classe abstrata

A.11 Manipulação de Arquivos


Python oferece funcionalidades robustas para ler e escrever em arquivos no sistema de
arquivos.
A.11. Manipulação de Arquivos 577

A.11.1 Abrindo e Fechando Arquivos


• A Instrução with open(...) as f: (Forma Recomendada):
Garante que o arquivo seja aberto e, crucialmente, fechado automaticamente, mesmo
que ocorram erros.
1 # Exemplo de abertura para leitura
2 with open ( " meu_arquivo . txt " , " r " , encoding = " utf -8 " ) as
arquivo_leitura :
3 conteudo = arquivo_leitura . read ()
4 O arquivo é automaticamente fechado ao sair deste bloco
5
6 # Exemplo de abertura para escrita
7 with open ( " outro_arquivo . txt " , " w " , encoding = " utf -8 " ) as
arquivo_escrita :
8 arquivo_escrita . write ( " Ol á , arquivo !\ n " )
9 # Fechamento autom á tico

Código A.60: Usando with open() para arquivos

• Função open(caminho, modo, encoding=None):

– caminho: String com o nome/caminho do arquivo.


– modo: String especificando como abrir o arquivo. Principais modos:
∗ ’r’: Leitura (padrão). Erro se o arquivo não existir.
∗ ’w’: Escrita. Cria o arquivo se não existir; sobrescreve o conteúdo se
existir.
∗ ’a’: Anexar (append). Cria se não existir; adiciona ao final do arquivo.
∗ ’x’: Criação exclusiva. Falha se o arquivo já existir.
∗ Adicionar ’+’ para leitura e escrita (ex: ’r+’, ’w+’).
∗ Adicionar ’b’ para modo binário (ex: ’rb’, ’wb’).
– encoding="utf-8": Especifica a codificação para arquivos de texto (ex: "utf-8",
"latin-1"). Crucial para caracteres acentuados. Sempre recomendado para
texto.

• Fechando Arquivos Manualmente (Menos Recomendado): [Link]().


Se não usar with, você é responsável por fechar.

A.11.2 Lendo Arquivos de Texto


Supondo que f é um objeto arquivo aberto para leitura em modo texto:

• conteudo_completo = [Link](tamanho_opcional): Lê o arquivo inteiro (ou tamanho


caracteres/bytes) para uma única string. Cuidado com arquivos grandes.

• linha_unica = [Link](): Lê uma única linha do arquivo (incluindo o \n no


final, se houver). Retorna string vazia no final do arquivo.

• lista_de_linhas = [Link](): Lê todas as linhas restantes do arquivo e as


retorna como uma lista de strings (cada string inclui o \n). Cuidado com arquivos
grandes.
578 Apêndice A. Guia Rápido de Sintaxe Python

• Iterando sobre o Objeto Arquivo (Forma Pythônica para ler linha por
linha):
1 with open ( " meu_arquivo . txt " , " r " , encoding = " utf -8 " ) as
f_iter :
2 for linha_lida in f_iter :
3 print ( linha_lida . strip () ) # . strip () remove \ n e
espa ç os extras

Código A.61: Iterando para ler arquivo linha por linha

A.11.3 Escrevendo em Arquivos de Texto


Supondo que f é um objeto arquivo aberto para escrita (’w’) ou anexação (’a’) em
modo texto:

• [Link](string_para_escrever): Escreve a string_para_escrever no arquivo.


Não adiciona \n automaticamente. Se precisar de nova linha, inclua-o na string
(ex: "texto\n").

• [Link](lista_de_strings): Escreve cada string de uma lista (ou outro


iterável) no arquivo. Também não adiciona \n entre as strings; elas devem contê-lo
se desejado.

1 with open ( " saida . txt " , " w " , encoding = " utf -8 " ) as f_out :
2 f_out . write ( " Primeira linha de sa í da .\ n " )
3 f_out . write ( " Segunda linha .\ n " )
4 linhas = [ " Terceira linha \ n " , " Quarta linha \ n " ]
5 f_out . writelines ( linhas )

Código A.62: Escrevendo em arquivo de texto

A.11.4 Módulo [Link] (Funções Comuns para Caminhos)


O módulo [Link] (geralmente usado após import os) fornece funções para manipular
caminhos de arquivos de forma portável.

• [Link](parte1, parte2, ...): Junta componentes de caminho com o


separador correto do SO.

• [Link](caminho): Retorna True se o caminho existir.

• [Link](caminho): True se for um arquivo regular.

• [Link](caminho): True se for um diretório.

• [Link](caminho): Retorna o caminho absoluto de um caminho (relativo


ou não).

• [Link](caminho): Retorna a parte final do caminho (nome do arquivo


ou diretório).

• [Link](caminho): Retorna a parte do diretório do caminho.


A.12. Tratamento de Exceções 579

• [Link](caminho): Retorna uma tupla (nome_base, .extensao).

1 import os
2
3 caminho_teste = os . path . join ( " pasta_exemplo " , " arquivo . txt " )
4 print ( f " Caminho constru í do : { caminho_teste } " )
5 # print ( f " Existe ? { os . path . exists ( caminho_teste ) }") # Depender á se
existe
6 print ( f " Nome base : { os . path . basename ( caminho_teste ) } " )
7 print ( f " Diret ó rio : { os . path . dirname ( caminho_teste ) } " )
8 nome , ext = os . path . splitext ( caminho_teste )
9 print ( f " Nome : { nome } , Extens ã o : { ext } " )

Código A.63: Exemplos com [Link]

A.11.5 Arquivos Binários (Breve Menção)


Para arquivos que não são texto (imagens, áudio, executáveis), use o modo binário
adicionando ’b’ ao modo (ex: ’rb’ para leitura binária, ’wb’ para escrita binária).

• Não se usa o parâmetro encoding.

• [Link]() retorna um objeto bytes.

• [Link](objeto_bytes) espera um objeto bytes (ou bytearray).

1 # Leitura bin á ria ( exemplo )


2 with open ( " imagem . png " , " rb " ) as f_bin_in :
3 dados_bytes = f_bin_in . read ()
4 # ’ dados_bytes ’ é um objeto bytes
5
6 # Escrita bin á ria ( exemplo )
7 b y te s_p a ra _ e sc r ev e r = b " \ x00 \ x01 \ x02 \ x03 "
8 with open ( " dados . bin " , " wb " ) as f_bin_out :
9 f_bin_out . write ( by t e s_ p ar a _ es c re v e r )

Código A.64: Leitura/Escrita Binária (Conceitual)

A manipulação detalhada de formatos binários específicos geralmente requer bibliotecas


especializadas.

A.12 Tratamento de Exceções


O tratamento de exceções permite que seus programas lidem com erros que ocorrem
durante a execução (runtime errors) de forma controlada, evitando que o programa pare
abruptamente.

A.12.1 O Bloco try-except


A estrutura fundamental para tratar exceções. Sintaxe:
580 Apêndice A. Guia Rápido de Sintaxe Python

1 try :
2 # Bloco de c ó digo que pode levantar uma exce ç ã o
3 # Ex : resultado = 10 / 0
4 pass
5 except N o m e D a E x c e c a o E s p e c i f i c a :
6 # Bloco de c ó digo executado SE N o m e D a E x c e c a o E s p e c i f i c a ocorrer no
try
7 # Ex : print (" Erro : Divis ã o por zero n ã o permitida !")
8 pass
9 # C ó digo continua aqui se a exce ç ã o foi tratada

Código A.65: Sintaxe básica do try-except

• O código no bloco try é executado.

• Se uma exceção do tipo NomeDaExcecaoEspecifica (ou uma subclasse dela) ocorre,


o bloco except correspondente é executado.

• Se nenhuma exceção ocorre, o bloco except é ignorado.

A.12.2 Capturando Múltiplas Exceções


• Múltiplas Cláusulas except: Para tratar diferentes tipos de exceção de formas
diferentes.
1 try :
2 valor = int ( input ( " Digite um n ú mero : " ) )
3 inverso = 1 / valor
4 except ValueError :
5 print ( " Entrada inv á lida , n ã o era um n ú mero . " )
6 except ZeroDivis ionError :
7 print ( " N ã o é poss í vel dividir por zero . " )

Código A.66: Múltiplas cláusulas except

• Agrupando Exceções: Se o tratamento for o mesmo para vários tipos.


1 try :
2 # ... c ó digo ...
3 pass
4 except ( TypeError , ValueError ) :
5 print ( " Ocorreu um erro de tipo ou valor . " )

Código A.67: Agrupando exceções

• Capturando a Instância da Exceção: Para acessar a mensagem de erro ou outros


detalhes.
1 try :
2 # ... c ó digo ...
3 pass
4 except FileNotFo undError as e : # ’e ’ é a inst â ncia da
exce ç ã o
5 print ( f " Erro ao acessar arquivo : { e } " )

Código A.68: Capturando a instância da exceção


A.12. Tratamento de Exceções 581

• Cláusula except Genérica (Usar com Cautela):

– except Exception as e:: Captura a maioria das exceções embutidas. Permite


inspecionar o erro e.
– except:: (Sozinho) Captura todas as exceções, incluindo SystemExit e
KeyboardInterrupt. Geralmente desencorajado por poder mascarar bugs.

• Ordem das Cláusulas except: As mais específicas devem vir antes das mais
genéricas.

A.12.3 A Cláusula else no Bloco try-except


O bloco else é executado somente se nenhuma exceção ocorrer no bloco try.
Sintaxe:
1 try :
2 # Opera ç õ es que podem falhar
3 resultado = 10 / 2
4 except Zero Division Error :
5 print ( " Divis ã o por zero . " )
6 else :
7 # Executa se o try foi bem - sucedido
8 print ( f " Resultado ( sem erro ) : { resultado } " )

Código A.69: Sintaxe do try-except-else

Útil para código que depende do sucesso do bloco try.

A.12.4 A Cláusula finally: Código que Sempre Executa


O bloco finally é sempre executado, independentemente de ter ocorrido uma
exceção ou não, e mesmo que haja um return, break ou continue nos blocos anteriores.
Sintaxe:
1 f = None # Para o exemplo
2 try :
3 # f = open (" arquivo . txt " , " w ")
4 # f . write (" teste ")
5 # resultado = 10/0 # Simula erro
6 pass
7 except Zero Division Error :
8 print ( " Erro de divis ã o . " )
9 finally :
10 print ( " Bloco finally : Limpeza de recursos . " )
11 # if f and not f . closed :
12 # f . close ()
13 # print (" Arquivo fechado no finally .")

Código A.70: Sintaxe do try-except-else-finally

Principal uso: Liberar recursos externos (como fechar arquivos, conexões de rede). A
instrução with open(...) já faz isso para arquivos.
582 Apêndice A. Guia Rápido de Sintaxe Python

A.12.5 Levantando Exceções com raise


Permite que seu código dispare (levante) exceções deliberadamente para sinalizar erros.
Sintaxe:

• raise NomeDaExcecao()

• raise NomeDaExcecao("Mensagem descritiva opcional do erro")

• raise (sozinho, dentro de um bloco except, para re-levantar a exceção que foi
capturada)

1 def v e r i f i c a r _ i d a d e _ p a r a _ e n t r a d a ( idade ) :
2 if idade < 18:
3 raise ValueError ( " Acesso negado : Idade menor que 18 anos . " )
4 print ( " Acesso permitido . " )
5

6 try :
7 v e r i f i c a r _ i d a d e _ p a r a _ e n t r a d a (15)
8 except ValueError as e :
9 print ( f " Erro de valida ç ã o : { e } " )

Código A.71: Levantando uma exceção

A.12.6 Definindo Exceções Personalizadas (Breve Menção)


Você pode criar seus próprios tipos de exceção herdando da classe base Exception
(ou de outras exceções embutidas). Isso torna o tratamento de erros mais específico e
semântico para sua aplicação. Sintaxe básica:
1 class Er r o De M eu A pl i c at i vo ( Exception ) :
2 " " " Exce ç ã o base para erros espec í ficos do meu aplicativo . " " "
3 pass
4
5 class E r r o D e E n t r a d a I n v a l i d a ( Er r o De M eu A p li c at i vo ) :
6 def __init__ ( self , mensagem , campo_com_erro = None ) :
7 super () . __init__ ( mensagem )
8 self . campo_com_erro = campo_com_erro
9
10 # # Levantando :
11 # # raise E r r o D e E n t r a d a I n v a l i d a (" Formato de data inv á lido ." ,
" data_nascimento ")

Código A.72: Definindo uma exceção personalizada

Usar exceções personalizadas é uma boa prática para bibliotecas e aplicações maiores.

A.13 Bibliotecas Essenciais (Breve Menção)


Ao longo deste livro, especialmente na Parte VI, introduzimos algumas bibliotecas
Python fundamentais para computação numérica, análise de dados e visualização. Este
é um lembrete rápido de suas convenções de importação e propósitos principais. Para
detalhes, consulte os capítulos dedicados a cada uma.
A.13. Bibliotecas Essenciais (Breve Menção) 583

A.13.1 NumPy (np)


Biblioteca fundamental para computação numérica em Python.

• Importação Padrão:
1 import numpy as np

Código A.73: Importando NumPy

• Principal Estrutura: O array N-dimensional (ndarray) para operações eficientes


com vetores e matrizes.

• Uso Comum: Criação de arrays, operações matemáticas vetorizadas, álgebra linear


básica.
1 array_np = np . array ([10 , 20 , 30])
2 print ( array_np * 2) # Sa í da : [20 40 60]

Código A.74: Exemplo NumPy conciso

A.13.2 Pandas (pd)


Biblioteca poderosa para manipulação e análise de dados tabulares e séries temporais.

• Importação Padrão:
1 import pandas as pd

Código A.75: Importando Pandas

• Estruturas Principais:

– Series: Array unidimensional rotulado.


– DataFrame: Estrutura de dados tabular bidimensional (como uma planilha).

• Uso Comum: Ler/escrever dados (CSV, Excel), limpar, transformar, filtrar, agrupar
e analisar dados.
1 dados = { ’ coluna_A ’: [1 , 2] , ’ coluna_B ’: [ ’x ’ , ’y ’ ]}
2 df = pd . DataFrame ( dados )
3 print ( df . shape ) # Sa í da : (2 , 2)

Código A.76: Exemplo Pandas conciso

A.13.3 Matplotlib (pyplot como plt)


Biblioteca fundamental para a criação de gráficos estáticos 2D (e alguns 3D) em Python.

• Importação Padrão (Módulo pyplot):


1 import matplotlib . pyplot as plt

Código A.77: Importando Matplotlib pyplot


584 Apêndice A. Guia Rápido de Sintaxe Python

• Uso Comum: Gerar gráficos de linha ([Link]()), dispersão ([Link]()),


barras ([Link]()), histogramas ([Link]()), personalizar títulos, rótulos, legen-
das, e exibir ([Link]()) ou salvar ([Link]()) gráficos.
1 plt . plot ([1 , 2 , 3 , 4] , [10 , 20 , 25 , 30])
2 plt . title ( " Meu Gr á fico Simples " )
3 # plt . show () # Para exibir

Código A.78: Exemplo Matplotlib conciso

A.13.4 Seaborn (sns)


Biblioteca de visualização de dados baseada no Matplotlib, que fornece uma interface
de alto nível para desenhar gráficos estatísticos atraentes e informativos.

• Importação Padrão:
1 import seaborn as sns

Código A.79: Importando Seaborn

• Uso Comum: Facilita a criação de gráficos de distribuição (histplot, kdeplot,


boxplot), gráficos categóricos (barplot, countplot), gráficos de relação (scatterplot,
lineplot) e mapas de calor (heatmap). Frequentemente usado com DataFrames
Pandas. Permite definir temas estéticos com sns.set_theme().
1 sns . set_theme () # Aplica tema padr ã o Seaborn
2 dados_aleatorios = np . random . randn (100)
3 sns . histplot ( dados_aleatorios , kde = True )
4 # plt . show ()

Código A.80: Exemplo Seaborn conciso

A.13.5 SciPy
Biblioteca que complementa o NumPy com uma vasta coleção de algoritmos e ferra-
mentas de nível mais alto para computação científica e de engenharia.

• Importação: Geralmente se importam submódulos específicos.


1 from scipy import stats
2 from scipy . optimize import root_scalar
3 import scipy . linalg as spla
4 # etc .

Código A.81: Importando submódulos SciPy

• Principais Submódulos (Exemplos):

– [Link]: Funções matemáticas especiais.


– [Link]: Álgebra linear avançada (decomposições, funções de matrizes).
– [Link]: Otimização e busca de raízes.
– [Link]: Interpolação.
A.13. Bibliotecas Essenciais (Breve Menção) 585

– [Link]: Integração numérica.


– [Link]: Processamento de sinais.
– [Link]: Estatísticas e distribuições de probabilidade.

• Uso Comum: Resolução de equações, otimização de parâmetros, integração de


funções ou dados, análise estatística avançada, processamento de sinais, etc.
1 from scipy . optimize import root_scalar
2 def f ( x ) : return x **2 - 4
3 sol = root_scalar (f , bracket =[0 , 3])
4 print ( f " Raiz de x ^2 - 4 = 0: { sol . root if sol . converged
else ’N ã o convergiu ’} " )

Código A.82: Exemplo SciPy conciso (busca de raiz)

Este guia rápido apenas arranha a superfície do que cada uma dessas bibliotecas pode
fazer. Consulte os capítulos dedicados e a documentação oficial para um aprendizado mais
aprofundado.
Apêndice B

Recursos Online e Comunidades Python

Aprender Python, como qualquer linguagem de programação ou nova habilidade técnica,


é uma jornada contínua. Este livro, “Python Descomplicado: Seu Guia Prático para
Começar a Programar”, foi projetado para fornecer a você uma base sólida e abrangente,
desde os fundamentos da linguagem até a aplicação de bibliotecas essenciais para engenharia
e análise de dados.
No entanto, o universo Python é vasto e está em constante evolução. Felizmente, existe
uma quantidade imensa de recursos online de alta qualidade e comunidades vibrantes
prontas para ajudar você a continuar aprendendo, a tirar dúvidas, a se manter atualizado
e a se conectar com outros desenvolvedores e entusiastas da linguagem.
Este apêndice tem como objetivo listar alguns dos recursos e comunidades mais valiosos
que podem complementar seu aprendizado e acompanhar você em seus próximos passos
com Python.

B.1 Documentação Oficial do Python


Sem dúvida, a fonte primária e mais definitiva de informação sobre a linguagem Python
e sua biblioteca padrão é a documentação oficial do Python. Ela é mantida pela
Python Software Foundation e está disponível gratuitamente online.

• Website Principal: [Link] (para a versão mais recente, ou


você pode selecionar a versão específica que está usando).

• O que você encontrará lá:

– O Tutorial Oficial do Python: Uma excelente introdução, mais aprofundada


em alguns aspectos, que pode servir como um ótimo complemento a este livro.
– Referência da Linguagem Python (Language Reference): Descreve a
sintaxe e a semântica da linguagem em detalhes técnicos. É o manual definitivo.
– Referência da Biblioteca Padrão (Standard Library Reference): De-
talha todos os módulos, funções e classes que vêm com a instalação padrão
do Python (como os módulos math, datetime, os, json, csv, abc, itertools,
collections, e centenas de outros). Essencial para descobrir o que já está
disponível para você.
– Guias de Instalação e Uso (Python Setup and Usage): Informações
sobre como instalar e configurar o Python em diferentes sistemas operacionais.

586
B.2. Google Colaboratory (Colab) com o Código do Livro 587

– Python HOWTOs: Guias práticos sobre tópicos específicos (ex: logging,


Unicode, expressões regulares).

• Por que é importante: A documentação oficial é precisa, completa e sempre atua-


lizada. Desenvolver o hábito de consultá-la é uma das habilidades mais importantes
para um programador Python.

B.2 Google Colaboratory (Colab) com o Código do


Livro
Para facilitar sua jornada de aprendizado e permitir que você execute e experimente
com todos os exemplos de código apresentados neste livro de forma interativa, foi preparado
um conjunto de notebooks Google Colaboratory (Colab).

• O que é o Google Colab? O Colaboratory, ou “Colab” para abreviar, é um


produto do Google Research. O Colab permite que qualquer pessoa escreva e execute
código Python arbitrário através do navegador. É especialmente bem adaptado para
aprendizado de máquina, análise de dados e educação, pois não requer nenhuma
configuração de ambiente no seu computador e oferece acesso gratuito a recursos de
computação (como GPUs e TPUs, embora não sejam necessários para os exemplos
deste livro).

• Acesso aos Notebooks do Livro: Você pode encontrar todos os exemplos de


código deste livro, organizados por capítulo, no seguinte endereço:

[Link]

• Como Usar:

1. Ao abrir o link, você verá o notebook no ambiente Colab.


2. Para poder editar e salvar suas próprias alterações, é altamente recomendável
que você primeiro salve uma cópia do notebook no seu próprio Google Drive
(Arquivo → Salvar uma cópia no Drive) ou faça o download do notebook
(Arquivo → Fazer download → Fazer download do .ipynb).
3. As células de código podem ser executadas clicando no botão de “play” à
esquerda da célula ou pressionando Shift+Enter.
4. Você pode modificar o código existente, adicionar novas células de código ou
texto para suas próprias anotações e experimentos.

• Vantagens:

– Sem Configuração: Não é preciso instalar Python ou bibliotecas no seu


computador para começar.
– Interativo: Permite executar o código trecho por trecho e ver os resultados
imediatamente.
– Acessível: Gratuito e disponível em qualquer lugar com um navegador e
conexão à internet.
588 Apêndice B. Recursos Online e Comunidades Python

Este recurso é uma excelente maneira de praticar ativamente os conceitos e exemplos


apresentados em cada capítulo.

B.3 Outras Plataformas de Aprendizado e Cursos On-


line
Além da documentação oficial e dos materiais de apoio deste livro, existe uma miríade
de plataformas online que oferecem cursos, tutoriais e trilhas de aprendizado em Python,
desde o nível básico até tópicos altamente especializados. Muitas dessas plataformas
oferecem tanto conteúdo gratuito quanto pago. A escolha dependerá do seu estilo de
aprendizado, do seu orçamento e dos seus objetivos específicos.
Algumas plataformas internacionais e nacionais bem conceituadas onde você pode
encontrar cursos de Python de qualidade incluem:

• Coursera ([Link]): Oferece cursos, especializações e até mesmo gradu-


ações online de universidades e empresas renomadas mundialmente. Muitos cursos
de introdução à programação com Python e de ciência de dados usam Python.

• edX ([Link]): Similar ao Coursera, é uma plataforma fundada por Harvard


e MIT, com cursos de alta qualidade de diversas universidades de ponta, incluindo
muitos sobre Python e suas aplicações.

• Udemy ([Link]): Um vasto mercado de cursos online, onde instrutores


independentes publicam conteúdo sobre praticamente qualquer tópico imaginável,
incluindo uma enorme variedade de cursos de Python para todos os níveis, desde
iniciantes até avançados, e para nichos específicos.

• Khan Academy ([Link]): Embora mais conhecida por matemática


e ciências para níveis escolares, a Khan Academy também oferece introduções à
programação que podem ser úteis para solidificar conceitos básicos, e possui conteúdo
interativo.

• Alura ([Link]): Uma das maiores plataformas brasileiras de cursos


de tecnologia, com diversas formações e cursos focados em Python, desde a lógica
de programação até desenvolvimento web, ciência de dados e mobile com Python.
Conteúdo em português e focado no mercado brasileiro.

• Curso em Vídeo ([Link]): Plataforma muito popular no Brasil,


conhecida por seus cursos gratuitos e de alta qualidade ministrados pelo Professor
Gustavo Guanabara. Oferece excelentes cursos de Python para iniciantes, com uma
didática muito elogiada.

• freeCodeCamp ([Link]): Embora mais focado em desenvolvi-


mento web, oferece certificações gratuitas que incluem Python para computação
científica e machine learning, com projetos práticos.

• Codecademy ([Link]): Famosa por seu ambiente de aprendizado


interativo diretamente no navegador, oferecendo cursos de Python que cobrem desde
o básico até aplicações mais avançadas.
B.4. Documentação das Principais Bibliotecas Científicas e de Dados 589

Além dessas plataformas estruturadas, o YouTube é um repositório imenso de tutoriais,


palestras e cursos gratuitos sobre Python. Muitos canais dedicados à programação e
tecnologia possuem playlists excelentes para aprender Python, cobrindo desde o básico
até tópicos muito específicos. Pesquise por termos como “curso de python para iniciantes”,
“python para engenharia”, “tutoriais numpy python”, etc.
A chave é encontrar o recurso ou plataforma cujo estilo de ensino melhor se adapte a
você e aos seus objetivos de aprendizado. Muitas oferecem módulos ou aulas gratuitas
para você experimentar antes de se comprometer com um curso pago.

B.4 Documentação das Principais Bibliotecas Científi-


cas e de Dados
Ao longo da Parte VI deste livro, introduzimos algumas das bibliotecas mais poderosas
e fundamentais do ecossistema Python para computação numérica, manipulação de dados,
visualização e computação científica: NumPy, Pandas, Matplotlib, Seaborn e SciPy.
Embora os capítulos dedicados a elas tenham fornecido uma base sólida, cada uma
dessas bibliotecas é vasta e possui uma quantidade imensa de funcionalidades, módulos,
classes e funções que não poderíamos cobrir exaustivamente.
Para realmente dominar essas ferramentas e explorar todo o seu potencial para os seus
projetos específicos de engenharia e análise de dados, a documentação oficial de cada
biblioteca é o seu recurso mais valioso e preciso. Elas são geralmente muito bem escritas,
contêm tutoriais, guias de usuário, exemplos de código e uma referência completa da API
(Application Programming Interface).
Aqui estão os links para as páginas principais da documentação dessas bibliotecas:
• NumPy:
Para tudo relacionado a arrays N-dimensionais, funções matemáticas vetorizadas,
álgebra linear fundamental e muito mais.
– Documentação Oficial do NumPy: [Link]
• Pandas:
Para manipulação e análise de dados tabulares com Series e DataFrames, leitura e
escrita de arquivos, tratamento de dados ausentes, agrupamento, junção de tabelas,
etc.
– Documentação Oficial do Pandas: [Link]
• Matplotlib:
Para criar uma vasta gama de gráficos estáticos, animados e interativos. Lembre-se
que exploramos principalmente a interface pyplot.
– Documentação Oficial do Matplotlib: [Link]
html
– Galeria de Exemplos (ótima para inspiração): [Link]
stable/gallery/[Link]
• Seaborn:
Para criar gráficos estatísticos informativos e atraentes com uma interface de alto
nível.
590 Apêndice B. Recursos Online e Comunidades Python

– Documentação Oficial do Seaborn: [Link]


(para a API)
– Tutorial e Galeria de Exemplos: [Link]
html e [Link]

• SciPy:
Para algoritmos científicos e de engenharia de nível mais alto, construídos sobre o
NumPy (otimização, integração, interpolação, processamento de sinais, estatísticas
avançadas, etc.).

– Documentação Oficial da SciPy (Referência da Biblioteca): [Link]


[Link]/doc/scipy/reference/
– Tutoriais da SciPy: [Link]
html

Desenvolver o hábito de consultar a documentação oficial é uma habilidade crucial


para qualquer programador. Não apenas para encontrar informações sobre como usar uma
função específica, mas também para descobrir novas funcionalidades, entender as melhores
práticas e se aprofundar nos conceitos por trás das ferramentas que você está utilizando.
Muitas documentações também incluem seções sobre como contribuir com o projeto, se
você se interessar em ir ainda mais longe.
Guardar esses links e usá-los como referência constante irá acelerar muito o seu
desenvolvimento e a sua capacidade de resolver problemas complexos com Python.

Concluindo o Apêndice B: Sua Jornada de Aprendizado


Contínuo
Neste apêndice, compilamos uma lista de recursos que acreditamos serem extremamente
valiosos para complementar o seu aprendizado de Python e para auxiliá-lo em sua jornada
contínua como desenvolvedor ou engenheiro que utiliza esta poderosa linguagem.
Exploramos a importância da documentação oficial do Python como sua fonte
primária e definitiva de informação. Destacamos o Google Colaboratory com o código
deste livro, uma ferramenta interativa para você executar e modificar todos os exemplos
práticos. Apresentamos também uma variedade de outras plataformas de aprendizado
e cursos online onde você pode encontrar diferentes abordagens e aprofundamentos em
tópicos específicos. Finalmente, listamos os links para as documentações oficiais das
principais bibliotecas científicas e de dados (NumPy, Pandas, Matplotlib, Seaborn,
SciPy), que são leituras essenciais para quem deseja dominar essas ferramentas.
O mundo do Python é vasto e dinâmico, com uma comunidade global incrivelmente
ativa e colaborativa. Os recursos online, fóruns, grupos de usuários e a documentação
aberta são testemunhos desse espírito. Encorajamos você a:

• Ser proativo na busca por conhecimento: se você tem uma dúvida, é provável que
alguém já a teve e a resposta está disponível online.

• Experimentar muito: a melhor forma de aprender a programar é programando,


testando, errando e corrigindo.
B.4. Documentação das Principais Bibliotecas Científicas e de Dados 591

• (Quando se sentir confortável) Participar de comunidades: fazer perguntas, respon-


der a dúvidas de outros (quando souber a resposta) e compartilhar seus projetos são
ótimas formas de aprender e crescer.

Lembre-se que aprender a programar e a utilizar novas ferramentas é um processo


contínuo. As bibliotecas evoluem, novas técnicas surgem, e sempre haverá algo novo para
descobrir. Esperamos que os recursos listados aqui sirvam como um excelente ponto de
partida e como companheiros constantes em sua jornada com Python.

Você também pode gostar