Livro Programar Python
Livro Programar Python
Organizadora:
Maria Inês Vasconcellos Furtado
Autores:
Maria Inês Vasconcellos Furtado
Pedro Ramos Brandão
José Cláudio Garcia Damaso
Lúcio Pereira de Andrade
Inês Gonçalves Saúde de Almeida
2
PREFÁCIO
A experiência do outro é o segredo da mudança 1.¹
Experiência para compartilhar não falta para os autores deste livro. Inês
Gonçalves Saúde de Almeida leciona Programação em Python desde 2020 e,
profissionalmente, utiliza Python para a análise de dados, além de ter
desenvolvido um chatbot de inteligência artificial em Python para apoio a
alunos e professores. José Cláudio Garcia Damaso atua como professor em
cursos de graduação há 23 anos e possui experiência de mais de 20 anos de
mercado no desenvolvimento de sistemas nas áreas financeiro-contábil,
acadêmica e administrativa usando diversas linguagens de programação.
Lúcio Pereira de Andrade possui mais de 30 anos de experiência em análise,
desenvolvimento e ensino de sistemas de informação, sua relação com
Python tem se dado pela aplicação em pesquisa em visão computacional e
pelo ensino de introdução à lógica de programação. Maria Inês Vasconcellos
Furtado utiliza Python no desenvolvimento de algoritmos e sistemas
inteligentes e também no ensino em sala de aula, leciona no ensino superior
há quase 30 anos, com experiência sólida em programação, IA e tecnologias
emergentes. Pedro Ramos Brandão realiza investigação em cibersegurança
utilizando Inteligência Artificial, com algoritmos desenvolvidos em Python.
Logo, a semelhança com o real existe porque foi esta a opção deste grupo de
professores: trazer à luz os resultados de uma experiência no exercício de
1
1 PAZ, Octavio. Convergências: ensaios sobre a arte e a literatura. Tradução por Moacir Werneck de Castro. Rio de
Janeiro: Rocco, 1991. p. 54. Tradução de Convergences.
3
lecionar. E o que temos? Um manual didático que alinha capítulos que
apresentam a teoria sempre seguidos de seções que destacam a aplicabilidade
do que foi exposto. Sabemos que, quando damos ênfase ao carácter prático
do que ensinamos, estabelecemos uma relação de sedução com os nossos
alunos. Isso não se limita à proposta de exercícios, encontramos exemplos
práticos comentados e estudos de casos. As respostas das atividades
contempladas ao final do livro permitem o estudo e o aprofundamento por
parte do leitor com autonomia para ampliar o seu saber nesta temática.
4
DEDICATÓRIA
Às nossas famílias, pelo apoio, e aos
nossos alunos, pela inspiração.
5
ÍNDICE
1 CONCEITOS BÁSICOS DE PROGRAMAÇÃO EM PYTHON ............ 13
1.1 INSTALAÇÃO DO PYTHON ........................................................................ 14
1.2 O INTERPRETADOR PYTHON .................................................................... 15
1.3 IDLE: AMBIENTE DE DESENVOLVIMENTO INTEGRADO........................... 16
1.3.1 Aceder ao IDLE .............................................................................. 17
1.4 PRIMEIRO PROGRAMA EM PYTHON .......................................................... 18
1.5 CARACTERÍSTICAS PRINCIPAIS DO PYTHON ............................................. 19
1.6 EXERCÍCIOS............................................................................................. 20
2 TIPOS DE DADOS E ESTRUTURAS BÁSICAS ..................................... 22
2.1 NÚMEROS, TEXTO E BOOLEANOS ............................................................. 22
2.2 CONVERSÃO DE TIPOS ............................................................................. 24
2.3 CONSTANTES E BOAS PRÁTICAS .............................................................. 25
2.4 COMENTÁRIOS E DOCSTRINGS ................................................................ 26
2.4.1 Comentários Simples ...................................................................... 26
2.4.2 Docstrings Básicas ......................................................................... 26
2.4.3 Quando Usar # e ´´´ ....................................................................... 27
2.5 BIBLIOTECAS........................................................................................... 28
2.5.1 Importação de biblioteca ................................................................ 29
2.5.2 Bibliotecas Mais Utilizadas ............................................................ 32
2.6 EXERCÍCIOS PROPOSTOS ......................................................................... 34
3 OPERADORES E EXPRESSÕES .............................................................. 36
3.1 OPERADORES ARITMÉTICOS .................................................................... 36
3.1.1 Precedência de Operadores ........................................................... 38
3.1.2 Operadores de atribuição composta .............................................. 39
3.2 OPERADORES RELACIONAIS .................................................................... 40
3.2.1 Comparações Encadeadas em Python............................................ 40
3.3 OPERADORES LÓGICOS ........................................................................... 43
3.3.1 Avaliação de Curto-Circuito (Short-Circuit Evaluation). .............. 43
3.4 OPERADORES DE IDENTIDADE ................................................................. 44
3.4.1 Diferença entre os operadores == e is .......................................... 45
3.5 OPERADORES DE ASSOCIAÇÃO ................................................................ 46
3.6 EXERCÍCIOS............................................................................................. 46
4 ENTRADA E SAÍDA DE DADOS .............................................................. 48
4.1 FUNÇÃO INPUT() PARA ENTRADA DE DADOS............................................ 48
4.2 CONVERSÃO DE TIPOS DE DADOS ............................................................ 49
6
4.3 FUNÇÃO PRINT() PARA SAÍDA DE DADOS ............................................... 51
4.4 PERSONALIZAÇÃO DA SAÍDA COM PRINT() .............................................. 52
4.5 TÉCNICAS DE FORMATAÇÃO DE STRINGS ................................................. 53
4.5.1 Concatenação com vírgulas e com o operador +........................... 53
4.5.2 Método [Link]() ........................................................................ 54
4.5.3 f-strings (Formatted String Literals) .............................................. 55
4.6 EXERCÍCIOS PROPOSTOS .......................................................................... 62
5 ESTRUTURAS DE CONTROLO .............................................................. 64
5.1 ESTRUTURAS CONDICIONAIS (IF, ELIF, ELSE) .................................... 64
5.1.1 Conceito fundamental em Python: indentação ............................... 64
5.1.2 Estrutura if ..................................................................................... 66
5.1.3 Estrutura if-else .............................................................................. 67
5.1.4 Estrutura if-elif-else........................................................................ 68
5.1.5 Operador Ternário ......................................................................... 69
5.1.6 Condições múltiplas com operadores lógicos ................................ 71
5.2 CICLOS (FOR, WHILE) ............................................................................. 72
5.2.1 Ciclo for.......................................................................................... 72
5.2.2 Função range() ............................................................................... 73
5.2.3 Ciclo while ...................................................................................... 75
5.2.4 Controlo de ciclos: break e continue .............................................. 76
5.3 ESTRUTURAS DE CONTROLO ANINHADAS ............................................... 78
5.3.1 Ciclos Dentro de Ciclos.................................................................. 78
5.4 ESTRUTURAS CONDICIONAIS DENTRO DE CICLOS ................................... 79
5.5 CICLOS DENTRO DE ESTRUTURAS CONDICIONAIS ................................... 80
5.6 EXEMPLO PRÁTICO DE ESTRUTURAS ANINHADAS.................................... 82
5.7 GERAÇÃO DE NÚMEROS ALEATÓRIOS COM O MÓDULO RANDOM............... 85
5.7.1 [Link]() ........................................................................... 85
5.7.2 [Link](a, b) ...................................................................... 85
5.7.3 [Link](a, b) ..................................................................... 86
5.7.4 [Link](sequencia).............................................................. 86
5.7.5 [Link](lista) ...................................................................... 86
5.7.6 Exemplos de aplicações práticas .................................................... 87
5.7.7 Controlar a aleatoriedade com sementes (seeds) ....................... 89
5.7.8 Limitações e considerações ............................................................ 91
5.8 TRATAMENTO DE ERROS COM TRY-EXCEPT............................................. 92
5.8.1 Tipos comuns de erros .................................................................... 94
5.8.2 Múltiplos blocos except .................................................................. 94
5.8.3 Bloco else e finally.......................................................................... 94
5.8.4 Boas práticas .................................................................................. 95
5.9 EXERCÍCIOS PROPOSTOS .......................................................................... 96
7
6 FUNÇÕES EM PYTHON ......................................................................... 103
6.1 DEFINIÇÃO DE FUNÇÕES ........................................................................ 103
6.2 ÂMBITO DE VARIÁVEIS .......................................................................... 105
6.2.1 Âmbito Local e Global.................................................................. 105
6.2.2 Palavra-chave: global .............................................................. 107
6.2.3 Âmbito não local (variáveis em funções aninhadas) .................... 108
6.2.4 Exemplo comparativo de âmbito de variáveis .............................. 109
6.3 TIPOS DE FUNÇÕES ................................................................................ 111
6.3.1 Funções sem parâmetros e sem retorno ....................................... 111
6.3.2 Funções com parâmetros e sem retorno ....................................... 112
6.3.3 Funções com parâmetro e com retorno ........................................ 113
6.3.4 Funções com múltiplos retornos................................................... 115
6.4 ARGUMENTOS OPCIONAIS E NOMEADOS ................................................ 116
6.4.1 Parâmetros com valores padrão (opcionais) ............................... 117
6.4.2 Múltiplos parâmetros opcionais ................................................... 118
6.4.3 Argumentos nomeados (keyword arguments) ............................... 120
6.5 FUNÇÕES ANÓNIMAS (LAMBDA).............................................................. 121
6.6 MAIN() E O GUARDIÃO IF __NAME__ == "__MAIN__": ................. 122
6.6.1 Comportamento na prática........................................................... 126
6.6.2 Erros comuns e como os evitar..................................................... 128
6.7 EXEMPLOS PRÁTICOS ............................................................................ 131
6.7.1 Exemplo 1: Sistema de Pontuação para Jogo .............................. 132
6.7.2 Exemplo 2: Analisador de Estatísticas de Streamer ..................... 134
6.7.3 Exemplo 3: Sistema de Conquistas para App Fitness .................. 136
6.7.4 Exemplo 4: Mistura de Poções num Jogo de Fantasia................. 138
6.8 EXERCÍCIOS PROPOSTOS ........................................................................ 140
7 ESTRUTURAS DE DADOS EM PYTHON ............................................ 146
7.1 LISTAS................................................................................................... 146
7.1.1 Criar e aceder a listas .................................................................. 147
7.1.2 Alterar e adicionar elementos a uma lista .................................... 148
7.1.3 Iterar sobre listas ......................................................................... 150
7.1.4 List Comprehensions (Compreensões de Lista)............................ 153
7.1.5 Listas Multidimensionais .............................................................. 157
7.2 TUPLOS ................................................................................................. 158
7.2.1 Utilidade e Vantagens dos tuplos ................................................. 160
7.2.2 Desempacotamento de Tuplos (Unpacking) ................................. 162
7.2.3 Iterar sobre tuplos ........................................................................ 166
7.2.4 Tuplos multidimensionais ............................................................. 167
7.3 CONJUNTOS (SET).................................................................................. 168
7.3.1 Criar um set .................................................................................. 168
8
7.3.2 Operações matemáticas em conjuntos.......................................... 170
7.3.3 Métodos úteis em sets ................................................................... 171
7.4 DICIONÁRIOS (DICT) ............................................................................. 173
7.4.1 Criar e aceder a dicionários ........................................................ 173
7.4.2 Adicionar e alterar valores .......................................................... 174
7.4.3 Métodos úteis ................................................................................ 175
7.5 COMPARAÇÃO E BOAS PRÁTICAS .......................................................... 177
7.5.1 Guia Prático de Seleção por Cenário .......................................... 178
7.5.2 Considerações de Desempenho e Eficiência ................................ 182
7.6 USO PRÁTICO DE LAMBDA EM COLEÇÕES ............................................... 183
7.7 EXEMPLOS PRÁTICOS ............................................................................ 186
7.7.1 Análise de Redes Sociais (Conjuntos) .......................................... 186
7.7.2 Sistema de conquistas em jogos.................................................... 187
7.7.3 Playlist Inteligente ........................................................................ 188
7.7.4 Controlo de Gastos Pessoais ........................................................ 188
7.7.5 Torneio de e-sports ....................................................................... 189
7.7.6 Análise de hábitos digitais............................................................ 190
7.7.7 Sistemas de Recompensa .............................................................. 191
7.7.8 Match de Amizade ........................................................................ 192
7.8 EXERCÍCIOS PROPOSTOS ........................................................................ 193
8 MANIPULAÇÃO E FORMATAÇÃO DE STRINGS ............................ 200
8.1 INDEXAÇÃO E FATIAMENTO .................................................................. 200
8.1.1 Indexação ..................................................................................... 201
8.1.2 Fatiamento (slicing) ..................................................................... 202
8.2 MÉTODOS DE MANIPULAÇÃO ................................................................ 203
8.3 FORMATAÇÃO MODERNA (F-STRINGS) ................................................. 206
8.4 CASOS RESOLVIDOS E EXPLICADOS ...................................................... 213
8.4.1 Normalização de Nomes de Utilizador ......................................... 213
8.4.2 Extração de Informação de um E-mail......................................... 214
8.4.3 Estatísticas de Jogadores - Tabela de Classificação.................... 215
8.5 EXERCÍCIOS PROPOSTOS ....................................................................... 218
9 FICHEIROS EM PYTHON ...................................................................... 223
9.1 ABERTURA DE FICHEIROS COM OPEN() ................................................. 223
9.1.1 Nome e extensão de ficheiros ....................................................... 224
9.1.2 Texto vs. Binário........................................................................... 227
9.1.3 Fechar ficheiros com close() ........................................................ 230
9.1.4 O uso do with................................................................................ 230
9.1.5 Buffering e desempenho ............................................................... 231
9.2 MÉTODOS DE LEITURA DE FICHEIROS .................................................... 232
9
9.2.1 Ler todo o conteúdo (read()) ................................................... 232
9.2.2 Ler linha a linha (readline())................................................ 233
9.2.3 Ler todas as linhas (readlines()) ......................................... 233
9.2.4 Iterar diretamente sobre o ficheiro .............................................. 234
9.3 MÉTODOS DE ESCRITA EM FICHEIROS .................................................... 235
9.3.1 Escrever conteúdo (write()) .......................................................... 235
9.3.2 Escrever múltiplas linhas (writelines()) ....................................... 236
9.4 EXEMPLO PRÁTICO COM FICHEIROS DE TEXTO ...................................... 237
9.5 TRATAMENTO DE EXCEÇÕES ................................................................. 238
9.5.1 Exemplos práticos: leitura e cópia de ficheiros texto ................... 240
9.6 MANIPULAR FICHEIROS CSV E JSON ................................................... 244
9.6.1 Ficheiros CSV (Comma-Separated Values) ................................. 244
9.6.2 Ficheiros JSON (JavaScript Object Notation) ............................. 249
9.7 POSICIONAMENTO E ESCOAMENTO DE DADOS ....................................... 253
9.8 CODIFICAÇÃO DE CARACTERES: ESTRATÉGIA E GESTÃO DE ERROS........ 254
9.8.1 Erros ............................................................................................. 254
9.8.2 Estratégia recomendada ............................................................... 255
9.9 APLICAÇÕES DE FICHEIROS TEXTO ....................................................... 261
9.9.1 Migrar base de dados de clientes para API web .......................... 261
9.9.2 Relatório diário de vendas da loja ............................................... 262
9.9.3 Fechamento diário de caixa ......................................................... 263
9.9.4 Sistema de configuração de aplicação web .................................. 264
9.9.5 Conversor de Formatos (CSV ⇄ JSON)....................................... 265
9.10 FICHEIROS BINÁRIOS ............................................................................. 267
9.10.1 Modo de Abertura de Ficheiros Binários ..................................... 268
9.10.2 Leitura de Ficheiros Binários....................................................... 268
9.10.3 Escrita em Ficheiros Binários ...................................................... 269
9.10.4 Manipulação de Dados Binários .................................................. 270
9.10.5 Ficheiros Binários e struct ........................................................... 270
9.10.6 Assinaturas binárias (magic numbers) ......................................... 271
9.11 EXEMPLOS PRÁTICOS ............................................................................ 273
9.11.1 Analisador de Logs para Deteção de Intrusões............................ 273
9.11.2 Sistema de Treino de Modelo IA com Persistência de Dados ...... 275
9.11.3 Sistema de Proteção de Ficheiros Simples ................................... 277
9.12 EXERCÍCIOS PROPOSTOS ....................................................................... 281
10 PROGRAMAÇÃO ORIENTADA A OBJETOS ................................. 282
10.1 CLASSES E OBJETOS............................................................................... 282
10.2 ATRIBUTOS E MÉTODOS......................................................................... 285
10.3 ENCAPSULAMENTO ............................................................................... 288
10.4 HERANÇA .............................................................................................. 290
10
10.4.1 Herança múltipla .......................................................................... 292
10.5 SOBRESCRITA DE MÉTODOS................................................................... 295
10.6 EXERCÍCIOS PROPOSTOS ....................................................................... 296
11 BIBLIOTECAS ÚTEIS EM PYTHON ................................................ 299
11.1 MÓDULOS E PACOTES ............................................................................ 299
11.1.1 Criar módulos e pacotes próprios ................................................ 301
11.2 BIBLIOTECA PADRÃO – TÓPICOS AVANÇADOS E PRÁTICOS .................... 302
11.2.1 math: precisão, combinatória e utilidades.................................. 302
11.2.2 random: objetos geradores, pesos e amostragem ...................... 303
11.2.3 datetime: fusos horários e horário de verão com zoneinfo 304
11.3 INTRODUÇÃO A NUMPY (ARRAYS, VECTORIZAÇÃO E BROADCASTING) ... 305
11.3.1 Conceitos-chave ........................................................................... 305
11.3.2 Views vs. cópias ............................................................................ 307
11.3.3 Entrada/Saída e tipos de dados .................................................... 308
11.3.4 Estatística e aleatoriedade ........................................................... 309
11.4 INTRODUÇÃO A MATPLOTLIB (API ORIENTADA A OBJETOS) ................ 310
11.4.1 Estrutura geral e filosofia da biblioteca ....................................... 311
11.4.2 Tipos fundamentais de gráficos .................................................... 312
11.4.3 Personalização e estilo ................................................................. 317
11.5 EXERCÍCIOS PROPOSTOS ........................................................................ 318
12 DESENVOLVENDO INTERFACES GRÁFICAS COM TKINTER 321
12.1 INTRODUÇÃO AO TKINTER .................................................................... 321
12.2 CONFIGURAÇÃO INICIAL ....................................................................... 322
12.3 ESTRUTURA BÁSICA DE UMA APLICAÇÃO TKINTER .............................. 323
12.4 WIDGETS FUNDAMENTAIS .................................................................... 325
12.4.1 Label (Rótulos) ............................................................................. 325
12.4.2 Button (Botões) ............................................................................. 327
12.4.3 Entry (Campos de Entrada).......................................................... 327
12.4.4 Text (Área de Texto) ..................................................................... 330
12.5 GERENCIADORES DE LAYOUT ............................................................... 334
12.6 WIDGETS AVANÇADOS ......................................................................... 337
12.7 MENUS E CAIXAS DE DIÁLOGO ............................................................. 340
12.8 APLICAÇÃO: MINI EDITOR DE TEXTO.................................................... 342
12.9 BOAS PRÁTICAS DE DESENVOLVIMENTO .............................................. 344
12.10 CONCLUSÃO ...................................................................................... 344
12.11 EXERCÍCIO PROPOSTO ....................................................................... 345
13 DO DATA MINING AO DASHBOARD.............................................. 347
13.1 IMPORTAÇÃO DE DADOS ........................................................................ 348
11
13.1.1 Padrão .csv ................................................................................... 349
13.1.2 APIs: Conceito e Acesso ............................................................... 350
13.1.3 Organização do Projeto em Módulos ........................................... 354
13.2 EDA: ANÁLISE EXPLORATÓRIA DE DADOS........................................... 355
13.2.1 Análise do conjunto de dados ....................................................... 356
13.2.2 Deteção de Outliers ...................................................................... 363
13.2.3 Correlação entre variáveis ........................................................... 366
13.3 LIMPEZA DE DADOS .............................................................................. 368
13.4 VISUALIZAÇÃO DE DADOS E TOMADA DE DECISÃO .............................. 372
13.5 EXERCÍCIOS PROPOSTOS ....................................................................... 378
14 APLICAÇÕES DE PYTHON EM CIBERSEGURANÇA ................. 380
14.1 FUNDAMENTOS DA SEGURANÇA DA INFORMAÇÃO................................ 381
14.2 GERAÇÃO DE SENHAS E CHAVES SEGURAS ........................................... 381
14.3 HASHING E VERIFICAÇÃO DE INTEGRIDADE .......................................... 382
14.4 CRIPTOGRAFIA SIMÉTRICA .................................................................... 383
14.5 CRIPTOGRAFIA ASSIMÉTRICA E ASSINATURAS DIGITAIS....................... 385
14.6 COMUNICAÇÃO SEGURA COM HTTPS E SSH........................................ 388
14.7 ANÁLISE E VARREDURA DE REDE ......................................................... 390
14.8 BOAS PRÁTICAS E ÉTICA PROFISSIONAL ............................................... 393
14.9 EXERCÍCIOS PROPOSTOS ....................................................................... 394
15 RESPOSTAS........................................................................................... 395
15.1 CAPÍTULO 2 ....................................................................................... 395
15.2 CAPÍTULO 3 ....................................................................................... 397
15.3 CAPÍTULO 4 ....................................................................................... 399
15.4 CAPÍTULO 5 ....................................................................................... 401
15.5 CAPÍTULO 6 ....................................................................................... 411
15.6 CAPÍTULO 7 ....................................................................................... 417
15.7 CAPÍTULO 8 ....................................................................................... 427
15.8 CAPÍTULO 9 ....................................................................................... 436
15.9 CAPÍTULO 10 ..................................................................................... 447
12
1 CONCEITOS BÁSICOS DE PROGRAMAÇÃO EM PYTHON
13
operações matemáticas elementares até à manipulação de ficheiros,
tratamento de datas e horas, compressão de dados, utilização de protocolos
de rede ou até execução de testes automáticos.
É importante ressaltar que o Python é uma linguagem interpretada, ou seja,
o código é lido e executado linha a linha pelo interpretador, sem necessidade
de compilação prévia. Esta característica permite testar instruções de forma
imediata, através do REPL (Read-Eval-Print-Loop), e facilita a deteção de
erros.
Uma das marcas mais distintivas do Python é o uso obrigatório da indentação
para organizar blocos de código. Em vez de recorrer a chavetas ou palavras-
chave adicionais, o recuo define claramente onde começa e termina cada
bloco, o que torna o programa mais legível e reduz ambiguidades.
15
Neste ambiente, podes experimentar comandos simples e ver os resultados
instantaneamente. É uma ferramenta extremamente útil para testes rápidos,
explorar funcionalidades da linguagem ou realizar cálculos como se de uma
calculadora avançada se tratasse.
16
1.3.1 Aceder ao IDLE
17
1.4 Primeiro programa em Python
18
1.5 Características principais do Python
19
desenvolveu milhares de bibliotecas externas para as mais diversas áreas.
Plataformas como o PyPI (Python Package Index) permitem instalar
facilmente bibliotecas como:
⋅ numpy e pandas para cálculo científico e análise de dados.
⋅ matplotlib e seaborn para visualização de dados e criação
de gráficos.
⋅ Flask e Django para desenvolvimento web.
⋅ requests para fazer pedidos HTTP.
Esta extensibilidade permite que developers resolvam problemas complexos
com poucas linhas de código, aproveitando o trabalho já realizado pela
comunidade.
1.6 Exercícios
20
3. Operações no Interpretador: Executar as seguintes operações no
interpretador, ou seja, escrever cada linha e pressionar Enter para ver o
resultado.
21
2 TIPOS DE DADOS E ESTRUTURAS BÁSICAS
Em Python, tudo é um objeto, e cada objeto tem um tipo que define o que é
e que operações pode-se fazer com ele. Os tipos de dados básicos são a base
para toda a programação em Python.
Em Python não é necessário declarar previamente o tipo de uma variável. O
interpretador identifica automaticamente o tipo de dado atribuído e ajusta o
seu comportamento de acordo com o valor. Esta característica torna a escrita
do código mais simples e próxima da linguagem natural, mas exige atenção
para compreender que diferentes tipos de dados podem influenciar o
resultado das operações.
22
b) Texto (str)
Em Python, as sequências de caracteres podem ser delimitadas por aspas
simples ('...') ou por aspas duplas ("..."), sem diferença de
significado.
Para além destas formas, existe ainda a possibilidade de usar aspas triplas
('''...'''). Este recurso é especialmente útil para escrever texto em
várias linhas sem necessidade de caracteres de escape e para documentação
(docstrings). O conteúdo é preservado tal como foi escrito, incluindo as
quebras de linha.
24
O valor da variável x era uma cadeia ("25"), mas após a conversão para
inteiro a soma resulta em 30.
25
2.4 Comentários e Docstrings
Por ser um erro comum, vale ressaltar que não se deve usar aspas triplas
como “comentário de várias linhas” no meio do código, pois isso não é
comentário, é uma string literal que o Python cria e descarta em tempo de
27
execução. Para ser considerada docstring deve estar na primeira linha da
definição.
Veja a seguir um exemplo prático simples, onde as primeiras linhas são
docstrings e não comentário.
2.5 Bibliotecas
28
2.5.1 Importação de biblioteca
b) Importação específica
Na segunda forma de importação, utiliza-se a instrução from ...
import ....
Nesse caso, ao invés de trazer o módulo inteiro para o programa, importam-
se apenas partes específicas, como uma função, uma classe ou uma
29
constante. Esses elementos passam a ficar disponíveis diretamente no espaço
de nomes do programa, ou seja, podem ser usados sem necessidade de
escrever o prefixo do módulo antes.
Essa abordagem torna o código mais conciso e legível, sobretudo quando se
trata de funções ou classes que são usadas com frequência.
Por exemplo, ao importar apenas a função sqrt do módulo math, pode-se
calcular raízes quadradas escrevendo apenas sqrt(25) em vez de
[Link](25). Essa economia de digitação torna-se relevante em
programas maiores ou em situações onde uma função é chamada repetidas
vezes.
Contudo, deve-se ter cuidado, pois ao importar apenas partes específicas de
um módulo, perde-se o contexto explícito de onde cada função veio. Se no
mesmo programa existir outra função ou variável com o mesmo nome,
podem surgir conflitos. Por isso, o uso de from ... import ... deve
ser criterioso, reservado para casos em que as funções importadas são
realmente usadas muitas vezes e não há risco de ambiguidade.
Veja no exemplo a seguir, que importa duas funções de dois módulos
distintos.
30
Há uma variante comum desta importação que tem a seguinte sintaxe:
from ... import *
Nesse caso, todas as funções e variáveis do módulo passam a estar
disponíveis diretamente no programa, podendo ser usadas apenas pelo nome,
sem o prefixo do módulo. Apesar disso, esta prática deve ser evitada, porque
mistura os nomes do módulo com os já existentes, aumenta o risco de
conflitos e pode originar erros difíceis de identificar e corrigir.
Veja o exemplo deste tipo de importação não recomendada em códigos.
31
O uso de codinomes facilita a escrita e torna o código mais limpo, sem
comprometer a legibilidade. Por exemplo, é muito comum encontrar o
módulo pandas importado como pd ou o [Link] como
plt. Esses apelidos já são reconhecidos e compreendidos pela maior parte
dos programadores Python, funcionando como uma espécie de linguagem
comum dentro da comunidade. Assim, o código mantém-se claro, compacto
e alinhado com as boas práticas mais difundidas.
Neste exemplo a seguir, o módulo pandas foi importado como pd,
seguindo a convenção adotada em praticamente todos os tutoriais e projetos.
Já a função sqrt recebeu o codinome raiz, tornando o código mais
descritivo e intuitivo.
O Python já traz uma biblioteca padrão bastante extensa, que cobre tarefas
comuns como cálculos matemáticos, geração de números aleatórios,
manipulação de datas e interação com o sistema operativo. Para além disso,
existem também bibliotecas externas muito populares, criadas pela
32
comunidade, que expandem enormemente as capacidades da linguagem em
áreas como ciência de dados, estatística, inteligência artificial e visualização
gráfica.
A linguagem Python disponibiliza uma biblioteca padrão muito extensa, cuja
documentação oficial se encontra no site [Link]. Ali estão descritos
todos os módulos que acompanham a instalação da linguagem, incluindo
exemplos de utilização e explicações detalhadas. Para uma consulta rápida
de todos os nomes disponíveis existe ainda o Python Module Index, que
organiza em ordem alfabética todos os módulos integrados. Algumas destas
bibliotecas estão apresentadas na Tabela 1.
Tabela 1 - Bibliotecas Padrão do Python
Biblioteca Finalidade
math Funções matemáticas
random Geração de números aleatórios
datetime Manipulação de datas e horas
os Acesso ao sistema operativo e ficheiros
sys Informação e interação com o interpretador
33
Tabela 2 - Bibliotecas Externas Populares do Python
Biblioteca Finalidade
numpy Operações numéricas e matrizes
pandas Análise e manipulação de dados em tabelas
matplotlib Criação de gráficos e visualizações
scikit-learn Algoritmos de aprendizagem automática (IA)
tensorflow Redes neuronais e deep learning
34
DICA: Concatenar significa juntar textos (strings). Pode-se usar o
operador + para unir duas ou mais strings, lembrando de incluir espaços
ou outros separadores manualmente, quando necessário. Por exemplo:
“a”+ “ ” +”b” é “a b”
35
3 OPERADORES E EXPRESSÕES
36
O seguinte exemplo demonstra a utilização desses operadores em código:
37
3.1.1 Precedência de Operadores
38
O uso de parênteses pode alterar explicitamente a ordem de avaliação,
proporcionando clareza e controle sobre o cálculo.
39
Os operadores de atribuição composta tornam o código mais curto, claro e
legível, evitando repetição desnecessária.
40
Este conceito permite que uma expressão do tipo a < b < c seja avaliada
pelo Python como (a < b) and (b < c). A expressão só retornará
True se todas as comparações individuais forem verdadeiras.
A seguir é apresentado um exemplo para verificar se um número está em um
intervalo e exibir a resposta.
41
Dentre as vantagens das comparações encadeadas, exemplificadas
anteriormente, pode-se citar:
− Mais legível: A expressão se assemelha à notação matemática
convencional
− Mais concisa: Evita a repetição de variáveis e operadores lógicos
− Mais elegante: O código fica mais limpo e fácil de entender
− Mais eficiente: Em alguns casos, o Python pode otimizar a avaliação.
Mas tenha cuidado, pois embora seja poderoso, o uso deste recurso deve ser
de maneira controlada. Cadeias muito longas podem dificultar a leitura,
como no exemplo a seguir, que apesar de correcto, está confuso.
42
3.3 Operadores Lógicos
43
Diz-se que Python é "preguiçoso" (lazy) na avaliação, pois apenas avalia o
mínimo necessário para obter o resultado final. A principal vantagem é
eficiência e economia de processamento, pois evita cálculos desnecessários.
Para além, a prevenção de erros, caso o segundo operando envolva uma
operação inválida ou pesada, conforme no exemplo apresentado a seguir, que
ocasionaria um erro caso não fosse esta ideia.
44
3.4.1 Diferença entre os operadores == e is
45
3.5 Operadores de Associação
3.6 Exercícios
46
2) Dadas as variáveis a = 5, b = 10 e c = 15, faça um programa
para verificar se:
a) a < b < c
b) a + b == c
c) c % a != 0
47
4 ENTRADA E SAÍDA DE DADOS
49
b) Conversão para decimal:
50
Se o utilizador escrever S, a variável continuar armazena o valor True.
Em qualquer outro caso, armazena o valor False.
51
4.4 Personalização da saída com print()
52
4.5 Técnicas de formatação de strings
a) Com vírgulas
A forma mais simples de combinar múltiplos valores numa só mensagem é
separá-los por vírgulas na função print(). O Python trata
automaticamente da conversão para string e insere um espaço entre cada
elemento. A vantagem é não ser necessário converter outros tipos de dados
para string manualmente, como no exemplo seguinte que usa a vírgula.
b) Com o operador +
O operador de concatenação + une strings de forma direta. No entanto, requer
mais atenção:
53
− Todos os elementos devem ser strings. Números e outros tipos precisam
de ser, obrigatoriamente, convertidos com str()
− Os espaços entre palavras devem ser incluídos explicitamente nas strings
A grande desvantagem é que o código é mais verboso e propenso a erro se
alguma conversão for esquecida.
54
No exemplo seguinte utilizam-se índices para indicar a posição dos valores.
Esta forma de exibição torna o código mais flexível, permitindo inclusive
reutilizar os mesmos valores em diferentes partes da frase.
55
Dentre as vantagens, cita-se:
− O código torna-se muito mais legível, pois os valores são inseridos
diretamente no local onde serão exibidos
− Permitem executar operações e funções diretamente dentro das chavetas
{}, sem necessidade de cálculos prévios
− São mais eficientes em termos de velocidade de execução
comparativamente a outros métodos de formatação.
56
A expressão :.2f dentro das chavetas significa:
− O símbolo : inicia a formatação
− O símbolo .2 define o número de casas decimais
− A letra f indica que é um número de vírgula flutuante (float).
b) Formatação de Percentagens
É também comum mostrar um valor decimal como uma percentagem. As f-
strings tornam isto muito simples. A seguir há um exemplo pertinente.
c) Alinhamento de Texto
É possível controlar a posição do texto dentro de um espaço definido, ou seja,
controlar o alinhamento do texto ao formatar strings. Para alinhar o conteúdo
à direita, à esquerda, ao centro ou definindo ainda o número total de espaços
a ocupar, conforme o exemplo a seguir.
57
d) Preencher com Caracteres
Preenche o espaço extra com um carácter específico e, neste exemplo,
também é possível definir este preenchimento.
Veja este exemplo para ilustrar esta forma de exibir.
58
e) Formatação de Números Inteiros
Na formatação de números inteiros, é possível adicionar separadores de
milhares para facilitar a leitura. O Python permite escolher diferentes
símbolos, como a vírgula ou o sublinhado. Veja o exemplo a seguir.
f) Formatação de Datas
Na formatação de datas, utilizam-se diretivas específicas que permitem
exibir o dia, o mês, o ano ou a hora em diferentes formatos. A seguir é
apresentado um exemplo.
g) Sistemas Numéricos
O Python permite exibir números em diferentes sistemas de numeração,
como hexadecimal, octal e binário, através de especificadores de formato,
conforme o exemplo.
59
h) Combinar Várias Formatações
É possível combinar diferentes opções de formatação num mesmo valor.
Assim, é possível definir separadores de milhares, o número de casas
decimais e o tipo de dado, tornando a apresentação mais clara e profissional,
como este exemplo.
60
i) Formatação de Números Inteiros
O especificador d indica que o valor deve ser tratado como inteiro decimal.
É útil para controlar o tamanho mínimo do campo, preenchendo com zeros
ou espaços à esquerda.
Neste exemplo a seguir, dentro do print() se apresentam as seguintes
formatações:
− 05d → reserva 5 posições, completa com zeros e mostra como
inteiro decimal
− 8d → reserva 8 posições, completa com espaços e mostra como
inteiro decimal.
61
k) Formatação de Strings
O especificador s garante que o valor seja tratado como texto. É
especialmente útil quando se precisa alinhar ou limitar o tamanho da string.
Exemplo da formatação de strings com os seguintes códigos no print():
− .3s → limita a string a 3 caracteres
− 10s → reserva 10 posições para a string, completando o espaço com
espaços em branco.
62
3. Simular um recibo simples. Ler o nome de um produto, preço unitário e
quantidade. Calcular e mostrar o total a pagar com IVA (23%),
formatado como moeda. Exemplo
Produto: Livro
Total: 12.50 €
IVA (23%): 2.88 €
Total com IVA: 15.38 €
63
5 ESTRUTURAS DE CONTROLO
64
programa. Isto não é apenas uma mera convenção de estilo, mas sim uma
regra sintática obrigatória.
A indentação refere-se aos espaços em branco no início de uma linha de
código. Em Python, ela define a hierarquia e o agrupamento lógico das
instruções. As regras básicas são:
− Blocos são definidos por indentação: Todas as linhas de um mesmo
bloco de código devem ter exatamente o mesmo nível de indentação
− O padrão recomendado é 4 espaços: Embora se possa usar tabs ou outro
número de espaços, a PEP 8 (guia de estilo oficial do Python),
recomenda 4 espaços por nível de indentação
− Misturar tabs e espaços não é permitido: Escolha um método e use-o
consistentemente em todo o projeto
− A mudança de indentação marca o início/fim de blocos: Um aumento na
indentação inicia um novo bloco e uma diminuição encerra o bloco atual.
65
5.1.2 Estrutura if
66
5.1.3 Estrutura if-else
67
5.1.4 Estrutura if-elif-else
68
5.1.5 Operador Ternário
69
Considere o exemplo seguinte, que verifica se um número é maior ou igual
a zero e, nesse caso, o considera como positivo.
70
5.1.6 Condições múltiplas com operadores lógicos
71
5.2 Ciclos (for, while)
72
A seguir há exemplos pertinentes ao comando for. Não se preocupe ainda
com o conceito de lista e dicionário, apenas tente perceber o funcionamento
do comando for.
73
Para melhor perceber o funcionamento há um exemplo prático para cada
sintaxe, logo a seguir.
74
Neste exemplo o ciclo repete a frase � Motor ligado! cinco vezes,
mas o valor do contador não é usado, por isso é substituído por _.
A função [Link](1) faz o programa pausar durante 1 segundo antes
de continuar para a próxima iteração, criando o efeito de espera realista até
o momento da descolagem.
75
5.2.4 Controlo de ciclos: break e continue
76
Exemplo 2: Já o comando continue faz o oposto, ou seja, não termina o
ciclo, apenas salta a iteração atual e segue para a seguinte. Neste exemplo,
quando i vale 2, o print(i) é ignorado e o ciclo continua normalmente.
77
5.3 Estruturas de Controlo Aninhadas
78
A saída do programa segue a formatação mostrada abaixo, mas apenas a
tabuada do 1 é apresentada como exemplo. As restantes tabuadas são
exibidas em sequência, mas foram omitidas para simplificar a amostra.
É bastante comum combinar condições com ciclos para que o programa tome
decisões a cada iteração. Essa prática permite filtrar valores, aplicar regras
específicas e resolver problemas mais elaborados.
79
Um exemplo clássico é a identificação de múltiplos comuns. Dentro de um
mesmo intervalo, pode-se verificar quais números satisfazem duas condições
ao mesmo tempo.
No exemplo a seguir, o programa percorre os números de 1 a 50 e seleciona
apenas aqueles que são divisíveis tanto por 3 como por 5, exibindo-os como
resultado.
80
Uma possível saída (a considerar que o utilizador irá digitar s) está a ser
apresentada a seguir.
81
Uma versão melhorada deste código, a garantir que se aceite apenas s/n é
apresentada a seguir.
82
permite verificar se uma string contém apenas dígitos numéricos, portanto
sendo útil para validar se o PIN foi inserido corretamente.
Após três erros, o cartão é bloqueado e o acesso é negado.
No exemplo anterior foi utilizado o método isdigit() para garantir que o PIN
continha apenas dígitos numéricos. Esse é apenas um entre vários métodos
disponíveis para validar e manipular strings em Python. Para facilitar a
consulta, apresenta-se a seguir a Tabela 10, que reúne alguns dos métodos
mais utilizados no dia a dia para esse tipo de verificação.
83
Tabela 10 - Métodos úteis de validação em strings
84
Em resumo: as estruturas de controlo podem ser usadas isoladamente ou
combinadas e é essa flexibilidade que dá ao programador o poder de resolver
problemas de diferentes níveis de complexidade.
5.7.1 [Link]()
5.7.2 [Link](a, b)
85
5.7.3 [Link](a, b)
5.7.4 [Link](sequencia)
5.7.5 [Link](lista)
86
5.7.6 Exemplos de aplicações práticas
87
módulo random para gerar resultados diferentes em cada execução. O dado
é lançado cinco vezes e o ciclo serve apenas para repetir a ação. Cada
lançamento gera um número aleatório entre 1 e 6, como num jogo de dados.
88
5.7.7 Controlar a aleatoriedade com sementes (seeds)
89
Já no exemplo a seguir é feita uma comparação da geração aleatória de
números com e sem semente.
90
O seu resultado com seed pode ser diferente do apresentado neste exemplo,
pois a versão do Python ou a implementação interna do gerador de números
aleatórios pode variar. Isso é normal e esperado, pois diferentes versões do
Python podem produzir sequências diferentes com a mesma seed.
91
O uso é amplo e engloba jogos e entretenimento, simulações não críticas,
amostragem estatística básica, testes e desenvolvimento e aplicações
educacionais. Não é adequado utilizar para aplicações de criptografia e
segurança, sistemas de autenticação, geração de chaves seguras, jogos de
azar online com apostas reais e aplicações financeiras críticas
Para estes cenários mais sensíveis que requerem verdadeira aleatoriedade
criptográfica, Python oferece módulos especializados, como o secrets
(Python 3.6+) e o módulo [Link]()
Por fim, considere que a geração de números pseudoaleatórios é geralmente
eficiente, mas em contextos específicos pode afetar o desempenho do
programa.
92
A sintaxe básica do try-except, que evitará tais problemas, está
apresentada a seguir.
Este exemplo a seguir mostra como usar um ciclo while juntamente com
try e except para garantir que a idade introduzida é válida. O programa
pede ao utilizador a idade e tenta converter a resposta para um número
inteiro. Se o valor não for numérico, o except captura o erro e o pedido
repete-se. Além disso, há uma verificação extra que não permite idades
negativas: sempre que o utilizador introduz um número menor que zero, é
exibida uma mensagem de erro e o pedido é repetido até que um valor correto
seja introduzido. Quando finalmente se obtém uma idade válida, o programa
calcula, exibe quantos anos a pessoa terá dentro de dez anos e o ciclo termina.
93
5.8.1 Tipos comuns de erros
94
O else é executado apenas se nenhum erro ocorrer no bloco try, de
maneira a funcionar como um caminho seguro após a tentativa.
Já o finally é executado sempre, independentemente de ter ocorrido erro
ou não, sendo útil para ações que não podem ser ignoradas, como fechar um
ficheiro ou libertar recursos.
O exemplo a seguir mostra a utilização conjunta dessas estruturas.
Tratar erros em Python não é apenas uma questão de evitar falhas: é também
uma forma de tornar o programa mais robusto, profissional e agradável de
usar. Para isso, algumas boas práticas fazem toda a diferença.
Antes de mais, seja específico. Evite usar um except genérico, que captura
tudo, sem distinção do problema. Capture apenas os erros que realmente
espera, como ValueError ou ZeroDivisionError. Isso facilita a
manutenção do código e evita surpresas.
Além disso, dê sempre feedback útil para o utilizador. Se o utilizador
introduzir um valor inválido, não basta dizer que houve erro. Indique o que
95
aconteceu e, se possível, como corrigir. Isso transforma uma falha em
oportunidade de aprendizagem.
Outro ponto importante é manter o bloco try pequeno. Coloque dentro deste
bloco apenas o que pode gerar erro. Isso torna a lógica mais clara e evita
apanhar exceções de trechos que não deveriam estar ali.
E, claro, use múltiplos except quando precisar tratar situações diferentes
de forma distinta. Dividir o tratamento torna o programa mais organizado e
fácil de compreender.
Por fim, se seguires estas práticas faz com que os programas sejam:
− Mais robustos, porque não “crasham” com entradas inesperadas
− Mais amigáveis, porque oferecem mensagens claras
− Mais profissionais, porque lidam graciosamente com exceções
− Mais seguros, porque evitam comportamentos indesejados.
96
− Usar um ciclo while para continuar a solicitar palpites até acertar
− Usar if-elif-else para comparar o palpite com o número
secreto
− Usar try-except para evitar erros se o utilizador não digitar um
número
97
5) Criar uma contagem regressiva para um lançamento que começa em 3
minutos e exibir no ecrã tempo no formato "T-2:59", "T-2:58",
..., "T-0:01"
Incluir uma mensagem especial a cada minuto e terminar com
"LANÇAMENTO!"
Desafio adicional: Fazer uma pausa de 1 segundo entre cada segundo,
para tal usar import time e [Link](1).
98
7) Escreva um programa que leia dois números inteiros e calcule a divisão
do primeiro pelo segundo. O programa deve garantir que apenas valores
numéricos inteiros sejam aceites como entrada. Para além, lembre-se que
não é permitido dividir por zero.
O resultado da divisão deve ser exibido apenas quando ambos os valores
forem válidos.
Usar estruturas de tratamento de exceções (try-except) para capturar
e tratar os erros adequados, mostrando mensagens claras ao utilizador.
100
9) Criar um programa interativo que apresente um menu com diferentes
opções de geração de padrões numéricos. O utilizador deve poder
escolher entre:
− Gerar um triângulo numérico, em que cada linha mostra números em
ordem crescente até atingir a altura definida pelo utilizador
− Construir uma tabuada personalizada de um número à escolha, até
um limite definido pelo utilizador
− Mostrar a sequência de Fibonacci, exibindo um número de termos
especificado
− Listar todos os números primos até um limite fornecido pelo
utilizador.
O programa deve validar as entradas, tratando erros de digitação e
valores inválidos, além de apresentar mensagens claras ao utilizador em
cada situação.
Dicas:
− Use ciclos aninhados (for dentro de for) para construir o triângulo
numérico
− Para a tabuada, utilize um ciclo for simples e a operação de
multiplicação
− Na sequência de Fibonacci, lembre-se de atualizar dois valores a
cada iteração (a e b)
− Para verificar se um número é primo, teste a divisão apenas até à raiz
quadrada desse número
− Utilize condicionais (if/elif/else) para controlar a escolha do
menu.
101
− Acrescente tratamento de exceções (try/except) para lidar com
entradas inválidas e interrupções do utilizador.
Exemplo de execução:
102
6 FUNÇÕES EM PYTHON
104
6.2 Âmbito de variáveis
105
a variável global permanece inalterada, mesmo que a local seja modificada,
conforme se percebe pelo exemplo a seguir.
106
Dentro de funções, qualquer atribuição a um nome em qualquer ponto faz
com que esse nome seja tratado como local em toda a função, devido à regra
LEGB (Local, Enclosing, Global, Built-in). Se tentares ler esse nome antes
de lhe atribuires um valor, ocorrerá um UnboundLocalError.
107
Embora a palavra-chave global resolva a questão, deve ser usada com
cautela. Alterar variáveis globais dentro de funções pode tornar o código
mais confuso, difícil de manter e menos modular.
A boa prática é passar valores como parâmetros e devolver resultados com
return, em vez de depender de alterações globais.
108
Entretanto, se o código necessitar usar nonlocal com frequência, o
programador deve optar por usar classes, na orientação objeto, pois estas
oferecem uma maneira mais organizada de gerenciar estado compartilhado.
109
Observe as saídas comentadas, nas chamadas das funções, para que percebas
e distinga claramente entre ler, sombrear e modificar valores em cada nível
de âmbito.
110
Após executar este programa ilustrativo sobre âmbito de variáveis, obtém-se
a seguinte saída.
Estas são as funções mais básicas, que não recebem nenhum dado de entrada
(parâmetros) e não devolvem nenhum valor como resultado. São
normalmente utilizadas para executar ações específicas que não dependem
de dados externos e não precisam fornecer resultados para outras partes do
código, conforme apresentado no exemplo a seguir.
111
As suas principais características são:
− Não possuem parâmetros (parênteses vazios)
− Não utilizam a instrução return, saem da função após o fim de sua
execução, entretanto podem utilizar um return sem valor
− Executam uma ação, como exibir informações no ecrã
− Úteis para agrupar código que será reutilizado várias vezes
112
Dentre as suas principais características destacam-se:
− Recebem um ou mais parâmetros. Não necessariamente do mesmo tipo
− Processam os dados recebidos
− Não utilizam return ou podem utilizar um return sem valor
− O valor retornado é sempre None (implícito).
Estas são funções completas que tanto recebem dados de entrada quanto
devolvem um resultado processado. São extremamente comuns e úteis para
agrupar cálculos ou transformações de dados.
A função pode ter um ou vários parâmetros e, para perceber o conceito,
considere o exemplo da soma, onde a função somar(a, b) recebe dois
valores, calcula o resultado e devolve-o com return:
113
Os parâmetros passados para uma função não precisam ser todos do mesmo
tipo, nem é obrigatório haver apenas um valor de retorno possível. No
exemplo a seguir, a função recebe três parâmetros distintos (uma string, um
número inteiro e um valor booleano). A depender do valor lógico do
parâmetro booleano, o return devolve uma resposta diferente.
É importante destacar que, no momento em que o return é executado, a
função termina imediatamente: o valor é devolvido e nenhuma instrução
abaixo dessa linha é executada.
114
Suas principais características são:
− Recebem parâmetros como entrada
− Processam esses parâmetros
− Utilizam return para devolver um resultado
− Podem ser usadas em expressões ou atribuições
Python permite que uma função devolva vários valores de uma só vez.
Quando isso acontece, os valores são entregues juntos como um único
pacote, que pode ser recebido em várias variáveis ao mesmo tempo na
chamada da função.
Este tipo de função tem importância especial porque quando uma função
executa a instrução return, sua execução termina imediatamente. Se
houvesse múltiplas instruções return consecutivas, apenas a primeira seria
executada, desta forma, para devolver vários valores, deve-se fazê-lo em um
único return.
Neste exemplo a seguir, a função operacoes_matematicas(a, b)
calcula quatro resultados (soma, diferença, produto e
quociente) e devolve-os de uma só vez sob a forma de tuplo (este
conceito será apresentado no capítulo 7). Na chamada, é usado
desempacotamento múltiplo, soma, diff, prod, div =
operacoes_matematicas(...), para distribuir cada elemento do
tuplo por variáveis distintas, na mesma ordem do return.
115
Entre as características das funções com múltiplos retornos, cita-se:
− Devolvem vários valores numa única operação de retorno
− Podem ser recebidos em múltiplas variáveis simultaneamente
− Úteis quando uma função precisa fornecer vários resultados relacionados
− Simplificam o código evitando múltiplas chamadas para obter dados
conectados.
116
A regra para definir funções com estes tipos de argumentos é simples:
primeiro devem vir os parâmetros obrigatórios, seguidos pelos opcionais.
Esta ordem é essencial para que o Python possa interpretar corretamente os
argumentos fornecidos.
Como nota importante, os valores padrão são avaliados no momento da
definição da função. Por este motivo, deve evitar-se usar objetos mutáveis
(como listas ou dicionários, a serem vistos no capítulo 7) como valores
padrão, pois isso pode levar a comportamentos inesperados.
117
segundo argumento, a saudação é personalizada. As chamadas ilustram o uso
do valor por defeito e a substituição posicional do parâmetro opcional. A
função não devolve valor, limita-se a imprimir a mensagem formatada com
f-strings.
Quando uma função possui múltiplos parâmetros opcionais, estes devem ser
definidos após os parâmetros obrigatórios. Esta ordem é importante porque
o Python atribui os argumentos por posição.
Neste exemplo a seguir, a função criar_perfil tem dois parâmetros
obrigatórios (nome e idade) e dois opcionais com valores por defeito
(cidade="Lisboa" e pais="Portugal"). A função imprime os
dados do perfil e uma linha separadora.
Na primeira chamada, passam-se apenas nome e idade, sendo então usados
os valores padrão para cidade e pais.
Na segunda chamada, substitui-se apenas a cidade (o país permanece
“Portugal”).
Na terceira chamada, substituem-se cidade e país.
118
Se quiseres, também podes escrever as substituições com argumentos
nomeados (independentemente da ordem), como por exemplo, colocar como
parâmetros da função criar_perfil:
("Maria", 28, pais="Espanha", cidade="Madrid")
119
6.4.3 Argumentos nomeados (keyword arguments)
120
Após a execução deste programa, obtém-se a seguinte saída:
121
filtragem ou transformação de coleções. O exemplo simples apresentado a
seguir introduz essa utilização, que será explorada em maior profundidade
no Capítulo 7, onde se apresentam listas, tuplos, conjuntos e dicionários.
122
Em scripts mais simples, basta chamar main() dentro do guardião. No
entanto, para aplicações mais sólidas ou para uso em automação, é
recomendável devolver um código de saída explícito ao sistema operativo.
Consegue-se isso com [Link](main()), onde a função main()
retorna 0 em caso de sucesso e um valor diferente de zero para indicar que
ocorreu um erro.
A seguir apresenta-se uma versão genérica como um template ou "sintaxe"
padrão da main() com guardião if __name__ == "__main__".
123
Nesta estrutura genérica salienta-se os seguintes pontos:
− Importações → Todos os imports no início do ficheiro
− Definições → Funções, classes e constantes reutilizáveis
− Função main() → Contém a lógica principal dentro de um bloco try-
except
− Ponto de entrada → O guardião que controla a execução
124
− [Link]() → Garante o retorno do código de saída adequado
125
Esta organização permite que o script funcione tanto como programa
independente quanto como módulo importável, mantendo o código limpo e
e de fácil entendimento.
126
Quando o ficheiro é executado diretamente (por exemplo, com python
meu_programa.py), o interpretador define __name__ como
"__main__" e o bloco protegido pelo guardião é executado: arranca-se a
função main() e corre o fluxo do programa.
Um exemplo mínimo é o seguinte:
127
6.6.2 Erros comuns e como os evitar.
128
Outro deslize típico é esquecer o próprio guardião e chamar main()
diretamente; nesse caso, a função será executada tanto na execução direta
como na importação.
129
Finalmente, importa manter apenas as importações necessárias no topo do
ficheiro, reduzindo tempo de arranque e dependências desnecessárias
Para além, é possível chamar apenas main() sem [Link] para o caso
de scripts introdutórios. Em programas usados por outras ferramentas ou
pipelines, preferir [Link](main()) para devolver um código de
saída explícito ao sistema.
130
6.7 Exemplos práticos
131
6.7.1 Exemplo 1: Sistema de Pontuação para Jogo
A função
calcular_pontuacao(moedas, inimigos_derrotados,
tempo_restante) soma a pontuação ponderada (moedas×10 +
inimigos×100 + tempo×5) e devolve o total.
A função
definir_nivel(pontuacao) classifica o jogador com base em
limiares fixos: Mestre (≥1000), Avançado (≥500),
Intermediário (≥200) ou Iniciante (abaixo de 200).
A função
exibir_resultado(nome_jogador, pontuacao, nivel)
trata apenas da apresentação formatada (nome, pontuação e nível).
132
Após a execução deste código, a seguinte saída é exibida no ecrã.
133
6.7.2 Exemplo 2: Analisador de Estatísticas de Streamer
134
informado no relatório, mas não influencia a classificação, permitindo
futuras extensões onde métricas financeiras possam ser ponderadas.
135
6.7.3 Exemplo 3: Sistema de Conquistas para App Fitness
136
consecutivos) e exibe a conquista devolvida pela primeira função,
produzindo assim um relatório claro e completo para o utilizador.
137
6.7.4 Exemplo 4: Mistura de Poções num Jogo de Fantasia
138
Um exemplo de relatório de saída é apresentado a seguir.
139
6.8 Exercícios propostos
140
− Contém pelo menos uma letra minúscula
− Contém pelo menos um número
A função deve retornar True se a palavra-passe for válida e False caso
contrário.
Este é um exemplo de uso:
141
4) Criar uma função gerar_relatorio_produto(nome, preco,
quantidade) que devolve um relatório formatado com as
informações de um produto. O relatório deve incluir:
− Nome do produto
− Preço unitário (formatado com duas casas decimais e símbolo €)
− Quantidade em estoque
− Valor total do estoque (preço × quantidade)
O programa principal deve:
− Repetidamente pedir ao utilizador o nome, preço e quantidade de
produtos
− Terminar a entrada quando o utilizador digitar "sair"
− Mostrar o relatório individual de cada produto logo após o cadastro
− Manter um acumulador com o valor total de todos os produtos
− No fim, exibir um relatório geral com a soma acumulada.
Dica: Para terminar o programa, comparar o nome do produto digitado com
"sair" usando .lower() para aceitar maiúsculas e minúsculas.
Exemplo de uso:
142
5) Implementar uma calculadora simples com funções em Python. O
programa deve conter quatro funções principais: somar, subtrair,
multiplicar e dividir. Cada função deve receber dois parâmetros e
devolver o resultado da operação correspondente.
No caso da divisão, deve-se usar tratamento de exceções (try /
except) para evitar o erro de divisão por zero.
O programa deve pedir ao utilizador dois números inteiros através do
teclado e, em seguida, exibir os resultados das quatro operações.
O código deve ser organizado, com mensagens claras para o utilizador,
conforme sugestão a seguir.
143
6) Implementar um jogo de adivinhação numérica usando funções em
Python. O programa deve permitir ao jogador adivinhar um número
secreto gerado aleatoriamente entre 1 e 100. O jogo deve ser
implementado usando as seguintes funções:
gerar_numero_secreto()
− Gera e retorna um número inteiro aleatório entre 1 e 100
obter_palpite()
− Solicita e valida a entrada do usuário
− Garante que o palpite seja um número inteiro entre 1 e 100
− Retorna o palpite validado
dar_dica(palpite, numero_secreto)
− Recebe o palpite do jogador e o número secreto
− Retorna uma string a indicar se o número é mais alto ou mais baixo
− Exemplo: "Tente um número MAIOR!" ou "Tente um número
MENOR!"
jogar_adivinhacao()
− Função principal que orquestra o jogo
− Controla o fluxo do jogo e o número de tentativas
− Exibe mensagens de vitória ou derrota
144
Regras do Jogo:
− O jogador tem no máximo 10 tentativas
− A cada palpite errado, o jogo fornece uma dica
− O jogo termina quando o jogador acerta ou esgota todas as tentativas
Dica: Implementar tratamento de erros para entradas inválidas
145
7 ESTRUTURAS DE DADOS EM PYTHON
7.1 Listas
146
É a estrutura mais versátil do Python, pois permite armazenar diferentes tipos
de dados e modificar os seus elementos, ou seja, as listas são mutáveis.
147
7.1.2 Alterar e adicionar elementos a uma lista
Método Descrição
append(x) Adiciona um elemento ao final da lista
insert(i, x) Insere o elemento x na posição i
remove(x) Remove a primeira ocorrência do elemento x
pop(i) Remove e devolve o elemento da posição i
clear() Remove todos os elementos (lista vazia)
sort() Ordena os elementos em ordem crescente
reverse() Inverte a ordem dos elementos
count(x) Conta quantas vezes x aparece na lista
index(x) Devolve o índice da primeira ocorrência de x
len(lista) (função) Devolve o tamanho da lista
149
Por ser uma estrutura flexível, uma lista em Python pode armazenar
elementos de diferentes naturezas. É comum haver listas apenas com strings,
apenas com números ou apenas com valores lógicos. No entanto, nada
impede que uma mesma lista misture vários tipos de dados, como no
exemplo a seguir:
150
O exemplo a seguir apresenta a iteração numa lista de strings e exibe os
elementos da lista em linhas distintas.
151
c) Imprimir todos os elementos sem os parênteses retos
Para mostrar a lista completa, mas sem parênteses retos e sem vírgulas, é
possível usar variações de iteração. No exemplo anterior é apresentada a
maneira de exibir através de um for iterável.
Mas é possível usar o operador * (expansão da lista), que mostra os
elementos da lista separados por espaços.
Há ainda o método join(), que pode ser utilizado apenas com listas de
strings. Este método permite juntar todos os elementos numa única linha,
usando como separador qualquer texto que o programador desejar. Esse
separador pode ser uma vírgula, um hífen, um espaço em branco ou até
152
mesmo ícones e emojis. Mas observe no exemplo que há espaço após a
vírgula, bem como antes e depois do hífen e dos emogis.
Em resumo:
− for → cada elemento numa linha
− *lista → todos elementos separados por espaço
− join() → todos elementos unidos por um separador à escolha.
153
− Ciclo for: Para percorrer o iterável
− Condição if (opcional): Para filtrar elementos.
154
As list comprehensions são uma forma poderosa e concisa de criar listas em
Python. Podem ir desde a forma mais básica até construções mais complexas
com filtros, condições, múltiplos ciclos e até listas aninhadas. Entretanto
senguem sempre o mesmo padrão:
[expressão for item in iterável ...]
A seguir, apresentam-se os principais tipos organizados em blocos, cada um
mostrando a sintaxe, um exemplo prático e o respetivo resultado. Por fim,
apresentam-se alguns exemplos de variações consideradas úteis.
a) Básica
− Sintaxe: [expr for item in iterable]
− Exemplo: [x*2 for x in range(3)]
− Resultado: [0, 2, 4]
155
b) Com filtro
− Sintaxe: [expr for item in iterable if cond]
− Exemplo: [x for x in range(5) if x%2==0]
− Resultado: [0, 2, 4]
c) Com if-else
− Sintaxe: [A if cond else B for item in iterable]
− Exemplo: ["Par" if x%2==0 else "Ímpar" for x in
range(3)]
− Resultado: ['Par', 'Ímpar', 'Par']
d) Múltiplos for
− Sintaxe: [expr for a in A for b in B]
− Exemplo: [(x,y) for x in [1,2] for y in [3,4]]
− Resultado: [(1,3), (1,4), (2,3), (2,4)]
e) Aninhada
− Sintaxe: [[exp for i in iter] for i in iter]
− Exemplo: [[x+y for x in [1,2]] for y in [3,4]]
− Resultado: [[4, 5], [5, 6]]
f) Outras variações úteis
i - Com função aplicada
o nomes = ["luís", "bruno", "david"]
o maiusculos = [[Link]() for n in nomes]
o # ['LUÍS', 'BRUNO', 'DAVID']
ii - Com transformação numérica
o cubos = [x**3 for x in range(5)]
o # [0, 1, 8, 27, 64]
156
iii - Com múltiplas condições
o par = [x for x in range(20)
if x % 2 == 0 if x > 10]
o # [12, 14, 16, 18]
iv - Achatamento de listas ou flatten
o listas = [[1,2], [3,4], [5,6]]
o flatten = [x for s in listas for x in s]
o # [1, 2, 3, 4, 5, 6]
Uma lista em Python pode conter outras listas no seu interior. Quando isso
acontece, forma-se uma estrutura multidimensional, utilizada para
representar matrizes, tabelas, grelhas de dados, tabuleiros de jogos etc.
Tal como acontece em qualquer lista em Python, a contagem dos índices
inicia em 0. Isso aplica-se tanto à lista principal como a cada sub-lista que
esta contém.
No exemplo seguinte, a lista principal recebe o nome matriz. O acesso a
um elemento específico faz-se indicando dois índices em sequência: primeiro
o da linha, depois o da coluna.
Também é possível percorrer todos os elementos com ciclos for aninhados,
em que o primeiro ciclo percorre as linhas e o segundo percorre os elementos
dentro de cada linha.
157
7.2 Tuplos
Tal como as listas, também é possível criar um tuplo vazio, ou seja, não tem
elementos, mas continua a ser um objeto do tipo tuple, cuja sintaxe é:
nome_tuplo = ()
158
Entretanto, antes de apresentar um exemplo, tenha em mente que um tuplo
funciona como uma lista imutável:
− Os elementos são ordenados
− O acesso faz-se por índices numéricos
− A contagem dos índices inicia em 0
− Tal como nas listas, também é possível usar índices negativos. O índice
-1 corresponde ao último elemento, -2 ao penúltimo, e assim por diante.
Um tuplo vazio não serve para armazenar valores, já que não pode ser
alterado depois de criado. No entanto, pode ser útil em contextos específicos,
como quando uma função precisa devolver um tuplo vazio para indicar que
não encontrou resultados. Veja este exemplo a seguir.
159
Um detalhe importante surge quando se cria um tuplo com apenas um único
elemento. Nesse caso, é necessário incluir uma vírgula após o valor, caso
contrário o Python interpreta apenas como o próprio valor entre parênteses,
e não como um tuplo, como no exemplo a seguir.
um_elemento = (elemento,) # atenção à vírgula
O tuplo unitário não é comum no dia a dia, mas existe para manter
consistência em situações em que se espera sempre um tuplo, como no
retorno de funções.
160
permanecem estáveis depois de criados. Essa estabilidade é essencial em
contextos onde a alteração dos valores não faz sentido, como no registo de
coordenadas geográficas, datas, entradas fixas ou em identificadores
compostos que servem como chave em dicionários. Nessas situações, o tuplo
atua como uma estrutura segura, sem modificações acidentais e a preservar
a consistência da informação em todo o programa. Dentre as principais
vantagens, cita-se:
− Integridade de dados garantida → um tuplo não é alterado
acidentalmente, sendo crucial para representar informações que devem
ser constantes durante a execução do programa
− Segurança como chaves em dicionários → a imutabilidade permite que
os tuplos sejam usados como chaves em dicionários, ao contrário das
listas. Isso é extremamente útil para criar mapeamentos complexos
− Retorno de múltiplos valores de funções → é uma prática comum usar
tuplos para que uma função retorne vários valores de uma vez.
Entre os exemplos práticos do uso, citam-se:
− Representar entidades imutáveis:
⋅ Coordenadas em um espaço 2D ou 3D: ponto = (x, y, z)
⋅ Data específica: data_nascimento = (26, 12, 1924)
⋅ Localização por GPS: gps = (-21.5356, -43.1986)
− Chaves compostas em dicionários (mais detalhes no tópico 7.3):
161
Pode-se também retornar múltiplos valores. Entretanto, a necessidade de
aceder os valores por índices (resultado[0], resultado[1]), como
neste próximo exemplo, ilustra uma limitação prática desta abordagem, pois
o programador precisa lembrar a ordem dos elementos no tuplo.
162
Em vez de aceder as posições com índices (dados[0], dados[1], ...),
se associa cada valor a uma variável distinta de forma concisa e legível.
A sintaxe básica consiste em colocar, do lado esquerdo da atribuição, um
conjunto de variáveis separadas por vírgulas, correspondendo à mesma
quantidade de elementos existentes no tuplo do lado direito.
variavel1, variavel2, variavel3 = tuplo
Dessa forma, cada variável recebe o valor correspondente na ordem em que
aparece no tuplo. Essa técnica é especialmente útil quando se trabalha com
dados agrupados, pois evita confusão com índices e melhora a clareza do
código. Portanto, o exemplo anterior, está aqui a ser reescrito utilizando o
conceito de desempacotamento:
163
b) Ignorar Múltiplos Valores com *_
Se for necessário descartar vários elementos ao mesmo tempo, é possível
utilizar *_, conforme apresenta-se o código a seguir.
164
d) Desempacotamento com ciclos for
É muito comum usar desempacotamento em estruturas de repetição. Quando
uma lista contém tuplos, o Python permite que cada tuplo seja
automaticamente separado em variáveis.
Perceba no exemplo a seguir que o desempacotamento nome, idade no
ciclo for torna o código mais limpo e legível do que se usar índices em cada
iteração.
165
7.2.3 Iterar sobre tuplos
Tal como acontece com listas, os tuplos também podem ser percorridos com
um ciclo for. Isso permite trabalhar elemento a elemento de forma simples.
No tópico anterior, na alínea d, foi apresentado um caso especial de iteração
em que o Python realiza automaticamente o desempacotamento de cada tuplo
dentro de uma lista, atribuindo os valores diretamente a variáveis distintas.
Agora o foco é no caso mais comum, em que se percorre um tuplo simples,
tratando cada elemento individualmente.
O exemplo a seguir mostra a simplicidade de percorrer um tuplo com ciclo.
166
7.2.4 Tuplos multidimensionais
Um tuplo também pode conter outros tuplos no seu interior, formando uma
estrutura multidimensional. Isso é semelhante ao que acontece com listas
multidimensionais, mas a diferença está no fato dos valores não poderem ser
alterados depois de definidos.
O exemplo seguinte ilustra o funcionamento de um tuplo multidimensional.
Para aceder a um elemento é necessário indicar dois índices em sequência: o
primeiro corresponde à linha e o segundo à coluna. Diferente das listas, o
conteúdo permanece fixo, ou seja, não é possível alterar matriz[0][1]
nem acrescentar novos elementos após a criação do tuplo.
167
7.3 Conjuntos (set)
169
7.3.2 Operações matemáticas em conjuntos
170
tornando o código mais conciso e direto. Essa forma é prática quando se
realizam várias operações encadeadas, tal como numa expressão matemática.
No entanto, os métodos (union(), intersection(), etc.) tornam o
código mais explícito e legível.
A seguir apresenta-se um exemplo simples que demonstra essas operações
na prática.
171
Método Descrição
add(x) Adiciona o elemento x ao conjunto
remove(x) Remove x; gera erro se não existir
discard(x) Remove x; não gera erro se não existir
clear() Remove todos os elementos, deixando o conjunto vazio
len(set) Devolve o número de elementos no conjunto
172
7.4 Dicionários (dict)
173
7.4.2 Adicionar e alterar valores
174
7.4.3 Métodos úteis
Método Descrição
keys() Devolve todas as chaves do dicionário
values() Devolve todos os valores
items() Devolve pares (chave, valor)
get(chave) Devolve o valor da chave ou None se não existir
pop(chave) Remove a chave e devolve o valor associado
update(...) Atualiza ou insere pares chave → valor
clear() Remove todos os elementos
175
Um exemplo com a aplicação dos métodos listados na Tabela 14 é
apresentado a seguir. Para tornar a explicação mais concreta, escolheu-se
como cenário um dicionário de cartas de baralho, no qual cada carta funciona
como a chave e o respetivo valor numérico corresponde ao valor atribuído
no jogo.
Esse exemplo a seguir permite observar como cada método atua sobre o
dicionário. Desde a simples consulta de chaves e valores até a remoção de
elementos e limpeza completa da estrutura. Assim, além de compreender a
descrição teórica de cada método, é possível visualizar sua execução.
176
7.5 Comparação e Boas Práticas
177
As Tabela 15 e Tabela 16 apresentam, respetivamente, uma breve comparação
entre algumas propriedades e características das estruturas apresentadas
neste capítulo.
Tabela 15 - Propriedades das Estruturas
* Deve-se ter atenção ao fato de que nos dicionários, a ordem dos elementos
só passou a ser preservada a partir do Python 3.7. Antes disso, a
linguagem não garantia a ordem de inserção das chaves.
178
− Precisas de aceder elementos por posição numérica
− Vais frequentemente adicionar, remover ou reordenar elementos
179
b) Quando usar conjuntos (sets):
− Precisas eliminar duplicados automaticamente
− A ordem dos elementos é irrelevante
− Vais realizar operações de teoria de conjuntos (união, interseção)
− Precisas verificar rapidamente se um elemento existe (in).
180
− O acesso por nome é mais significativo que por índice numérico
− Precisas de agrupar informações relacionadas sob um mesmo contexto.
181
duplicados e verificar pertença rapidamente, enquanto dicionários permitem
mapear nomes a valores de forma clara e eficiente.
182
− Conjuntos são imbatíveis quando é preciso eliminar duplicados ou
trabalhar com operações matemáticas de união, interseção e diferença
− Dicionários são ideais quando é necessário associar um valor a uma
chave nomeada, como num mapa de contactos ou numa tabela de
configurações.
183
2. Filtragem de elementos → a função filter()serve para selecionar
elementos de uma coleção com base numa condição. Recebe dois
argumentos: uma função que representa o critério e a coleção a ser
analisada. O resultado é uma sequência filtrada que contém apenas os
elementos que satisfazem a condição. Ao usar lambda, a condição pode
ser escrita de forma compacta e direta.
Neste exemplo a seguir são filtrados produtos com preço maior que 50.
O comando lambda produto: produto[1] > 50 atua como um
teste, onde cada elemento só é mantido se esta condição for verdadeira.
184
sequência com todos os valores já modificados. O uso de lambda nesse
caso facilita a escrita de transformações rápidas e legíveis.
Veja um exemplo a seguir que aplica um desconto de 8% sobre todos os
produtos. O comando lambda preco: preco * 0.8 é a
transformação aplicada a cada elemento da lista original.
185
Mas estes comandos estão a ser usados com o lambda, que tem clareza e
concisão. Em vez de ocupar várias linhas com uma função definida apenas
para ser usada uma única vez, o programador consegue expressar a regra de
forma compacta, dentro da própria chamada.
Contudo, é importante não abusar deste recurso. Quando a lógica for
demasiado longa ou complexa, é preferível criar uma função tradicional com
def, pois a legibilidade deve ser sempre uma prioridade.
186
7.7.2 Sistema de conquistas em jogos
187
7.7.3 Playlist Inteligente
188
categoria, valor e descrição. A partir desses registos, é possível agrupar os
valores por categoria e calcular totais mensais, fornecendo uma visão clara
dos principais focos de despesa. O exemplo seguinte mostra como
implementar esse controlo em Python.
Os tuplos são ideais para representar dados fixos que não devem ser
alterados, como acontece em torneios ou competições, onde as equipas
inscritas permanecem constantes durante o evento. Cada tuplo pode guardar
informações relevantes, como o nome da equipa, o país de origem e a sua
pontuação de classificação.
189
No exemplo seguinte, uma lista de tuplos é usada para organizar as equipas
participantes num torneio de e-sports, gerar automaticamente o calendário de
partidas e identificar a equipa favorita com base no maior (max) rating.
190
viciante, ou seja, que está presente em todos os dias, e calcular a diversidade
total de apps utilizadas.
191
7.7.8 Match de Amizade
192
7.8 Exercícios propostos
193
2) Criar uma lista de dicionários que registe utilizadores, em que cada
dicionário tenha as chaves user (string) e desafios (inteiro).
− Ordenar essa lista por desafios em ordem decrescente (em caso de
empate, ordenar alfabeticamente por user para manter resultado
determinístico)
− Exibir os três primeiros elementos numerando a partir de 1 no formato
1º Nome — X desafios
− Dica: usar
sorted(lista, key=lambda d: ( -d["desafios"],
d["user"] )) ou
sorted(lista, key=lambda d: d["desafios"],
reverse=True).
194
4) Fazer o código de uma agenda como lista de tuplos (data, hora,
título), conforme o exemplo a seguir.
tuplos = [
("2025-10-28", "09:30", "Consulta Médica de Família"),
("2025-11-27", "14:00", "Dentista"),
("2025-11-21", "10:00", "Consultoria Python"),
]
− Ordenar por data e hora
− Exibir cada compromisso no formato apresentado a seguir, um por linha,
desempacotando os tuplos no ciclo for.
− Dica: for data, hora, titulo in agenda.
Exemplo de saída:
195
6) Fazer um programa que funcione como um gerador de equipas para
jogos online, ou seja, dada uma lista de jogadores com ratings, dividir
em duas equipas balanceadas.
− Usar tuplos para armazenar (nome, rating) e ordenar por rating
− Atribuir jogadores alternadamente às equipas, por exemplo, melhor
jogador à equipa A, segundo melhor à B e assim sucessivamente.
7) Fazer um programa em Python que gere uma grelha 5x5 como lista
multidimensional, iniciando todas as posições com o carácter #.
− Colocar o jogador J na posição (linha, coluna), gerada aleatoriamente
− Marcar 3 obstáculos com o carácter | em posições geradas aleatoriamente
196
− Exibir a grelha linha a linha
− Dicas: usar compreensões aninhadas na criação da matriz e considerar
coordenadas baseadas em 0 (de 0 a 4) e ignorar posições fora da grelha.
− Exemplo de saída:
197
9) Implementar um sistema de votação para escolher a "Melhor Série do
Ano". Para resolver o problema, usar um dicionário para contar votos
(série: número de votos).
− Permitir votos múltiplos, mas evitar duplicados por utilizador (usar sets).
− Mostrar o top 3 das séries mais votadas.
198
A saída seria algo como apresentado a seguir.
199
8 MANIPULAÇÃO E FORMATAÇÃO DE STRINGS
200
Estas técnicas permitem desde tarefas simples, como mostrar apenas a
primeira letra de um nome, até manipulações mais elaboradas, como inverter
uma frase inteira.
Dominar indexação e fatiamento torna o trabalho com texto em Python mais
poderoso, flexível e expressivo.
8.1.1 Indexação
String P y t h o n
Índice 0 1 2 3 4 5
Índice- -6 -5 -4 -3 -2 -1
201
8.1.2 Fatiamento (slicing)
O fatiamento (slicing) permite extrair uma parte de uma string, criando uma
nova sequência sem alterar a original.
Essa técnica é extremamente útil para manipular texto de forma flexível, seja
para obter palavras, inverter frases ou selecionar apenas os caracteres
desejados.
A sintaxe geral é:
string[início:fim:passo]
onde:
− início → índice onde a extração começa (inclusivo). Se omitido, assume
o início da string (0)
− fim → índice onde a extração termina (exclusivo). Se omitido, assume o
fim da string
− passo → salto entre os índices. Se omitido, assume o valor 1
202
8.2 Métodos de manipulação
203
um exemplo de utilização e o respetivo resultado, permitindo compreender
de imediato a sua aplicação prática.
A Tabela 18 apresenta os métodos mais comuns para converter o texto entre
maiúsculas e minúsculas. Estes métodos permitem padronizar a escrita ou
ajustar a forma de apresentação dos dados.
204
Tabela 19 - Remoção de espaços e caracteres
205
A Tabela 21apresenta métodos que permitem dividir uma string em várias
partes e, no sentido inverso, unir elementos de uma lista numa única string.
São ferramentas essenciais para o processamento de dados estruturados.
206
A sintaxe é simples, pois basta prefixar a string com f ou F e usar chavetas
{} para inserir expressões.
Dentro das chavetas, pode-se incluir qualquer expressão Python válida, como
operações matemáticas, chamadas de métodos, condicionais até aceder a
elementos em listas e dicionários, como no exemplo a seguir.
207
A seguir apresenta-se um exemplo que ilustra diferentes possibilidades de
formatação numérica com f-strings:
208
Opcionalmente, é possível indicar um caractere de preenchimento antes do
especificador de alinhamento.
A seguir apresenta-se um exemplo com diferentes combinações.
209
A Tabela 22 seguinte reúne os códigos de formatação mais utilizados em
Python.
Tabela 22 - Códigos de formatação de datas e horas
Exemplo
Código Significado
(para 15/01/2024 10:30:45)
%d 15 Dia do mês (2 dígitos)
%m 01 Mês (2 dígitos)
%Y 2024 Ano com 4 dígitos
%y 24 Ano com 2 dígitos
%H 10 Hora no formato 24h (00–23)
%M 30 Minutos (00–59)
%S 45 Segundos (00–59)
%A Monday Nome completo do dia da semana
%a Mon Nome abreviado do dia da semana
%B January Nome completo do mês
%b Jan Nome abreviado do mês
210
b) Tabelas formatadas
Outro uso frequente das f-strings é a criação de tabelas alinhadas, úteis
para relatórios ou inventários. No exemplo seguinte, cada coluna é ajustada
com largura e alinhamento específicos.
211
No exemplo anterior, observa-se que a instrução print() original foi
adaptada. A linha ficava demasiado extensa para ser escrita de uma só vez,
pelo que foi dividida em várias partes para facilitar a leitura e a manutenção
do código.
Portanto, quando um print() fica muito comprido, há algumas formas de
quebrar a linha no código sem afetar a saída.
A primeira técnica (com parênteses) é como no exemplo anterior, onde se
quebra as linhas dentro dos parênteses.
A segunda maneira é usar a barra invertida \. A \ indica que a instrução
continua na linha seguinte, conforme este exemplo:
212
8.4 Casos Resolvidos e Explicados
Métodos utilizados:
1. .strip() → Remover espaços em branco do início e do fim da string.
2. .lower()→ Converter os caracteres alfabéticos para minúsculas
3. replace(" ", "_") → Substituir cada ocorrência de um espaço
por um underscore
O ponto chave é a imutabilidade das strings que permite encadear métodos.
Cada método retorna nova string, que é usada como entrada para o próximo.
213
8.4.2 Extração de Informação de um E-mail
214
2. string[:indice] → Fatiamento para obter uma substring do início
até (mas não incluindo) o índice
3. string[indice+1:] → Fatiamento para obter uma substring a
partir do índice seguinte até o final.
Neste exemplo se cria uma tabela de classificação para um jogo online que
mostra o nome do jogador, sua pontuação (K/D - Eliminações/Mortes) e o
nível. O nome deve ter 20 caracteres alinhados à esquerda, a pontuação K/D
deve ter 8 caracteres centralizados com 2 casas decimais e o nível deve ter 6
caracteres alinhados à direita.
A saída do programa deve ser a seguinte:
215
O código é apresentado a seguir.
216
Alguns exemplos das técnicas aplicadas:
1. f"{nome:<15}" → Alinhado à esquerda e 15 caracteres de largura
2. f"{kd:>6.2}" → K/D alinhado à direita com 6 caracteres e 2 casas
decimais
Para além destas técnicas, há neste exemplo:
217
− Cálculo de K/D com proteção contra divisão por zero
kd = elim / mortes if mortes > 0 else elim
− Extração sequencial de dados
for nome, elim, mortes, nivel in jogadores:
− Acumulação progressiva de totais
total_eliminacoes += jogador[1]
− Uso de desempacotamento de tuplos para aceder direto às variáveis
− Cálculo condicional seguro para evitar erros de divisão por zero
− Formatação tabular profissional com alinhamento consistente
− Separadores visuais com | para organizar colunas.
218
PARTICULAS = {"de","da","das","do","dos","e"}
⋅ Filtrar partículas (sem diferenciar maiúsculas/minúsculas):
partes_validas = [p for p in partes if [Link]() not in
PARTICULAS]
⋅ Obter a primeira letra alfabética de cada parte:
letra = next((ch for ch in p if [Link]()), "")
⋅ Construir as iniciais:
inic = '.'.join([Link]() for letra in letras if
letra) + '.'
⋅ Terminar a leitura quando a linha estiver vazia depois de strip():
if not [Link](): break
Exemplo de saída:
219
morse_code = {
'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.',
'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---',
'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---',
'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-',
'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--',
'Z': '--..', ' ': '/', '1': '.----', '2': '..---', '3': '...--',
'4': '....-', '5': '.....', '6': '-....', '7': '--...',
'8': '---..', '9': '----.', '0': '-----'
}
Estas são as funções sugeridas:
def texto_para_morse(texto):
# TODO: Converter cada caractere para Morse
# TODO: Juntar códigos com espaço entre letras
# TODO: Usar '/' entre palavras
pass
def morse_para_texto(codigo_morse):
# TODO: Criar dicionário reverso
# TODO: Separar palavras por ' / '
# TODO: Separar letras por espaços
# TODO: Traduzir cada código Morse
pass
def main():
# TODO: Menu com opções
# TODO: Input do usuário
# TODO: Detectar direção automaticamente
# TODO: Mostrar resultados formatados
pass
220
# Adicionar estes testes:
# print(texto_para_morse("PYTHON"))
# Deve retornar: ".--. -.-- - .... --- -."
# print(morse_para_texto(".--. -.-- - .... --- -."))
# Deve retornar: "PYTHON"
Exemplo de saída:
221
3) Desenvolva um programa que receba um texto do utilizador e conte o
número de palavras, de frases (terminadas com .!?) e parágrafos
(separados por \n). Identificar a palavra mais longa e calcular o
comprimento médio das palavras
Este é um exemplo de saída:
222
9 FICHEIROS EM PYTHON
223
9.1.1 Nome e extensão de ficheiros
O nome pode ser apenas o do ficheiro, desde que esteja na mesma pasta do
programa, ou o caminho completo. O modo define se o ficheiro será lido,
escrito ou acrescentado.
Um ficheiro normalmente possui uma extensão, que ajuda a identificar o tipo
de conteúdo, como por exemplo:
− [Link] → ficheiro de texto simples
− [Link] → texto em colunas separadas por vírgulas
− [Link] → ficheiro binário de imagem
− [Link] → ficheiro binário genérico
A extensão não altera a forma como Python lê o ficheiro, mas serve como
convenção para o utilizador e para o sistema operativo reconhecer o tipo de
dados.
Uma vez aberto, o ficheiro comporta-se como um objeto em memória, que
pode ser manipulado com métodos de leitura e escrita. É responsabilidade do
programador fechar o ficheiro depois de o utilizar.
225
Este estilo exige chamadas independentes para cada operação, o que pode
tornar o código mais verboso. Ainda assim, é bastante útil quando se mantém
compatibilidade com versões antigas de Python ou quando se trabalha em
projetos que já utilizam extensivamente o módulo os.
A alternativa mais moderna é a biblioteca pathlib, introduzida
oficialmente no Python 3.4 e hoje considerada a abordagem recomendada.
Diferente do [Link], a classe Path trata cada caminho como um objeto,
oferecendo uma API orientada a objetos, clara e intuitiva. Uma das vantagens
é que o operador / pode ser usado para unir diretórios e ficheiros de forma
natural.
No exemplo a seguir, repete-se as mesmas operações, mas com pathlib.
O método resolve() devolve o caminho absoluto, tal como abspath()
no caso anterior. A propriedade parent indica a pasta mãe do ficheiro,
equivalente a dirname().
226
9.1.2 Texto vs. Binário
Python permite abrir ficheiros tanto em modo texto como em modo binário.
No modo texto, a leitura e a escrita trabalham com cadeias de caracteres,
ideais para ficheiros como .txt ou .csv. Já no modo binário, os dados são
tratados exatamente como estão gravados, sem conversões, o que é essencial
para imagens, áudio, ficheiros comprimidos ou outros formatos não textuais.
A distinção é feita através da presença da letra b no modo de abertura. Se o
modo inclui a letra b, o ficheiro é tratado como binário (rb, wb, ab,
r+b, w+b, a+b).
227
Tabela 23 - Modos principais de abertura de ficheiros em Python
Modo Modo
Código Descrição
texto binário
228
Quando um ficheiro é aberto em modo texto, o Python normaliza o carácter
de nova linha \n de acordo com o sistema operativo. No Windows, esse
carácter é convertido para \r\n, enquanto em Linux e macOS permanece
como \n. Essa adaptação é conveniente na maioria dos casos, mas pode
causar problemas em formatos estruturados, como os ficheiros CSV.
É comum que, no Windows, apareçam linhas em branco adicionais quando
um CSV é escrito sem indicar o parâmetro newline. Para evitar esse
problema, o ficheiro deve ser aberto com newline="", o que impede o
Python de alterar os caracteres de quebra de linha, conforme exemplo a
seguir.
229
9.1.3 Fechar ficheiros com close()
230
Esta abordagem é considerada uma boa prática e deve ser preferida em
praticamente todos os casos.
231
Em situações como registos de log ou sistemas de alta frequência, pode ser
útil controlar o tamanho do buffer ou forçar a escrita imediata com
flush(), conforme apresentado no exemplo a seguir.
232
O resultado corresponde exatamente ao que está gravado no ficheiro,
incluindo quebras de linha.
Este método é útil quando apenas algumas linhas iniciais interessam, sem
necessidade de percorrer o ficheiro completo.
233
9.2.4 Iterar diretamente sobre o ficheiro
Este método é eficiente para ficheiros grandes, porque processa uma linha de
cada vez, sem carregar tudo para a memória.
Entretanto, se estás a trabalhar em ficheiros muito grandes, é ainda mais
eficiente processar o conteúdo em partes.
− O método [Link](n) lê até n bytes ou caracteres por vez. Deve ser
colocado dentro de um ciclo para se processar o ficheiro bloco a bloco
− O padrão iter(partial([Link], n), ""), do módulo
functools, cria automaticamente um ciclo que chama [Link](n)
repetidamente até devolver uma string vazia (""), sinal de fim de
ficheiro. Desta forma, o programa percorre o ficheiro em blocos, sem
ocupar memória excessiva e sem necessidade de controlar manualmente
a condição de paragem.
234
9.3 Métodos de escrita em ficheiros
235
9.3.2 Escrever múltiplas linhas (writelines())
236
9.4 Exemplo prático com ficheiros de texto
237
9.5 Tratamento de exceções
238
Neste próximo exemplo vê-se como tratar estes casos de forma simples.
Dessa forma, mesmo que um erro ocorra, o programa informa o utilizador e
continua a correr, em vez de terminar abruptamente.
239
Na prática, prefere-se o uso do with, mas este exemplo ilustra todas as
cláusulas possíveis num bloco de tratamento de erros.
240
Esse erro indica que o ficheiro não está em UTF-8, mas numa codificação
diferente. Por exemplo, a letra “é” corresponde ao byte 0xe9 em Latin-
1/Windows-1252, mas não é válido em UTF-8.
Com este cuidado, evita-se a maioria dos erros relacionados com leitura de
acentos e caracteres especiais.
241
Exemplo 1: Contador de palavras
242
Este programa abre o ficheiro de origem em modo leitura e o ficheiro de
destino em modo escrita. Cada linha lida é imediatamente gravada no novo
ficheiro. O tratamento de erros assegura que, caso o ficheiro original não
exista, o utilizador seja informado.
Neste exemplo se deve copiar apenas as linhas que contêm uma palavra
específica em um ficheiro e, depois, criar um novo ficheiro filtrado.
243
9.6 Manipular ficheiros CSV e JSON
244
a) Método 1 – Leitura como listas
Cada linha é interpretada como uma lista, em que a posição de cada elemento
representa a coluna correspondente, conforme o exemplo a seguir.
Assim como na leitura, a escrita em ficheiros pode ser feita a partir de listas
ou de dicionários. A seguir se apresentam exemplos de diferentes métodos
de escrever em ficheiros .csv.
245
Em cada sublista corresponde a uma linha no ficheiro. O argumento
newline="" evita linhas em branco adicionais no Windows, e
encoding="utf-8" garante suporte a acentos.
O método writerows() recebe uma lista de listas e grava todas de uma
só vez no ficheiro CSV. Cada sublista dentro de dados torna-se uma linha no
ficheiro.
Ressalta-se que se fosse utilizado o método writerow() (no singular),
seria necessário chamar o método repetidamente para cada linha.
246
O método DictWriter usa a lista fieldnames para definir a ordem das
colunas. O método writeheader() escreve automaticamente o
cabeçalho antes dos dados.
A lista fieldnames de strings define os nomes das colunas e a ordem em
que aparecem no ficheiro CSV e, mesmo que o dicionário dos dados tenha
as chaves numa ordem diferente, o DictWriter vai respeitar a ordem
indicada em fieldnames.
Vale ressaltar que se faltar uma chave no dicionário, a coluna correspondente
fica em branco e se aparecer uma chave que não está em fieldnames,
ocorre erro (a menos que seja tratado com extrasaction='ignore').
c) Personalização do CSV
Nem sempre o separador padrão (vírgula) é o mais adequado. Em Portugal e
no Brasil, por exemplo, o ponto e vírgula (;) é muito utilizado em ficheiros
CSV. O módulo csv permite personalizar o delimitador de forma simples,
como no exemplo a seguir.
247
O argumento delimiter=';' altera o separador de colunas, permitindo
ler ficheiros que não utilizam a vírgula.
248
Tabela 26 - Opções de quoting no módulo csv
a) Leitura de JSON
O formato JSON é frequentemente usado para guardar dados estruturados
em ficheiros. O Python permite abrir o ficheiro e convertê-lo diretamente
para um dicionário.
Segue um exemplo elucidativo.
250
b) Escrita em JSON
Também é possível guardar dados Python diretamente em ficheiros JSON,
garantindo que estruturas como listas e dicionários sejam preservadas,
conforme o próximo exemplo.
251
Considere este exemplo de JSON Lines (NDJSON) a seguir, onde cada
linha contém um objeto JSON válido.
252
O método [Link]() converte uma string JSON para estruturas
Python, enquanto [Link]() faz o contrário, gerando uma string
JSON a partir de dicionários ou listas.
253
É importante perceber que em ficheiros de texto, seek() pode não apontar
exatamente para “caracteres” devido a codificações; em binário, a posição
corresponde diretamente a bytes.
9.8.1 Erros
254
− O ficheiro não pode ser aberto porque não está disponível ou o
sistema não permite a operação
− Exceções típicas: FileNotFoundError (ficheiro não existe),
PermissionError (sem permissões), caminho inválido ou
ficheiro bloqueado por outro processo
2. Erros de conteúdo
− O ficheiro é aberto, mas os bytes não correspondem à codificação ou
ao formato esperado, como ao ler texto com acentos em ficheiro não
UTF-8 e ao tentar gravar caracteres especiais em ficheiros
configurados com codificação limitada
− Exceções típicas: UnicodeDecodeError (leitura de texto com
codificação errada), UnicodeEncodeError (escrita em texto
com caracteres não suportados).
1) Verificar acesso
− Confirmar se o ficheiro existe e se há permissões de leitura/escrita
(FileNotFoundError, PermissionError)
255
− Em caso de incerteza sobre o tipo de conteúdo, recomenda-se abrir
o ficheiro em modo binário numa primeira etapa, realizando apenas
uma inspeção mínima, como a verificação do tamanho ou da
eventual presença de um BOM.
⋅ BOM (Byte Order Mark): alguns ficheiros UTF-8 começam
com três bytes (EF BB BF). Se o ficheiro for aberto com
encoding="utf-8", essa marca pode aparecer como
carácter estranho. Com encoding="utf-8-sig", o BOM
é ignorado automaticamente.
2) Selecionar codificação
− Usar UTF-8 como padrão universal
− Se houver BOM, usar UTF-8-sig
− Em ficheiros antigos, recorrer a Latin-1 ou CP1252 se UTF-8 falhar
4) Validar o resultado
− Verificar se aparecem muitos símbolos , sinal de erro de codificação
256
− Confirmar se os dados mantêm sentido: número de colunas em CSV, ou
leitura correta em JSON.
257
Se ainda
Se falhar falhar
Abrir Tentar → Se nada resultar →
→
ficheiro UTF-8 UTF-8- leitura tolerante
sig Latin-1 /
CP1252
Se falhar Usar
→ Útil Alternativa errors= Informar o
FileNotFou quando para utilizador
Padrão "replace"
ndError existe BOM ficheiros da possível
universal ou
ou (marca no antigos ou perda de
início) vindos do errors= caracteres
Permission
Error Windows "ignore"
258
de backup. Isso assegura que, mesmo se ocorrer uma falha durante a
escrita, o ficheiro original permanecerá intacto
− Usar flush() e fsync() → O método flush() garante que os
dados sejam enviados da memória para o disco, enquanto fsync()
força a gravação real no disco, garantindo que a operação seja
completada corretamente
− Substituir o ficheiro original → Após garantir que a escrita no ficheiro
temporário foi concluída com sucesso, o próximo passo é substituir o
ficheiro original pelo temporário utilizando [Link](). A
operação replace() é atómica e garante que, se o sistema falhar, o
ficheiro original não seja corrompido, já que a substituição só acontece
após a conclusão da escrita.
259
Uma forma prática de aplicar este conceito consiste em usar a biblioteca
tempfile para gerar automaticamente um ficheiro temporário no mesmo
diretório, garantindo nomes seguros e evitando colisões. No passo final, a
substituição do original é feita com [Link](), o que assegura que a
troca só ocorre depois de a escrita estar concluída com sucesso. O exemplo
a seguir mostra essa implementação.
260
9.9 Aplicações de Ficheiros Texto
261
9.9.2 Relatório diário de vendas da loja
262
9.9.3 Fechamento diário de caixa
263
9.9.4 Sistema de configuração de aplicação web
264
9.9.5 Conversor de Formatos (CSV ⇄ JSON)
265
Inicialmente cria-se o ficheiro [Link] a partir da string fornecida;
em seguida, depois converte o CSV em JSON e confirma a criação de
produtos_api.json com a chave de lista definida; por fim, executa
gera o ficheiro produtos_export.csv e verifica que os campos e o
cabeçalho foram preservados corretamente.
266
9.10 Ficheiros Binários
Ficheiros binários são aqueles que armazenam dados exatamente como estão
gravados, sem qualquer tipo de conversão ou processamento de texto. Isso é
crucial para quando se trabalha com imagens, áudio, vídeos, ficheiros
comprimidos ou qualquer outro formato que não seja baseado em texto.
Diferente dos ficheiros de texto, que armazenam dados como cadeias de
267
caracteres, os ficheiros binários mantêm os dados tal como estão no disco,
byte a byte.
Neste exemplo, data conterá a sequência exata de bytes que foram gravados
no ficheiro e pode ser processada ou manipulada conforme necessário.
Neste exemplo, o conteúdo que está a ser escrito no ficheiro é uma sequência
de bytes (b'\x89PNG\r\n\x1a\n...'), que corresponde ao início de
um ficheiro PNG. Ao escrever os dados diretamente em formato binário,
garante-se que a estrutura do ficheiro será preservada sem qualquer alteração.
269
9.10.4 Manipulação de Dados Binários
270
struct, que permite converter entre tipos de dados Python e suas
representações binárias.
Aqui está um exemplo de como usar o struct para trabalhar com dados
binários mais complexos.
272
9.11 Exemplos práticos
273
A biblioteca re (expressões regulares) é uma ferramenta poderosa para o
processamento de texto e de padrões. Ao importar, é possível:
− Buscar padrões específicos em texto
− Validar formatos, como emails, URLs e telefones
− Extrair informações estruturadas de texto não estruturado
274
− Substituir partes do texto baseado em padrões
É essencial em cibersegurança para analisar logs e em Inteligência Artificial
para pré-processar dados de texto.
Este exemplo demonstra como ficheiros JSON são usados para persistir
dados em sistemas de IA. Na prática, modelos de Machine Learning devem
guardar dados de treino, guardar os parâmetros do modelo para implantação,
guardar as métricas de performance para avaliação e manter o histórico de
versões para controlo de qualidade. Neste contexto, o objetivo deste exemplo
é apresentar um sistema em IA simples, apenas com o intuito de criar um
sistema que guarda tais dados.
275
Este é o relatório de saída deste sistema:
276
9.11.3 Sistema de Proteção de Ficheiros Simples
277
Com a seguinte saída no ecrã:
278
Neste segundo exemplo, pertinente ao mesmo contexto, verifica uma
password, de forma bem básica, inicialmente guarda a palavra-passe num
ficheiro e depois a compara com o que o utilizador digita.
279
E no exemplo a seguir, há um sistema de registo de acessos, onde dados são
acrescentados ao ficheiro e guarda a data/hora e o nome do utilizador.
280
9.12 Exercícios Propostos
281
10 PROGRAMAÇÃO ORIENTADA A OBJETOS
Pense numa analogia simples, uma classe é como uma receita: lá estão as
instruções e as características do que se quer preparar (por exemplo, um bolo
de chocolate). Ler a receita não produz o bolo, é preciso executá-la. Quando
282
“executa-se a receita” de uma classe em Python, cria-se um objeto: o bolo
feito. As características do bolo (cor, tamanho, cobertura) correspondem aos
atributos do objeto, as ações (misturar, verter, cozer) correspondem aos
métodos definidos na classe.
Resumindo, a classe define, o objeto concretiza, os atributos descrevem o
estado, métodos definem o que o objeto sabe fazer. Para inicializar os
atributos de um objeto de uma classe, é necessário definir o método
construtor dentro da classe. Em Python, o método construtor é designado de
__init__. O construtor recebe como parâmetro a própria instância do
objeto (self) e quaisquer outros parâmetros que sejam necessários para
inicializar os atributos do objeto.
Dentro do construtor, os atributos do objeto são definidos usando a sintaxe
self.nome_do_atributo = valor. Em Python, define-se uma
classe com a palavra-reservada class, seguida do nome (por convenção,
em CamelCase):
Neste exemplo, definiu-se uma classe com três atributos definidos dentro do
método construtor (__init__). Esta classe, também tem um método
próprio designado de ligar_motor. A criação de objetos (instanciação)
283
reserva memória, executa o construtor para inicializar o estado e devolve
uma referência. Guardar essa referência numa variável não “copia” o objeto,
apenas aponta para o mesmo exemplar (semântica de referência).
Notas úteis
Pode-se também usar argumentos nomeados:
Automovel(marca="BMW", matricula="AA-00-AA")
Para confirmar o tipo da variável pode-se utilizar a função type:
type(auto_bmw) que devolve Automovel. Se o número/ordem dos
argumentos não corresponder ao __init__, o Python lança um
284
TypeError (assinatura inválida). A função integrada
isinstance(obj, Classe) verifica se um objeto é instância de uma
classe (ou suas subclasses). Já issubclass(Sub, Super) verifica
relações na hierarquia de herança.
285
O valor pertence apenas àquela instância e pode ser lido/alterado sem afetar
as restantes. Já, a chamada dos atributos estaticos é recomendado que seja
através do nome da classe: [Link]. O valor pertence
à classe e é comum a todas as instâncias. Pode ser lido a partir da instância
([Link]), mas continua a pertencer à classe. Se for atribuída
um novo valor através da instância, cria-se um atributo de instância com o
mesmo nome (sombreamento), sem alterar o valor partilhado da classe.
Neste exemplo a seguir, cria-se um objeto da classe Automovel
armazenado na variável auto_bmw. Para invocar um atributo de instancia
tem-se de indicar a variável auto_bmw seguido do atributo. Já para os
atributos estáticos pode-se indicar a classe Automovel.
286
A chamada típica é
A chamada típica é:
287
10.3 Encapsulamento
288
Neste exemplo, apresenta-se uma classe Automovel que ilustra, de forma
integrada, o encapsulamento: usa _nome para ocultação fraca, __nome para
ocultação forte com name mangling, e providencia getters/setters para
acesso controlado, evidenciando a separação entre interface pública e
detalhes internos.
No exemplo seguinte cria-se uma instância de Automovel e demonstra-se,
o acesso a um atributo público, a um atributo com ocultação fraca (_) e, por
fim, o acesso controlado ao atributo com ocultação forte (__) através do
respetivo getter.
289
10.4 Herança
Mesmo sem acrescentar código nas subclasses, Cao e Gato já possuem tudo
o que Animal expõe (por exemplo, fazer_som). Isto ilustra a reutilização
290
direta de métodos e atributos herdados. Assim, atributos como nome e vivo
e métodos utilitários como info() vivem uma única vez em Animal e são
herdados por quem deles precisa.
Quando for necessário que as subclasses tenham atributos e métodos
específicos da classe, define-se um __init__ na subclasse e usa-se
super().__init__(atributo) para reaproveitar a parte comum da
classe. Depois, acrescenta-se o que é específico. Quando um método é
invocado numa subclasse, o Python procura a implementação desse método
dentro da própria classe e, caso não encontre, procura na classe superclasse.
Ao utilizar super(), a implementação de um método é delegada
explicitamente para a superclasse, permitindo que a subclasse modifique ou
estenda o comportamento definido na superclasse. Conforme apresentado
neste exemplo da subclasse Cao:
291
10.4.1 Herança múltipla
Em Python, é possível que uma classe herde de mais do que uma classe ao
mesmo tempo. A isto chama-se herança múltipla. A ideia é permitir que uma
classe final combine comportamentos vindos de várias origens diferentes,
sem que seja necessário escrever tudo de raiz em cada classe. Muitas vezes,
essas classes intermediárias não representam propriamente um tipo “do
mundo real”, mas sim uma capacidade adicional: por exemplo, algo que sabe
registar operações de log, algo que sabe guardar a data de criação, algo que
sabe ser exportado etc. A estas classes centradas numa capacidade em vez de
numa identidade chama-se frequentemente mixins. Em vez de dizer “um
objeto é um Logavel”, diz-se antes “este objeto tem a capacidade de registar
log porque herda de Logavel”. Em termos práticos, herança múltipla permite
construir tipos finais que agregam várias capacidades sem duplicar código.
Considere-se o seguinte exemplo da classe Animal definida anteriormente:
292
Até aqui tem-se duas classes que não se sobrepõem:
− Animal sabe o nome e sabe apresentar-se com info().
− Nadador sabe guardar uma velocidade de nado e sabe dizer que se
move “a nadar”.
A seguir cria-se uma classe Pato que herda das duas ao mesmo tempo:
herda de Animal e herda de Nadador. Ou seja, Pato passa a ser ao
mesmo tempo “um Animal com nome” e “algo que nada”. Isto já é herança
múltipla.
293
nome) para inicializar a parte que vem de Animal (isto é, dar um nome ao
pato) e Nadador.__init__(self, velocidade) para inicializar
a parte que vem de Nadador (isto é, a velocidade de nado), o que significa
que se está a construir manualmente as duas dimensões do objeto: a
identidade (nome) e a capacidade funcional (velocidade). Por fim,
define-se em Pato um método próprio mover() que, mesmo existindo já
em Nadador, é reescrito de forma mais específica para este tipo concreto,
combinando dados herdados das duas superclasses (o nome vindo de
Animal e a velocidade de nado vinda de Nadador), o que exemplifica
como uma subclasse pode personalizar e especializar o comportamento
herdado.
Agora, veja isto a funcionar na prática:
294
10.5 Sobrescrita de métodos
A saída será:
295
Como é possível observar no exemplo anterior, a subclasse herda
fazer_som de Animal, mas redefine o método com um comportamento
específico. Neste exemplo, é possivel verificar que o mesmo nome de
método provoca ações distintas consoante a classe real do objeto.
296
− get_*/set_* com validação (nome não vazio, NIF com 9 dígitos
numéricos, salário positivo, departamento não vazio);
− o método transferir_para(novo_departamento) que altera
o departamento com validação;
− o método registar_desempenho(nota) que diciona uma nota
entre 1 e 5 à lista de avaliações do utilizador.
Crie uma instância Colaborador ("guido van rossum",
"123456789", 1600, "Engenharia"). Imprima, por esta ordem,
o nome, o NIF, o salario base, o departamento e o nome da
empresa. Atualize depois o nome para "Guido van Rossum", o
NIF para "987654321" e o salario base para 1700.0; transfira o
colaborador para "I&D". Por fim, imprima numa única linha o nome, NIF,
salario base, departamento e get_avaliacoes() (que deverá
estar vazio).
Este é um exemplo de saída:
297
− get_*/set_* com validação (bolsa deve ser numérica maior do que
0, nome tem de ser uma string, NIF tem de ser string com exatamente 9
dígitos, salário base numérico tem de ser maior do que 0, o
departamento tem de ser uma string não vazia;
− redefina salario_mensal() para devolver o montante da bolsa;
− o método elegivel_efetivacao() que devolve True se
existirem pelo menos 3 avaliações e se a média das avaliações for igual
ou superior a 4 (use as avaliações herdadas de Colaborador).
Crie uma instância de Estagiario (com nome, NIF, salario base,
departamento e bolsa válidos), registe três avaliações e apresente: o
salário mensal (bolsa) e o resultado de elegibilidade à efetivação.
298
11 BIBLIOTECAS ÚTEIS EM PYTHON
299
atual. Apesar de ser prática em alguns casos, pode tornar o código mais difícil
de compreender por quem não conhece a estrutura do projeto:
from .utils import texto # um nível acima
from ..comum import ajuda # dois níveis acima
Internamente, o Python mantém um cache de módulos em [Link],
carregando cada módulo apenas uma vez durante a execução. Isto torna
reimportações subsequentes instantâneas, pois o módulo é obtido
diretamente da memória em vez de ser relido do ficheiro. No entanto, durante
o desenvolvimento ou em modo interativo (REPL), este comportamento
pode gerar confusão: se o ficheiro do módulo for alterado após o primeiro
import, o Python não aplica automaticamente as alterações.
Para resolver isso sem reiniciar o interpretador, pode-se usar a função
[Link](nome_do_módulo). Essa função força o Python
a ler novamente o ficheiro original e a atualizar o módulo em memória,
garantindo que as modificações feitas no código passem a ter efeito imediato.
Este recurso é especialmente útil durante testes e sessões de desenvolvimento
interativo, permitindo iterar rapidamente sobre o código sem precisar
reiniciar o ambiente.
Um cuidado adicional deve ser tomado com os ciclos de importação, que
ocorrem quando o módulo A importa o módulo B, e B tenta importar
novamente A. Essa situação cria uma dependência circular e normalmente
resulta num AttributeError. Para evitar o problema, pode-se atrasar o
import para dentro de uma função, mover partes comuns para um terceiro
módulo ou reorganizar a estrutura do código de modo a manter dependências
unidirecionais e claras.
300
11.1.1 Criar módulos e pacotes próprios
301
Esse padrão é amplamente usado para criar módulos que funcionam tanto
como bibliotecas quanto como programas executáveis.
Entre as boas práticas de design de código em Python destacam-se:
− manter módulos coesos, com uma responsabilidade bem definida
− utilizar tipagem estática (PEP 484) para tornar o código mais seguro e
legível
− incluir um ficheiro [Link] quando o pacote for distribuído com type
hints
− escrever docstrings claras e concisas, preferencialmente com
exemplos de uso que possam ser testados automaticamente com o
módulo doctest.
Essas práticas tornam o código mais modular, previsível e fácil de integrar
em ambientes de produção ou de investigação científica.
303
Para amostragem probabilística, [Link]() realiza seleções
com reposição e suporta pesos, enquanto [Link]() faz
amostragem sem reposição. A função [Link]() embaralha
listas in-place, sendo eficiente, mas modificando o conteúdo original.
Em grandes conjuntos de dados, recomenda-se gerar valores sob demanda
através de iteradores, economizando memória e tornando o processamento
escalável.
304
11.3 Introdução a NumPy (arrays, vectorização e broadcasting)
11.3.1 Conceitos-chave
305
A criação de arrays pode ser feita de diversas formas. A mais simples é a
partir de uma lista Python, como no exemplo a seguir.
306
As chamadas funções universais (ou ufuncs), como [Link](),
[Link]() e [Link](), aplicam-se a todos os elementos do array de
forma vetorizada.
Reduções ao longo de eixos específicos são feitas com métodos como
mean(), sum(), min(), max() e std(), usando o parâmetro
axis para definir a direção da operação, como no próximo exemplo:
307
Esse mecanismo economiza memória e tempo, mas pode causar surpresas se
o programador não estiver atento.
Para criar uma cópia independente dos dados, deve-se usar o método
copy(), como a seguir:
y = x[2:7].copy()
Além disso, é importante saber que operações de indexação avançada (com
listas, arrays booleanos ou múltiplas dimensões) criam sempre cópias, nunca
views.
Saber essa diferença é crucial para otimizar o desempenho e evitar bugs sutis
em código científico.
309
10_000 == 10000 == 0b10011100010000
310
11.4.1 Estrutura geral e filosofia da biblioteca
311
A saída deste código, que traça a função seno, está apresentada a seguir.
312
A seguir, apresenta-se um exemplo contendo estes tipos de gráficos.
313
b) Exemplo de Gráfico de Dispersão
314
c) Exemplo de Histograma
315
d) Exemplo de Gráfico de Barras
Essas são formas básicas, mas servem como ponto de partida para
composições mais ricas e relatórios visuais complexos.
316
11.4.3 Personalização e estilo
318
3. Simulação probabilística com o módulo random
Crie um programa que simule um sorteio ponderado de itens com
diferentes probabilidades, utilizando [Link]() com
pesos. Execute a simulação milhares de vezes e compare as frequências
observadas com as probabilidades teóricas esperadas, ilustrando
graficamente o resultado com Matplotlib.
319
Desenvolva um relatório visual que apresente, numa única figura,
múltiplos tipos de gráficos:
− uma curva temporal (linha),
− um histograma de distribuição,
− e um gráfico de dispersão entre variáveis.
Utilize o modelo orientado a objetos (fig, ax =
[Link]()) e organize os gráficos em subplots bem rotulados.
Aplique um estilo consistente com rcParams e exporte o resultado em
pelo menos três formatos: PNG, SVG e PDF. Explique como a
personalização visual contribui para a clareza e a comunicação científica.
320
12 DESENVOLVENDO INTERFACES GRÁFICAS COM TKINTER
321
O Tkinter é frequentemente a primeira escolha para quem deseja
desenvolver aplicações desktop em Python por diversas razões:
− Integração nativa: é distribuído com o Python, não exigindo
dependências externas
− Multiplataforma: o mesmo código executa-se, sem alterações, em
Windows, macOS e Linux
− Simplicidade conceitual: os widgets seguem uma hierarquia lógica e
coerente e o modelo de eventos é intuitivo
− Documentação extensa e estabilidade da API: o Tkinter é amplamente
documentado, o que o torna adequado tanto para iniciantes como para
profissionais que necessitam de uma base sólida para aplicações desktop.
Apesar de existirem alternativas mais modernas, como PyQt, PySide ou
Kivy, o Tkinter continua a ser o ponto de partida ideal para compreender
os fundamentos de programação de interfaces em Python.
323
A saída deste código está apresentada a seguir:
324
Ao clicar no botão Sair, a seguinte messagebox aparece no ecrã:
325
A saída deste exemplo é apresentada a seguir.
326
Um erro comum é perder a referência à imagem carregada. Quando o objeto
PhotoImage é recolhido pelo garbage collector, o rótulo deixa de exibir a
imagem. A atribuição label_imagem.image = foto garante que a
imagem permaneça acessível enquanto o label existir.
327
A saída é uma caixa para entrada do nome, conforme a imagem a seguir.
328
Se um nome for digitado, como por exemplo, o nome Maria José, uma caixa
de mensagens é então exibida, após pressionar o botão enviar, conforme esta
imagem:
329
12.4.4 Text (Área de Texto)
330
A saída deste exemplo corresponde ao comportamento esperado de uma área
de texto interativa. Após a executar o programa, surge uma janela contendo
o campo multilinha com scrollbar associada, permitindo deslocar o conteúdo
quando o texto excede o espaço visível. Os botões colocados abaixo da área
possibilitam testar imediatamente todas as operações implementadas, ou
seja, se pressionar “Inserir Texto”, acrescenta-se uma linha de exemplo. Ao
selecionar “Limpar”, o conteúdo é removido integralmente, e através do
botão “Obter Texto”, o utilizador pode visualizar o texto escrito numa caixa
de mensagem, conforme imagens ilustrativas a seguir.
331
Ao clicar em “Inserir Texto”:
332
Também é possível que o próprio utilizador introduza texto na área, como se
observa na imagem seguinte. Como o conteúdo excede o espaço disponível,
o scrollbar vertical (eixo y) torna-se visível para permitir a navegação pelas
várias linhas.
333
Assim, é possível verificar de forma clara como cada funcionalidade atua
sobre o widget Text, e observar que tanto a inserção como a remoção e a
leitura do conteúdo funcionam conforme o esperado, tornando o exemplo
particularmente eficaz para demonstrar o uso deste componente em
Tkinter.
O Text é extremamente versátil, pois pode ser usado para construção de
editores, blocos de notas, consolas de saída ou até mesmo renderização de
texto formatado.
334
Neste exemplo os botões são empilhados verticalmente de acordo com a ordem em
que foram declarados.
b) grid
O gerenciador grid distribui os widgets numa matriz de linhas e colunas,
sendo especialmente útil para formulários e interfaces onde é importante
alinhar rótulos, entradas e botões. Um simples exemplo de uso com grid
está a ser aperesentado a seguir, com a respetiva saída.
335
Cada widget é posicionado numa célula da grelha, facilitando alinhamentos
precisos.
c) place
O gerenciador place posiciona widgets em coordenadas absolutas (x,y)
ou relativas (proporções da janela). Deve ser usado com cuidado,
principalmente quando é necessário que a interface se adapte ao
redimensionamento da janela. A seguir, há um exemplo do uso do place.
336
É importante ressaltar que o pack é a escolha natural para interfaces
simples. O grid é indicado para formulários estruturados e a maioria dos
layouts organizados. Já o place deve ser usado apenas quando é necessária
precisão absoluta na posição dos elementos.
337
O Combobox, disponível através do ttk, fornece uma lista suspensa com
valores pré-definidos, sendo ideal para opções controladas, como seleção de
categorias ou configurações. O trecho de código deste exemplo com a sua
respetiva saída, sendo a primeira imagem com a lista suspensa fechada e a
segunda com a lista suspensa após ser clicada.
338
Estes widgets suportam eventos específicos: <<ListboxSelect>>,
<<ComboboxSelected>> e <<TreeviewSelect>>, que permitem
reagir quando o utilizador altera uma seleção. Dessa forma, é possível criar
interfaces de navegação, consulta e gestão de dados com poucas linhas de
código e grande flexibilidade.
339
Neste exemplo, ao clicar em uma das opções, esta fica marcada em azul. A
saída apresentada mostra a seleção da Opção B.
340
Um exemplo simples é o Listbox, onde o evento <<ListboxSelect>>
permite detetar imediatamente quando o utilizador escolhe um item. No
trecho anterior, sempre que ocorre uma seleção, o programa obtém o valor
correspondente e apresenta-o no terminal. Este é o caso mais compacto para
ilustrar como Tkinter reage a eventos de seleção com poucas linhas de
código. Neste exemplo, exibe-se um Menu denominado Ficheiro, que ao
ser clicado, abre as opções de Abrir e Sair.
Cuja saída é:
341
avisos, erros, mensagens informativas ou pedidos de confirmação, sendo
uma forma rápida de comunicar o estado de uma operação.
O filedialog permite selecionar ficheiros ou escolher um local para
guardar informação, recorrendo à janela nativa do sistema operativo, o que
garante familiaridade e consistência visual.
Por fim, o colorchooser disponibiliza um seletor de cores intuitivo, útil
em programas que necessitam de personalização visual. Estas caixas de
diálogo tornam a interface mais completa e reduzem significativamente o
esforço de desenvolvimento, ao disponibilizar soluções prontas para algumas
das interações mais comuns numa aplicação.
Estas caixas de diálogo encapsulam funcionalidades complexas e oferecem
um comportamento consistente (visualmente e funcionalmente) em
diferentes sistemas operativos, poupando tempo ao programador e reduzindo
a probabilidade de erros. São componentes fundamentais para criar
aplicações que interajam de forma natural com o utilizador.
342
Este exemplo mostra como os principais elementos do Tkinter podem ser
combinados numa aplicação funcional. A área de texto, acompanhada por
uma scrollbar, permite editar conteúdo de forma cómoda, enquanto o menu
“Ficheiro” reúne as opções essenciais de abertura e gravação de
documentos. As funções associadas a cada ação demonstram a leitura e
escrita de ficheiros, além do uso das caixas de diálogo nativas para selecionar
o destino do conteúdo. Apesar de simples, o exemplo ilustra claramente
343
como estruturar uma aplicação completa e serve como base para versões
mais avançadas, propostas nos exercícios.
12.10 Conclusão
345
b) Área de edição - Um widget Text com suporte a scrollbars vertical e
horizontal, configurado para permitir edição fluida de texto, incluindo as
funcionalidades de undo e redo
c) Menus - Uma barra de menus contendo, no mínimo, os seguintes grupos:
− Ficheiro: opções de Novo, Abrir, Guardar, Guardar Como e Sair
− Editar: funcionalidades de Cortar, Copiar, Colar, Desfazer e
Refazer.
d) Gestão de ficheiros - Implementação da abertura e gravação de ficheiros
através do filedialog, garantindo tratamento apropriado de
exceções e preservação do caminho do ficheiro atual.
e) Atalhos de teclado - As ações mais importantes devem ser acessíveis por
atalhos (por exemplo, Ctrl+N, Ctrl+O, Ctrl+S, Ctrl+Z,
Ctrl+Y, entre outros).
f) Barra de estado - Um rótulo na parte inferior da janela que apresente
mensagens contextuais ao utilizador, tais como operações concluídas,
ficheiros carregados, ou erros encontrados.
g) Boas práticas de organização - O programa deve apresentar uma
estrutura clara, com funções bem definidas para cada tarefa, facilitando
a leitura e manutenção do código.
Acrescente funcionalidades adicionais, caso deseje, como contagem de
palavras, destaque de linha atual, alteração do tamanho do tipo de letra, ou
confirmação de saída quando existir texto não guardado. O importante é
demonstrar domínio dos conceitos fundamentais do Tkinter, bem como
capacidade de estruturar uma aplicação completa e coerente.
346
13 DO DATA MINING AO DASHBOARD
347
de Dados (EDA), examinando estrutura, valores, padrões e possíveis
problemas do conjunto de dados. A seguir corrigem‑se erros e incoerências,
como valores em falta, duplicados, categorias escritas de forma inconsistente
ou tipos incorretos. Só então é sensato calcular indicadores, métricas e
estatísticas de negócio. Finalmente, apresenta‑se tudo de forma visual e
interativa num dashboard para apoio à decisão.
Esta abordagem passo a passo não é apenas uma opção, mas uma forma de
garantir qualidade. Sem compreender o estado real dos dados não é possível
confiar nas conclusões. Sem limpar adequadamente os dados, as métricas
podem ficar enviesadas (distorcidas) por erros simples. Sem visualização,
padrões importantes passam despercebidos. E sem comunicar resultados de
forma clara, mesmo a melhor análise perde valor. Ao final deste capítulo
espera-se que sejas capaz de percorrer todo este processo com autonomia e
rigor técnico.
348
Uma boa prática recorrente é encapsular a lógica de importação numa classe.
Por exemplo, uma classe DataLoader pode receber o caminho do ficheiro
e expor um método que devolve o DataFrame carregado.
O mesmo princípio aplica‑se para dados vindos de uma API: cria‑se uma
classe que sabe construir o pedido, autenticar‑se, receber a resposta em
formato JSON e convertê‑la para DataFrame. Esta organização dá‑nos
reutilização e clareza. Ao longo das secções seguintes apresenta-se ambos os
casos.
349
Neste exemplo define-se uma classe chamada DataLoader que recebe o
caminho de um ficheiro CSV quando é criada, guarda esse caminho
internamente e disponibiliza um método carregar_csv() que usa
pandas para ler o ficheiro (pd.read_csv), devolver os dados sob a
forma de um DataFrame e armazenar esse DataFrame no próprio objeto
para reutilização.
351
obter a lista de filmes populares e convertê-la diretamente para um
DataFrame do pandas.
352
Neste exemplo criou-se um objeto DataLoaderAPI utilizando
“CHAVE_API” (que deve ser substituída pela sua própria chave gerada após
criar conta e pedir uma API key), pediu-se à API a lista de filmes populares
em inglês (lingua="en-US"), extraiu-se o título (title) e o ano
(release_date para construir um DataFrame apenas com "Title" e
"Year". Por fim, mostra-se as primeiras linhas com head().
Um exemplo de saída está apresentado a seguir:
Note que esta classe pode facilmente ganhar novas funcionalidades, como
um método que filtra filmes por género, por ano de lançamento ou por
avaliação média. A ideia é que o programador não precise reescrever o
acesso à API sempre que surja uma nova pergunta. Basta criar um novo
método dentro desta classe e reutilizar a mesma infraestrutura de
autenticação e conversão em DataFrame.
Há situações em que a informação necessária ainda não está disponível em
formato de ficheiro ou API. Nesses casos, recorre‑se frequentemente a
técnicas de extração automática de páginas web, conhecidas como web
scraping. A ideia é descarregar o conteúdo HTML de uma página e procurar
dentro desse conteúdo os elementos que interessam (tabelas, títulos, preços
etc.).
353
Em Python, bibliotecas como requests (para obter o HTML) e
BeautifulSoup ou Scrapy (para analisar e extrair partes específicas)
são tipicamente utilizadas.
Apesar de muito útil, o scraping exige responsabilidade legal e ética. É
essencial confirmar se o sítio autoriza a recolha automática de dados e
respeitar limites de acesso. Além disso, os dados extraídos devem ser
tratados com o mesmo cuidado aplicado aos restantes métodos de
importação: precisam de ser limpos, interpretados e convertidos para uma
estrutura tabular consistente, de preferência um DataFrame do pandas.
Neste capítulo, o foco recai apenas sobre fontes que disponibilizam dados de
forma explícita (ficheiros, APIs), mas é importante saber que o scraping
representa outra porta de entrada muito comum no trabalho com dados.
354
experiências isoladas para aplicações que precisam de ser mantidas ao longo
do tempo.
Outro benefício desta organização modular é a reutilização. Caso se pretenda
analisar um dataset diferente (por exemplo, outro conjunto de vendas ou
dados de clientes), grande parte do código de Eda e
DataVisualization permanece válida e funcional. Basta fornecer um
novo DataFrame carregado por DataLoader e executar novamente os
métodos. Esta forma de estruturar o projeto reduz esforço repetitivo e
incentiva boas práticas de engenharia de software.
355
garante-se duas coisas: primeiro, a análise exploratória fica repetível,
segundo, que o relatório inicial de qualidade dos dados pode ser mostrado
rapidamente a outras pessoas da equipa sem exigir que elas leiam todo o
código.
356
especial o DataFrame, que representa uma tabela com linhas e colunas
(semelhante a uma folha de cálculo), mas com capacidade de filtragem,
agregação, ordenação, limpeza e cálculo estatístico de forma programática e
repetível.
A Tabela 28 resume os principais métodos e atributos da biblioteca pandas.
357
Método / Atributo O que faz
Mostra um resumo técnico: tipos de
[Link]() coluna, nº de valores não nulos e uso
de memória
358
Método / Atributo O que faz
Mostra um resumo técnico: tipos de
[Link]() coluna, nº de valores não nulos e uso
de memória
359
Neste exemplo definiu-se uma classe chamada Eda, cujo objetivo é gerar
um relatório inicial de Análise Exploratória de Dados (EDA) a partir de um
DataFrame do pandas.
O construtor __init__ recebe o DataFrame original (data_frame) e
guarda-o internamente em self.__df. O método
analisar_estrutura() recolhe um conjunto de indicadores
descritivos sobre esse DataFrame e devolve tudo num dicionário chamado
relatorio.
Esse dicionário inclui: as dimensões do dataset (linhas e colunas:
self.__df.shape), as primeiras linhas (self.__df.head()), a lista
dos nomes das colunas (list(self.__df.columns)), o número de
valores ausentes por coluna (self.__df.isnull().sum()), o tipo de
dados de cada coluna (self.__df.dtypes), o número de registos
duplicados (self.__df.duplicated().sum(), convertido em
inteiro) e as estatísticas descritivas das colunas numéricas
(self.__df.describe()).
Métricas como média, mediana, mínimo, máximo e desvio padrão ajudam a
perceber o comportamento típico e a variabilidade dos dados.
O próprio pandas oferece muitas dessas medidas automaticamente através
do método .describe(). Ao observar estes valores pode-se rapidamente
detetar situações anómalas, tais como números impossíveis (por exemplo,
preços negativos) ou amplitudes excessivamente grandes que sugerem erros
de registo.
360
Estas estatísticas são também cruciais para preparar a fase de visualização.
Se uma variável tem valores muito concentrados num intervalo pequeno e
alguns valores extraordinariamente altos, talvez seja necessário utilizar
escalas logarítmicas ou destacar explicitamente os casos extremos. Se duas
colunas numéricas têm médias e desvios parecidos, pode haver uma relação
interessante para explorar. O segredo está em usar estas medidas como um
primeiro radar que orienta as perguntas seguintes.
No final, analisar_estrutura() devolve este dicionário
relatorio, que pode depois ser usado para gerar um relatório formatado
ou para alimentar outras partes da aplicação. Para utilizar a classe Eda, é
preciso primeiro importar os dados. Neste exemplo, isso é feito através de
um objeto da classe DataLoader, definida anteriormente.
361
A seguir é apresentado o relatório gerado a partir do dicionário devolvido pela classe
Eda, utilizando um ficheiro CSV ([Link]) previamente carregado através do
DataLoader. Este ficheiro encontra-se na mesma pasta dos restantes ficheiros do
projeto, mas poderia ser substituído por qualquer outro CSV com a mesma lógica.
362
13.2.2 Deteção de Outliers
Outliers são valores que se afastam fortemente do padrão geral dos dados.
Entretanto, outliers podem ser registos legítimos, como por exemplo, uma
venda excecionalmente alta em época festiva, ou podem resultar de erros de
introdução, como por exemplo, escrever 10000 em vez de 100.
Identificar outliers é importante pois eles podem distorcer médias, gerar
gráficos enganosos e levar a conclusões incorretas.
363
Na prática, a identificação pode ser feita com boxplots, análise de quartis ou
regras como o IQR (Interquartile Range).
Encontrar um outlier não significa eliminá‑lo automaticamente. É preciso
interpretar o contexto de negócio, pois um valor extremo pode representar
uma venda VIP que interessa manter. Por outro lado, pode ser apenas ruído
que deve ser corrigido ou removido para não contaminar as métricas. O mais
importante é registar claramente as decisões tomadas. A limpeza de dados
não é só técnica, é também documental, pois afeta diretamente a confiança
que os outros terão nos resultados apresentados.
364
365
13.2.3 Correlação entre variáveis
367
13.3 Limpeza de Dados
368
É também essencial a limpeza é tratar registos duplicados. Linhas repetidas
podem inflacionar totais e enviesar estatísticas, especialmente em métricas
de volume de vendas ou número de pedidos. No pandas, métodos como
.duplicated() e .drop_duplicates() permitem identificar e
remover linhas iguais. Antes de apagar, confirme se a duplicação é realmente
erro e não apenas o reflexo de duas transações legítimas muito semelhantes.
Também é comum encontrar inconsistências de escrita e de formatação. Um
mesmo centro comercial pode aparecer registado como "Shopping Norte" e
"shopping norte"; um método de pagamento pode surgir como "Cartão" num
registo e "cartao" noutro. Estas diferenças impedem que o agrupamento dos
dados funcione corretamente, porque o computador entende cada variação
como uma categoria diferente. Parte do processo de limpeza consiste em
normalizar estas categorias, padronizando maiúsculas/minúsculas,
corrigindo erros ortográficos óbvios e unificando abreviações.
Para além de valores ausentes e duplicados, é crucial garantir que cada
coluna tem o tipo correto. Datas devem ser interpretadas como datas,
números como números, e assim sucessivamente. Uma coluna que
aparentemente contém preços pode ter sido lida como texto devido ao uso de
vírgulas ou símbolos monetários. Converter explicitamente o tipo (por
exemplo usando pandas.to_datetime para datas ou
pandas.to_numeric para valores numéricos) evita surpresas ao calcular
estatísticas ou ao gerar gráficos cronológicos.
Em certos casos, especialmente quando se pretende treinar modelos
estatísticos ou comparar variáveis em escalas diferentes, aplica‑se
normalização ou padronização. Normalizar significa redimensionar os
369
valores para uma faixa comum (por exemplo, entre 0 e 1); padronizar
significa ajustar uma variável para ter média 0 e desvio padrão 1. Estas
transformações reduzem o impacto de variáveis que, apenas por terem
unidades grandes, dominariam visualizações ou cálculos automáticos.
Após limpar os dados, começa a fase de fazer perguntas úteis. Em contexto
de vendas ao público, algumas perguntas clássicas são: Qual o centro
comercial com maior faturação total? Que categoria de produto gera mais
receita? Clientes do género masculino ou feminino gastam mais por compra?
Estas perguntas transformam o DataFrame num relatório de gestão.
Note que cada pergunta corresponde, a uma agregação: agrupar por loja e
somar total de vendas, agrupar por categoria e contar número de itens
vendidos, agrupar por género do cliente e calcular o ticket médio. É
exatamente este tipo de raciocínio que um dashboard precisa apresentar de
forma imediata, sem obrigar o utilizador final a escrever código. Ao desenhar
o painel deve-se ter a certeza de que estas perguntas aparecem respondidas
logo nos primeiros elementos visuais.
No final desta fase, a limpeza dos dados pode (e deve) ser formalizada numa
classe própria, por exemplo DataCleaner, com todas as operações de
preparação do dataset antes da análise. A ideia é separar
responsabilidades: enquanto a classe DataLoader importa os dados e a
classe Eda descreve e avalia a qualidade desses dados (dimensão, nulos,
duplicados, outliers, correlação), a classe DataCleaner é a responsável
por aplicar as correções necessárias de forma consistente e repetível.
370
371
13.4 Visualização de Dados e Tomada de Decisão
372
(títulos, secções, layout em colunas), (ii) disponibilizar controlos de
filtragem ao utilizador (listas, sliders etc.), e (iii) apresentar resultados de
negócio de forma clara (métricas, tabelas e gráficos). A Tabela 29 resume os
métodos mais comuns do Streamlit utilizados neste tipo de dashboard e
o papel de cada um.
373
Método Streamlit Função principal
374
375
376
Este exemplo lê o ficheiro [Link], calcula métricas básicas de
negócio (receita total, número de transações e ticket médio) e mostra essas
métricas juntamente com um gráfico de receita por categoria num pequeno
dashboard feito em Streamlit, que pode ser lançado diretamente em
Python sem usar a linha de comandos, conforme este exemplo de saída:
377
13.5 Exercícios Propostos
378
deve comparar os resultados com uma base de dados de vulnerabilidades
conhecidas e exportar um relatório detalhado em formato HTML. O
programa deve:
− Ler ficheiros de configuração em diferentes formatos
− Detetar padrões de risco (passwords, permissões fracas)
− Validar com base de vulnerabilidades predefinida
− Exportar relatório estruturado em HTML
379
14 APLICAÇÕES DE PYTHON EM CIBERSEGURANÇA
• 14.1 Introdução
A cibersegurança tornou-se um dos pilares essenciais da computação
moderna. Com o crescimento exponencial das redes, da Internet das Coisas
e dos serviços digitais, proteger a informação é tão importante quanto
produzi-la. A linguagem Python, pela sua clareza, simplicidade e vasto
ecossistema de bibliotecas, é hoje uma das ferramentas mais utilizadas no
desenvolvimento de soluções de segurança, auditoria, deteção e automação.
O Python é usado tanto em equipas de defesa (blue teams), os
responsáveis por monitorar e proteger, como em equipas de teste e auditoria
(red teams), que simulam ataques de forma controlada e ética, com o
objetivo de avaliar a robustez dos sistemas.
Este capítulo apresenta como a linguagem pode ser aplicada a tarefas
fundamentais de cibersegurança, desde a criação de senhas seguras até à
encriptação de dados e à análise de tráfego de rede.
A abordagem será progressiva. Inicia-se com conceitos básicos de segurança
digital e manipulação segura de dados, avançando depois para técnicas de
criptografia, comunicação segura e análise de rede.
Os exemplos apresentados são ilustrações práticas de defesa e
monitorização. Devem ser utilizados exclusivamente em ambientes
controlados e autorizados.
380
14.1 Fundamentos da Segurança da Informação
381
O código a seguir gera uma senha aleatória, combinando letras, números e
símbolos. O módulo secrets garante aleatoriedade imprevisível,
adequada para gerar senhas, chaves de API, tokens de autenticação e outros
segredos digitais.
382
garantir que o conteúdo não foi alterado. O procedimento consiste em ler o
ficheiro em blocos, atualizar progressivamente o algoritmo de hashing e, por
fim, obter o valor hexadecimal correspondente. Este tipo de abordagem é
essencial quando se trabalham ficheiros grandes, uma vez que evita carregar
todo o conteúdo na memória de uma só vez.
384
sequência parece totalmente aleatória, o que é desejável em criptografia
moderna. Após a desencriptação, obtém-se novamente o texto original,
demonstrando a simetria do processo.
É importante notar que a mensagem cifrada será diferente em cada execução,
mesmo que o texto original seja o mesmo. Isto acontece porque o Fernet
incorpora informação aleatória a cada operação de encriptação, contribuindo
para reforçar a segurança.
Este método é ideal para proteger ficheiros locais, configurações internas ou
pequenas bases de dados. Contudo, exige especial cuidado no
armazenamento da chave, já que qualquer pessoa que obtenha acesso a esse
ficheiro poderá desencriptar a informação protegida. Por isso, é
recomendável guardar a chave em diretórios restritos ou utilizar mecanismos
adicionais de proteção.
385
No exemplo seguinte apresenta-se o processo fundamental da criptografia
assimétrica através da geração de um par de chaves RSA e da subsequente
encriptação e desencriptação de uma mensagem. A chave privada é criada
localmente e, a partir dela, obtém-se a chave pública correspondente. Utiliza-
se então a chave pública para encriptar a mensagem, garantindo que apenas
o detentor da chave privada poderá recuperá-la. Este procedimento ilustra o
princípio essencial da criptografia de chave pública e mostra, de forma
simples, como o Python permite implementar este mecanismo de segurança
de forma robusta.
386
mensagem. Ao assinar digitalmente um documento com a chave privada, o
emissor garante que o conteúdo não foi alterado e que a sua origem pode ser
verificada através da chave pública correspondente.
Este princípio é utilizado diariamente em sistemas como certificados digitais,
assinaturas eletrónicas e mecanismos de autenticação em dois fatores,
constituindo uma base essencial da segurança moderna.
O exemplo seguinte mostra como se pode gerar uma assinatura digital e a
verificar utilizando RSA, um algoritmo de criptografia assimétrica
apresentado em 1977. O algoritmo baseia-se na dificuldade computacional
de fatorizar números inteiros muito grandes, construídos a partir do produto
de dois números primos.
387
Neste exemplo, a mensagem é assinada com a chave privada e verificada
com a chave pública, demonstrando o funcionamento básico da assinatura
digital.
388
A opção verify=True garante que o certificado SSL apresentado pelo
servidor é validado antes de a ligação ser mantida. Esta verificação impede
ataques de man-in-the-middle, nos quais um atacante tenta interpor-se na
comunicação para capturar ou alterar dados. Caso o certificado não seja
fiável, o pedido é interrompido, preservando a integridade da comunicação.
389
O paramiko permite estabelecer uma sessão encriptada, autenticar o
utilizador e executar comandos de forma remota, substituindo com segurança
ferramentas antigas como o Telnet, que transmitiam credenciais e dados em
texto simples. Este tipo de automação é especialmente útil em ambientes de
administração de sistemas, permitindo monitorizar servidores, recolher
informações de diagnóstico ou executar atualizações programadas.
390
portas. A técnica consiste em tentar estabelecer ligações sucessivas a várias
portas de um determinado host. Se a ligação for aceite, a porta é considerada
aberta.
O exemplo seguinte apresenta um varrimento simples.
b) Utilização de scapy
Para além da varredura de portas, a análise de pacotes é uma técnica essencial
para compreender o comportamento de uma rede. A biblioteca scapy
permite capturar, gerar e analisar pacotes de baixo nível, tornando-se uma
ferramenta muito poderosa para diagnóstico, deteção de intrusões e
investigação em segurança.
O exemplo seguinte captura um pequeno número de pacotes e apresenta um
resumo de cada um, entretanto, antes do exemplo, ressalta-se que a captura
391
de pacotes com o Scapy depende do suporte do sistema operativo para
acesso de baixo nível à interface de rede. Esta funcionalidade está totalmente
disponível em sistemas Linux e macOS, onde a biblioteca opera sem
necessidade de componentes adicionais, permitindo o uso normal da função
sniff() e de outras operações de análise de pacotes.
No Windows, contudo, esse suporte não é nativo. Para que o Scapy consiga
capturar ou enviar pacotes ao nível da camada de enlace (camada 2), é
necessário instalar o componente Npcap, disponível no site oficial do Nmap.
Sem este componente, o Scapy devolve um erro indicando que não é
possível efetuar a captura a nível 2. Alternativamente, é possível recorrer ao
modo de captura ao nível 3 (camada de rede), que funciona sem
dependências externas, mas oferece capacidades reduzidas e não permite
observar todo o tráfego da interface.
Assim, recomenda-se a utilização de Linux ou macOS quando se pretende
explorar todo o potencial do Scapy, como neste exemplo.
Com apenas estas linhas, é possível observar tráfego real circulando pela
interface de rede. Cada pacote capturado é analisado e resumido, permitindo
ao programador identificar padrões, tipos de protocolo ou comportamentos
inesperados. A partir deste ponto, é possível evoluir para funcionalidades
mais complexas, tais como filtragem seletiva, deteção de pacotes suspeitos
ou análise temporal do tráfego.
392
Este tipo de abordagem constitui a base conceptual dos sistemas de deteção
de intrusões (IDS), que dependem de monitorização contínua e análise
criteriosa do fluxo de dados para identificar atividades anómalas.
393
das ferramentas abordadas neste capítulo é um passo essencial para garantir
que o conhecimento técnico é aplicado de forma responsável.
394
15 RESPOSTAS
15.1 CAPÍTULO 2
1)
a = 10
b = 3.5
# a) a + b
resultado1 = a + b
print("a) a + b =")
print(resultado1)
print("Tipo:")
print(type(resultado1))
# b) a * b
resultado2 = a * b
print("b) a * b =")
print(resultado2)
print("Tipo:")
print(type(resultado2))
# c) a / b
resultado3 = a / b
print("c) a / b =")
print(resultado3)
print("Tipo:")
print(type(resultado3))
2)
nome = "Paulo"
apelido = "Marques"
395
# Mostrar o resultado
print(nome_completo)
3)
numero_str = "15"
4)
x = 10
y = 20
5)
TAXA_JUROS = 0.05
valor = 1000
juro = valor * TAXA_JUROS
print(juro)
396
15.2 CAPÍTULO 3
1)
# Cálculo da expressão
resultado = 10 + 3 * 2 2 / 4 - 1
print(resultado)
2)
# Cálculo da expressão
a = 5
b = 10
c = 15
# a) a < b < c
resultado_a = a < b < c
print("a) a < b < c:")
print(resultado_a)
# b) a + b == c
resultado_b = a + b == c
print("b) a + b == c:")
print(resultado_b)
# c) c % a != 0
resultado_c = c % a != 0
print("c) c % a != 0:")
print(resultado_c)
397
3)
idade = 18
tem_cartao = True
4)
lista1 = ["cpu", "pc", "ram"]
lista2 = ["cpu", "pc", "ram"]
5)
fruta = "banana"
398
15.3 CAPÍTULO 4
1)
# Solicitar nome e cidade
nome = input("Qual é o teu nome? ")
cidade = input("De que cidade vens? ")
2)
# Ler horas
horas = float(input("Introduzir o número de horas: "))
# Converter
minutos = horas * 60
segundos = horas * 3600
# Mostrar resultados
print(f"{horas} horas = {minutos:.0f} minutos =
{segundos:.0f} segundos")
3)
# Ler dados do produto
produto = input("Nome do produto: ")
preco_unitario = float(input("Preço unitário (€): "))
quantidade = int(input("Quantidade: "))
# Calcular valores
total_sem_iva = preco_unitario * quantidade
iva = total_sem_iva * 0.23
total_com_iva = total_sem_iva + iva
399
4)
# Ler número de 6 dígitos
numero = int(input("Digitar um número de 6 dígitos: "))
# Diferentes formatações
print(f"Com separadores: {numero:,}")
print(f"Como moeda: {numero:.2f} €")
print(f"Com zeros: {numero:010d}")
print(f"Em hexadecimal: {numero:x}")
5)
# Solicita as três palavras ao usuário
palavra1 = input("Digite a primeira palavra: ").strip()
palavra2 = input("Digite a segunda palavra: ").strip()
palavra3 = input("Digite a terceira palavra: ").strip()
6)
# Ler resultados do teste
corretas = int(input("Número de respostas corretas: "))
total = int(input("Total de perguntas: "))
# Calcular percentagem
percentagem = (corretas / total) * 100
# Mostrar resultado
print(f"Acertaste {corretas} de {total} questões
({percentagem:.1f}%)")
400
15.4 CAPÍTULO 5
1)
numero = int(input("Digite um numero: "))
# Método tradicional
if numero % 2 == 0:
tipo = "par"
else:
tipo = "ímpar"
print(f"O número {numero} é {tipo}")
2)
import random
# Gerar número aleatório entre 1 e 100
numero_secreto = [Link](1, 100)
tentativas = 0
print("�
� Bem-vindo ao Jogo de Adivinhação!")
print("Tente adivinhar o número entre 1 e 100")
print("-" * 40)
401
print("�
� Dica: Tente um número MENOR!")
else:
print(f"�
� PARABÉNS! Acertaste em
{tentativas} tentativas!")
acertou = True # Termina o ciclo
except ValueError:
print("� Erro: Digite um número válido!")
3)
# Pedir texto ao utilizador
texto = input("Introduza uma palavra ou frase: ")
402
# Mostrar resultado
print(f"O texto tem {contador_vogais} vogais.")
# Processamento
texto_minusculo = texto_utilizador.lower()
total_vogais = 0
403
for caractere in texto_minusculo:
# Verificar cada tipo de vogal separadamente
if caractere in ['a', 'á', 'à', 'â', 'ã']:
total_vogais += 1
elif caractere in ['e', 'é', 'è', 'ê']:
total_vogais += 1
elif caractere in ['i', 'í', 'ì', 'î']:
total_vogais += 1
elif caractere in ['o', 'ó', 'ò', 'ô', 'õ']:
total_vogais += 1
elif caractere in ['u', 'ú', 'ù', 'û']:
total_vogais += 1
print("-" * 40)
print(f"Texto analisado: {texto_utilizador}")
print(f"Total de vogais encontradas: {total_vogais}")
4)
# Versão básica
print("Contagem regressiva para lançamento:")
if minuto > 0:
print(f"Faltam {minuto} minutos!")
print("LANÇAMENTO!")
404
print(f"T-{minuto}:{seg_str}")
[Link](1) # Pausa de 1 segundo
if minuto > 0:
print(f"Faltam {minuto} minutos!")
[Link](2) # Pausa extra entre minutos
print("LANÇAMENTO!")
5)
while True:
try:
numerador = int(input("Numerador: "))
denominador = int(input("Denominador: "))
resultado = numerador / denominador
print(f"Resultado: {resultado}")
break # termina o ciclo se tudo correr bem
except ValueError:
print("� Erro: Digite números válidos!")
except ZeroDivisionError:
print("� Erro: Impossível dividir por zero!")
6)
try:
series = int(input("Quantas séries quer fazer? "))
repeticoes = int(input("Quantas repetições por
série? "))
# Validar os valores
if series <= 0 or repeticoes <= 0:
print("Por favor, insira valores maiores que
zero!")
else:
print(f"\n�
� INICIAR TREINO: {series} séries de
{repeticoes} repetições!")
print("=" * 50)
405
# Simular cada série
serie = 1
while serie <= series:
print(f"\nSÉRIE {serie}:")
# Simular cada repetição da série atual
repeticao = 1
while repeticao <= repeticoes:
print(f" Repetição
{repeticao}/{repeticoes}")
repeticao += 1
serie += 1
# Mensagem final de incentivo
print("\n" + "=" * 50)
print("�
� PARABÉNS! Completaste o treino!")
print("�
� Continue! Cada treino te torna mais
forte!")
print("�
� És incrível! Até o próximo treino!")
except ValueError:
print("Erro: Inserir apenas números inteiros!")
7)
print("�
� PROGRAMA DE DIVISÃO DE NÚMEROS INTEIROS")
print("=" * 40)
try:
# Ler o primeiro número
num1 = int(input("Digite 1º número (dividendo): "))
406
# Verificar se o divisor é zero
if num2 == 0:
print("� ERRO: Impossível dividir por zero!")
else:
# Realizar a divisão
resultado = num1 / num2
print(f"�
� Resultado da divisão: {num1} ÷
{num2} = {resultado}")
except ValueError:
print("� ERRO: Digite apenas números inteiros!")
except Exception as e:
print(f"� Ocorreu um erro inesperado: {e}")
8)
import random
print("�
� SIMULADOR DE DADOS COM ESTATÍSTICAS")
print("=" * 40)
try:
num_lancamentos = int(input("Quantas vezes queres
lançar o dado? "))
if num_lancamentos <= 0:
print("� O número deve ser maior que zero!")
else:
# Inicializar contadores
contadores = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6:
0}
soma_total = 0
# Simular lançamentos
for i in range(1, num_lancamentos + 1):
resultado = [Link](1, 6)
407
contadores[resultado] += 1
soma_total += resultado
# Calcular estatísticas
media = soma_total / num_lancamentos
# Mostrar resultados
print("\n" + "=" * 40)
print("�
� ESTATÍSTICAS:")
print(f"Total: {soma_total}")
print(f"Média: {media:.2f}")
print(f"Valor mais frequente:
{mais_frequente}")
print("\n�
� DISTRIBUIÇÃO:")
for face, quantidade in [Link]():
percentual = (quantidade / num_lancamentos)
* 100
print(f"Face {face}: {quantidade} vezes
({percentual:.1f}%)")
except ValueError:
print("� Erro: Digitar um número inteiro válido!")
9)
print("�
� GERADOR DE PADRÕES NUMÉRICOS")
print("=" * 40)
while True:
# Menu de opções
print("\nEscolha o tipo de padrão:")
print("1 - Triângulo numérico")
408
print("2 - Tabuada personalizada")
print("3 - Sequência de Fibonacci")
print("4 - Números primos")
print("0 - Sair")
try:
opcao = input("Sua escolha (0-4): ")
if opcao == "1":
# Triângulo numérico
altura = int(input("\nAltura do triângulo:
"))
print("\n� Triângulo Numérico:")
for i in range(1, altura + 1):
for j in range(1, i + 1):
print(j, end=" ")
print()
409
limite = int(input("\nAté que número
verificar primos? "))
print(f"\n�
� Números primos até {limite}:")
for num in range(2, limite + 1):
primo = True
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
primo = False
break
if primo:
print(num, end=" ")
print()
else:
print("� Opção inválida!")
except ValueError:
print("� Digite um número válido!")
except KeyboardInterrupt:
print("\n\n�
� Programa interrompido!")
break # garante saída também no Ctrl+C
410
15.5 CAPÍTULO 6
1)
def celsius_para_fahrenheit(celsius):
"""Converte temperatura de Cº para Fahrenheit"""
return (celsius * 9/5) + 32
def fahrenheit_para_celsius(fahrenheit):
"""Converte temperatura de Fahrenheit para Cº"""
return (fahrenheit - 32) * 5/9
else:
# Se não especificou a unidade, assumir Celsius
try:
celsius = float(entrada)
fahrenheit = celsius_para_fahrenheit(celsius)
print(f"Assumindo Celsius: {celsius}°C =
{fahrenheit:.1f}°F")
except ValueError:
print("Entrada inválida. Digite no formato: 25C
ou 77F")
411
2)
def verificar_password(password):
if len(password) < 8:
return False
tem_maiuscula = False
tem_minuscula = False
tem_numero = False
for c in password:
if [Link]():
tem_maiuscula = True
if [Link]():
tem_minuscula = True
if [Link]():
tem_numero = True
# Programa principal
print("===== Verificação de Palavra-Passe =====")
print("Requisitos mínimos:")
print("1) Ter pelo menos 8 caracteres")
print("2) Conter pelo menos uma letra maiúscula")
print("3) Conter pelo menos uma letra minúscula")
print("4) Conter pelo menos um número")
print("========================================")
if verificar_password(palavra):
print("Palavra-passe válida")
else:
print("Palavra-passe inválida")
3)
from datetime import datetime
412
def gerar_saudacao(nome):
hora_atual = [Link]().hour
print(gerar_saudacao(input("Nome: ")))
# OU em 2 linhas:
# nome = input("Nome: ")
# print(gerar_saudacao(nome))
4)
def main():
acumulado = 0.0
while True:
nome = input("Nome do produto (ou 'sair'):
").strip()
if [Link]() == "sair":
break
try:
preco = float(input("Preço (€):
").replace(",", "."))
quantidade = int(input("Quantidade: "))
except ValueError:
print("Dados inválidos. Tente de novo.\n")
continue
413
print() # linha em branco antes do relatório
acumulado += gerar_relatorio_produto(nome,
preco, quantidade)
print() # linha em branco depois do relatório
if __name__ == "__main__":
main()
5)
# Programa principal
try:
num1 = int(input("Digite o primeiro número: "))
num2 = int(input("Digite o segundo número: "))
414
print(f"Divisão: {num1} / {num2} = {dividir(num1,
num2)}")
except ValueError:
print("� Erro: Digite apenas números inteiros
válidos!")
6)
import random
def gerar_numero_secreto():
return [Link](1, 100)
def obter_palpite():
while True:
s = input("Palpite (1..100): ").strip()
try:
n = int(s)
if 1 <= n <= 100:
return n
except ValueError:
pass
print("Entrada inválida. Digite um inteiro
entre 1 e 100.")
def jogar_adivinhacao():
secreto = gerar_numero_secreto()
tentativas = 10
415
palpite = obter_palpite()
dica = dar_dica(palpite, secreto)
if dica == "ACERTOU!":
print("Parabéns! Acertou o número.")
return
else:
print(dica)
if __name__ == "__main__":
jogar_adivinhacao()
416
15.6 CAPÍTULO 7
1)
2)
# criar dados
jogadores = [
{"user": "mariana", "desafios": 12},
{"user": "rui", "desafios": 7},
{"user": "maria", "desafios": 19},
{"user": "luis", "desafios": 14},
]
3)
# Criar lista de compras inteligente
def lista_compras_inteligente():
lista = []
while True:
produto = input("Digite um produto (ou 'fim'
para terminar): ")
if [Link]() == 'fim':
break
[Link](produto)
417
# Remover duplicados usando set e ordenar
lista_sem_duplicados = sorted(set(lista))
# Executar
lista_compras_inteligente()
4)
agenda = [
("2025-10-28", "09:30", "Consulta Médica de
Família"),
("2025-11-27", "14:00", "Dentista"),
("2025-11-21", "10:00", "Consultoria Python"),
]
5)
# Processar dados de sensores
def processar_dados_iot():
# Matriz de temperaturas (cada linha = um sensor,
cada coluna = uma hora)
temperaturas = [
[22.5, 23.1, 24.0, 25.2, 26.1], # Sensor 1
[21.8, 22.5, 23.3, 24.0, 24.8], # Sensor 2
[23.0, 23.7, 24.5, 25.3, 26.0] # Sensor 3
]
418
# Média por sensor
medias = [sum(sensor) / len(sensor) for sensor in
temperaturas]
print("\nLeituras em Fahrenheit:")
for i, sensor in enumerate(fahrenheit, 1):
leituras_fmt = " ".join(f"{temp:5.1f}°F" for
temp in sensor)
print(f" Sensor {i:<2} ➝ {leituras_fmt}")
# Executar
processar_dados_iot()
6)
# Construir gerador de equipas
def gerador_equipas():
jogadores = [
("Leonor", 85),("Santiago", 92),("Diana", 78),
("Sofia", 88),("Beatriz", 95),("Vicente", 82),
("Rui", 90),("Teresa", 86)
]
419
# Ordenar por rating (decrescente)
jogadores_ordenados = sorted(jogadores, key=lambda
x: x[1], reverse=True)
# Dividir em equipas
equipa_a = jogadores_ordenados[0::2] # Índices pares
equipa_b = jogadores_ordenados[1::2] # Índices
ímpares
# Executar
gerador_equipas()
7)
import random
def gerar_grelha_aleatoria_simples():
N = 5
420
[Link]('#')
[Link](linha)
# exibir grelha
print("=== GRELHA 5x5 ===")
for i in range(N):
linha_txt = ""
for j in range(N):
if j == 0:
linha_txt = grelha[i][j]
else:
linha_txt += " " + grelha[i][j]
print(linha_txt)
# exibir coordenadas
print(f"\nJogador em: ({rJ}, {cJ})")
coords_txt = ""
for idx, par in enumerate(obstaculos):
if idx > 0:
coords_txt += ", "
coords_txt += f"({par[0]}, {par[1]})"
print("Obstáculos em:", coords_txt)
# Executar
gerar_grelha_aleatoria_simples()
421
8)
def exibir_grelha(g):
print("=== GRELHA 5x5 ===")
for i in range(len(g)):
linha_txt = g[i][0]
for j in range(1, len(g[i])):
linha_txt += " " + g[i][j]
print(linha_txt)
def programa_input_simples():
N = 5
422
[Link]('#')
[Link](linha)
exibir_grelha(grelha)
print(f"\nJ em: ({rJ}, {cJ}) | Obstáculos: {',
'.join(str(p) for p in obstaculos)}")
# Executar
programa_input_simples()
9)
def votar_series():
votos = {}
while True:
423
nome = input("Nome do votante (ou 'fim' para
terminar): ").strip()
if [Link]() == "fim":
break
votos[serie] = [Link](serie, 0) + 1
# Executar
votar_series()
10)
comentarios = [
"O filme é bom e excelente!",
"Que experiência horrível, péssimo serviço.",
"Adoro este restaurante, maravilhoso!",
"Não gostei, muito chato e ruim.",
"Fantástico! Recomendo a todos.",
"Apesar de chato, é um bom filme."
]
424
contador_positivas = 0
contador_negativas = 0
total_palavras = 0
palavras_analisadas = []
# Contar ocorrências
for palavra in palavras:
if palavra in palavras_positivas:
contador_positivas += 1
elif palavra in palavras_negativas:
contador_negativas += 1
425
print(f"Palavras negativas encontradas:\n
{negativas_encontradas}")
print(f"Positivas: {perc_positivas:.1f}% |
Negativas: {perc_negativas:.1f}% | Neutras:
{perc_neutras:.1f}%")
# Executar
analisador_sentimentos()
426
15.7 CAPÍTULO 8
1)
if ignorar_particulas:
palavras = [p for p in palavras if [Link]() not
in PARTICULAS]
# Leitura repetida
print("Nomes completos (Enter vazio para terminar).")
while True:
nome = input("Nome completo sem abreviar:
").strip()
if not nome: # Enter vazio termina
break
print(iniciais(nome))
427
2)
def texto_para_morse(texto):
"""
Converte texto normal para código Morse.
"""
resultado = []
texto = [Link]().strip()
def morse_para_texto(codigo_morse):
"""
Converte código Morse para texto normal.
"""
428
# Criar dicionário reverso
morse_caractere = {codigo: caractere for caractere,
codigo in MORSE_CODE.items()}
palavra_traduzida.append(morse_caractere[codigo])
else:
palavra_traduzida.append(f"[{codigo}]")
# Código não reconhecido
[Link](''.join(palavra_traduzida))
def detectar_direcao(entrada):
"""
Deteta automaticamente se o input é texto ou Morse.
"""
entrada = [Link]()
if not entrada:
return "vazio"
def main():
print("�� TRADUTOR DE CÓDIGO MORSE")
429
print("=" * 40)
print("O tradutor funciona em ambas as direções:")
print("- Digite texto para converter para Morse")
print("- Digite código Morse para converter para
texto")
print("- Pressione Enter sem texto para sair")
print("=" * 40)
while True:
print("\n" + "-" * 40)
entrada = input("Digite o texto ou código
Morse: ").strip()
if not entrada:
print("Programa terminado. Até logo!")
break
direcao = detectar_direcao(entrada)
if direcao == "texto_para_morse":
resultado = texto_para_morse(entrada)
print(f"\n�
� Texto original: {entrada}")
print(f"�
� Código Morse: {resultado}")
430
("HELLO WORLD", ".... . .-.. .-.. --- / .-- ---
.-. .-.. -.."),("123", ".---- ..--- ...--"), ("PYTHON",
".--. -.-- - .... --- -.")
]
print("TESTANDO TRADUTOR")
print("=" * 50)
if __name__ == "__main__":
# Executar testes rápidos
testar_tradutor()
3)
def analisar_texto(texto):
"""
Analisa um texto e retorna estatísticas detalhadas.
"""
# 1. Análise básica - remover espaços extras
texto_limpo = [Link]()
431
# 2. Contar parágrafos (separados por quebras de linha)
paragrafos = [p for p in texto_limpo.split('\n') if
[Link]()]
num_paragrafos = len(paragrafos)
frases_paragrafo.append(frase_atual.strip())
frase_atual = ""
frases_paragrafo.append(frase_atual.strip())
[Link](frases_paragrafo)
num_frases = len(frases)
num_palavras = len(palavras)
432
# 5. Encontrar palavra mais longa
palavra_mais_longa = ""
for palavra in palavras:
if len(palavra) > len(palavra_mais_longa):
palavra_mais_longa = palavra
# 7. Retornar resultados
return {
'palavras': num_palavras,
'frases': num_frases,
'paragrafos': num_paragrafos,
'palavra_mais_longa': palavra_mais_longa,
'comprimento_mais_longa':
len(palavra_mais_longa),
'media_caracteres': media_caracteres,
'total_caracteres': total_caracteres
}
def mostrar_estatisticas(texto):
"""
Mostra as estatísticas formatadas em tabela.
"""
resultados = analisar_texto(texto)
print("ESTATÍSTICAS DO TEXTO")
print("=" * 50)
# Tabela principal
print(f"{'PARÂMETRO':<20} | {'VALOR':>10} |
{'DETALHES':<15}")
print("-" * 50)
print(f"{'Palavras':<20} |
{resultados['palavras']:>10} | {'Total':<15}")
print(f"{'Frases':<20} | {resultados['frases']:>10}
| {'Pontuações (.!?)':<15}")
433
print(f"{'Parágrafos':<20} |
{resultados['paragrafos']:>10} | {'Quebras de
linha':<15}")
print(f"{'Palavra mais longa':<20} |
{resultados['comprimento_mais_longa']:>10} |
{resultados['palavra_mais_longa']:<15}")
print(f"{'Média de caracteres':<20} |
{resultados['media_caracteres']:>10.1f} | {'Por
palavra':<15}")
print(f"{'Total caracteres':<20} |
{resultados['total_caracteres']:>10} | {'Sem
espaços':<15}")
print("-" * 50)
# Programa principal
def main():
print("ANALISADOR DE TEXTO")
print("=" * 30)
print("Texto a analisar:")
print(texto_exemplo)
print("\n" + "=" * 50)
if opcao == 's':
434
print("\nDigite seu texto (pressione Enter duas
vezes para finalizar):")
linhas = []
while True:
linha = input()
if linha == "":
break
[Link](linha)
texto_usuario = "\n".join(linhas)
if texto_usuario.strip():
print("\n" + "=" * 50)
mostrar_estatisticas(texto_usuario)
else:
print("Nenhum texto inserido.")
# Executar o programa
if __name__ == "__main__":
main()
435
15.8 CAPÍTULO 9
1)
import os
import re
import csv
import json
# -------------------------------
# PADRÕES DE PHISHING
# -------------------------------
PALAVRAS_SUSPEITAS = [
"urgente", "verifique a sua conta", "clique aqui",
"atualize os seus dados",
"password", "palavra-passe", "credenciais", "dados
bancários",
"confirme", "login", "acesso bloqueado", "conta
suspensa",
"oferta imperdível", "prémio", "ganhou", "pagamento
pendente"
]
PEDIDOS_DADOS_PESSOAIS = [
"número de cartão", "cartão de crédito", "iban",
"nif",
"senha", "pin", "cvv", "dados pessoais"
]
DOMINIOS_SUSPEITOS = [
"[Link]", "[Link]", "[Link]", "shady-
[Link]",
"[Link]", "[Link]"
]
# -------------------------------
# FUNÇÕES AUXILIARES
# -------------------------------
def extrair_urls(texto):
padrao = r'https?://[^\s]+|www\.[^\s]+'
return [Link](padrao, [Link]())
def contar_palavras_suspeitas(texto):
436
texto = [Link]()
encontradas = []
return encontradas
def contar_pedidos_dados(texto):
texto = [Link]()
encontrados = []
return encontrados
def verificar_urls_suspeitas(urls):
urls_suspeitas = []
return list(set(urls_suspeitas))
def calcular_risco(texto):
pontuacao = 0
detalhes = []
urls = extrair_urls(texto)
palavras = contar_palavras_suspeitas(texto)
dados = contar_pedidos_dados(texto)
437
urls_suspeitas = verificar_urls_suspeitas(urls)
if urls:
pontuacao += len(urls)
[Link](f"Foram encontradas {len(urls)}
URL(s).")
if palavras:
pontuacao += len(palavras) * 2
[Link](f"Palavras suspeitas: {',
'.join(palavras)}")
if dados:
pontuacao += len(dados) * 3
[Link](f"Pedidos de dados pessoais:
{', '.join(dados)}")
if urls_suspeitas:
pontuacao += len(urls_suspeitas) * 4
[Link](f"URLs suspeitas: {',
'.join(urls_suspeitas)}")
if pontuacao >= 8:
risco = "ALTO"
elif pontuacao >= 4:
risco = "MÉDIO"
else:
risco = "BAIXO"
return {
"pontuacao": pontuacao,
"risco": risco,
"urls": urls,
"urls_suspeitas": urls_suspeitas,
"palavras_suspeitas": palavras,
"pedidos_dados_pessoais": dados,
"detalhes": detalhes
}
# -------------------------------
# LEITURA DE FICHEIROS
# -------------------------------
438
def ler_txt(caminho):
with open(caminho, "r", encoding="utf-8") as f:
return [[Link]()]
def ler_csv(caminho):
emails = []
return emails
def ler_emails(caminho):
extensao = [Link](caminho)[1].lower()
if extensao == ".txt":
return ler_txt(caminho)
elif extensao == ".csv":
return ler_csv(caminho)
else:
raise ValueError("Formato não suportado. Use
.txt ou .csv")
# -------------------------------
# RELATÓRIO
# -------------------------------
def gerar_relatorio(resultados):
print("\n" + "=" * 60)
print("RELATÓRIO DE ANÁLISE DE PHISHING")
print("=" * 60)
if resultado["detalhes"]:
439
print("Indicadores encontrados:")
for d in resultado["detalhes"]:
print(f" - {d}")
else:
print("Nenhum indicador suspeito
encontrado.")
def guardar_json(resultados,
ficheiro_saida="relatorio_phishing.json"):
with open(ficheiro_saida, "w", encoding="utf-8") as
f:
[Link](resultados, f, ensure_ascii=False,
indent=4)
# -------------------------------
# PROGRAMA PRINCIPAL
# -------------------------------
def main():
caminho = input("Indique o nome do ficheiro de
emails (.txt ou .csv): ").strip()
try:
emails = ler_emails(caminho)
resultados = []
for email in emails:
analise = calcular_risco(email)
analise["conteudo"] = email
[Link](analise)
gerar_relatorio(resultados)
guardar_json(resultados)
print("\nResultados guardados em
'relatorio_phishing.json'.")
except FileNotFoundError:
print("Erro: ficheiro não encontrado.")
except ValueError as e:
print("Erro:", e)
except Exception as e:
print("Ocorreu um erro:", e)
440
if __name__ == "__main__":
main()
Caro utilizador,
2)
import os
import re
import json
from [Link] import ElementTree as ET
# -----------------------------------
# BASE DE DADOS DE VULNERABILIDADES
# -----------------------------------
BASE_VULNERABILIDADES = [
{
"id": "VULN-001",
"nome": "Palavra-passe em claro",
"descricao": "Foi encontrada uma password
armazenada diretamente no ficheiro."
},
{
"id": "VULN-002",
"nome": "Permissões demasiado permissivas",
"descricao": "Foi detetada uma configuração de
permissões insegura."
},
{
"id": "VULN-003",
441
"nome": "Uso de utilizador root/admin",
"descricao": "Foi encontrado uso direto de
root/admin em contexto sensível."
}
]
PADROES_PASSWORD = [
r'password\s*[:=]\s*.+',
r'passwd\s*[:=]\s*.+',
r'secret\s*[:=]\s*.+',
r'"password"\s*:\s*".+"'
]
PADROES_PERMISSOES_FRACAS = [
r'chmod\s+777',
r'permission\s*[:=]\s*777',
r'"permission"\s*:\s*"777"',
r'allow_all\s*[:=]\s*true',
r'"allow_all"\s*:\s*true'
]
PADROES_ROOT = [
r'user\s*[:=]\s*root',
r'username\s*[:=]\s*admin',
r'"user"\s*:\s*"root"',
r'"username"\s*:\s*"admin"'
]
# -----------------------------------
# LEITURA DOS FICHEIROS
# -----------------------------------
def ler_ficheiro_texto(caminho):
with open(caminho, "r", encoding="utf-8") as f:
return [Link]()
def ler_json(caminho):
with open(caminho, "r", encoding="utf-8") as f:
dados = [Link](f)
return [Link](dados, indent=4,
ensure_ascii=False)
442
def ler_xml(caminho):
arvore = [Link](caminho)
raiz = [Link]()
return [Link](raiz, encoding="unicode")
def ler_configuracao(caminho):
extensao = [Link](caminho)[1].lower()
if extensao == ".json":
return ler_json(caminho)
elif extensao == ".xml":
return ler_xml(caminho)
elif extensao == ".conf":
return ler_ficheiro_texto(caminho)
else:
raise ValueError("Formato não suportado. Use
.conf, .xml ou .json")
# -----------------------------------
# DETEÇÃO DE VULNERABILIDADES
# -----------------------------------
def procurar_padroes(texto, lista_padroes):
encontrados = []
return encontrados
def analisar_ficheiro(caminho):
texto = ler_configuracao(caminho)
vulnerabilidades_encontradas = []
passwords = procurar_padroes(texto,
PADROES_PASSWORD)
permissoes = procurar_padroes(texto,
PADROES_PERMISSOES_FRACAS)
root_admin = procurar_padroes(texto, PADROES_ROOT)
if passwords:
vulnerabilidades_encontradas.append({
"id": "VULN-001",
443
"nome": "Palavra-passe em claro",
"ocorrencias": passwords
})
if permissoes:
vulnerabilidades_encontradas.append({
"id": "VULN-002",
"nome": "Permissões demasiado permissivas",
"ocorrencias": permissoes
})
if root_admin:
vulnerabilidades_encontradas.append({
"id": "VULN-003",
"nome": "Uso de utilizador root/admin",
"ocorrencias": root_admin
})
return vulnerabilidades_encontradas
def obter_descricao_vulnerabilidade(vuln_id):
for vuln in BASE_VULNERABILIDADES:
if vuln["id"] == vuln_id:
return vuln["descricao"]
return "Descrição não encontrada."
# -----------------------------------
# RELATÓRIO HTML
# -----------------------------------
def gerar_html(caminho_ficheiro, vulnerabilidades,
ficheiro_saida="relatorio_vulnerabilidades.html"):
html = """
<html>
<head>
<meta charset="UTF-8">
<title>Relatório de Vulnerabilidades</title>
<style>
body { font-family: Arial, sans-serif;
margin: 30px; background-color: #f4f4f4; }
h1 { color: #333; }
h2 { color: #444; }
.bloco { background: white; padding: 15px;
margin-bottom: 20px; border-radius: 8px; }
.critico { color: red; font-weight: bold; }
444
.seguro { color: green; font-weight: bold;
}
ul { line-height: 1.6; }
</style>
</head>
<body>
"""
if not vulnerabilidades:
html += "<div class='bloco'><p
class='seguro'>Nenhuma vulnerabilidade
encontrada.</p></div>"
else:
html += "<div class='bloco'><p
class='critico'>Foram encontradas
vulnerabilidades.</p></div>"
html += "</ul></div>"
html += "</body></html>"
445
# -----------------------------------
# PROGRAMA PRINCIPAL
# ----------------------------------
def main():
caminho = input("Indique o nome do ficheiro (.conf,
.xml ou .json): ").strip()
try:
vulnerabilidades = analisar_ficheiro(caminho)
gerar_html(caminho, vulnerabilidades)
print("\nAnálise concluída.")
print("Relatório guardado em
'relatorio_vulnerabilidades.html'.")
except FileNotFoundError:
print("Erro: ficheiro não encontrado.")
except ValueError as e:
print("Erro:", e)
except Exception as e:
print("Ocorreu um erro:", e)
if __name__ == "__main__":
main()
user=root
password=123456
permission=777
allow_all=true
446
15.9 CAPÍTULO 10
1)
class Estudante:
def get_nome(self):
return self.__nome
def imprimir_nome(self):
print(f"Estudante: {self.__nome}")
2)
class Colaborador:
empresa = "Tech & Data, SA"
def get_nome(self):
return self.__nome
def get_nif(self):
return self.__nif
def get_salario_base(self):
return self.__salario_base
try:
v = float(valor)
def get_departamento(self):
return self.__departamento
448
self.__departamento = novo_departamento.strip()
def get_avaliacoes(self):
return tuple(self.__avaliacoes)
try:
n = int(nota)
except (TypeError, ValueError):
# Testes de getters
print(c.get_nome())
print(c.get_nif())
print(c.get_salario_base())
print(c.get_departamento())
print([Link])
# Testes de setters
c.set_nome("Guido van Rossum")
c.set_nif("987654321")
c.set_salario_base(1700)
c.set_departamento("Plataforma")
# Transferência de departamento
c.transferir_para("I&D")
# Registo de desempenho
c.registar_desempenho(4)
c.registar_desempenho(5)
449
c.registar_desempenho(3)
# Verificação final
print(c.get_nome(), c.get_nif(), c.get_salario_base(),
c.get_departamento(), c.get_avaliacoes())
3)
class Estagiario(Colaborador):
def __init__(self, nome, nif, salario_base,
departamento, bolsa):
super().__init__(nome, nif, salario_base,
departamento)
self.__bolsa = bolsa
def get_bolsa(self):
return self.__bolsa
try:
v = float(valor)
def salario_mensal(self):
return self.__bolsa
def elegivel_efetivacao(self):
avals = self.get_avaliacoes()
if len(avals) < 3:
return False
media = sum(avals) / len(avals)
return media >= 4
450
e.registar_desempenho(4)
e.registar_desempenho(5)
e.registar_desempenho(4)
if e.elegivel_efetivacao():
print(f"Sim, está elegível para efetivação")
else:
print(f"Não está elegível para efetivação")
451
Capítulo 13
1)
import tkinter as tk
from tkinter import filedialog, messagebox
# ----------------------------
# Janela principal
# ----------------------------
root = [Link]()
[Link]("Editor de Texto - Tkinter")
[Link]("800x500")
ficheiro_atual = None
# ----------------------------
# Área de texto com scrollbars
# ----------------------------
frame_texto = [Link](root)
frame_texto.pack(fill="both", expand=True)
scroll_y = [Link](frame_texto)
scroll_y.pack(side="right", fill="y")
scroll_x = [Link](frame_texto,
orient="horizontal")
scroll_x.pack(side="bottom", fill="x")
texto = [Link](
frame_texto,
wrap="none",
undo=True,
yscrollcommand=scroll_y.set,
xscrollcommand=scroll_x.set,
font=("Consolas", 12)
)
[Link](fill="both", expand=True)
scroll_y.config(command=[Link])
452
scroll_x.config(command=[Link])
# ----------------------------
# Barra de estado
# ----------------------------
barra_status = [Link](root, text="Pronto",
anchor="w")
barra_status.pack(fill="x")
def atualizar_status(msg):
barra_status.config(text=msg)
# ----------------------------
# Funções de ficheiros
# ----------------------------
def novo_ficheiro(event=None):
global ficheiro_atual
[Link]("1.0", [Link])
ficheiro_atual = None
atualizar_status("Novo ficheiro criado")
def abrir_ficheiro(event=None):
global ficheiro_atual
ficheiro = [Link](
filetypes=[("Ficheiros de texto", "*.txt"),
("Todos os ficheiros", "*.*")]
)
if ficheiro:
try:
with open(ficheiro, "r", encoding="utf-8")
as f:
conteudo = [Link]()
[Link]("1.0", [Link])
[Link]("1.0", conteudo)
ficheiro_atual = ficheiro
atualizar_status(f"Abrido: {ficheiro}")
except Exception as e:
[Link]("Erro", f"Não foi
possível abrir o ficheiro.\n{e}")
def guardar_ficheiro(event=None):
global ficheiro_atual
453
if ficheiro_atual:
try:
with open(ficheiro_atual, "w",
encoding="utf-8") as f:
[Link]([Link]("1.0", [Link]))
atualizar_status(f"Guardado:
{ficheiro_atual}")
except Exception as e:
[Link]("Erro", f"Erro ao
guardar o ficheiro.\n{e}")
else:
guardar_como()
def guardar_como():
global ficheiro_atual
ficheiro = [Link](
defaultextension=".txt",
filetypes=[("Ficheiros de texto", "*.txt"),
("Todos os ficheiros", "*.*")]
)
if ficheiro:
ficheiro_atual = ficheiro
guardar_ficheiro()
# ----------------------------
# Operações de edição
# ----------------------------
def cortar(event=None):
texto.event_generate("<<Cut>>")
def copiar(event=None):
texto.event_generate("<<Copy>>")
def colar(event=None):
texto.event_generate("<<Paste>>")
def desfazer(event=None):
try:
texto.edit_undo()
except:
pass
454
def refazer(event=None):
try:
texto.edit_redo()
except:
pass
# ----------------------------
# Menu
# ----------------------------
menu_principal = [Link](root)
menu_principal.add_cascade(label="Ficheiro",
menu=menu_ficheiro)
menu_principal.add_cascade(label="Editar",
menu=menu_editar)
455
[Link](menu=menu_principal)
# ----------------------------
# Atalhos de teclado
# ----------------------------
[Link]("<Control-n>", novo_ficheiro)
[Link]("<Control-o>", abrir_ficheiro)
[Link]("<Control-s>", guardar_ficheiro)
[Link]("<Control-x>", cortar)
[Link]("<Control-c>", copiar)
[Link]("<Control-v>", colar)
[Link]("<Control-z>", desfazer)
[Link]("<Control-y>", refazer)
# ----------------------------
# Iniciar aplicação
# ----------------------------
[Link]()
456
457