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

TecPro - 08 03 2024

Enviado por

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

TecPro - 08 03 2024

Enviado por

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

COTUCA - UNICAMP - Depto Processamento de Dados – Prof.

Francisco Rodrigues - Técnicas de Programação I

1o Informática
1o Desenvolvimento de Sistemas

TÉCNICAS DE PROGRAMAÇÃO

Professor Francisco da Fonseca Rodrigues – 2024

“O princípio é o momento mais delicado para a obtenção do equilíbrio.”


Princesa Irulan - Duna
Frank Herbert - 1965
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 2

CONTEÚDO
Parte 1 – Fundamentos de Programação Orientada a Objetos
1. Comunicação e linguagens 4 Palavras Reservadas 98
Informações gerais sobre padrão de escrita 98
2. Algoritmos e Modelagem de Objetos 9
Declaração de Variáveis e Tipos de Dados 99
Algoritmos 9
Ponto-e-vírgula e comandos 100
Computador, Processador, Memória e Variáveis 11
Comando de atribuição de valor a variáveis 100
Pensamento Orientado a Objetos 14
Comentários 101
Abstração e Classificação 15
Operadores aritméticos, relacionais e lógicos 101
Modelo de Classe 16
Objetos 102
Encapsulamento 18
Comandos de Desvio de Fluxo 103
Instanciação 19
Comando If 106
Codificando o algoritmo da soma 19
Comando match 107
Comando de atribuição de valor a variável 22
Comandos de Repetição de Fluxo 106
Programa principal e instanciação de objetos 33
Repetição com Teste Antes da Execução 106
3. Uso do VSCode 43 Repetição controlada - Comando for 107
Prática de Uso do VSCode 44 Como evitar erros frequentes de programação 111
Execução e depuração de programas 48
6. Strings - Cadeias de Caracteres 112
4. Programação e Orientação a Objetos 50 Variáveis Compostas de Tipo Homogêneo 112
Melhorando a Classe Calculadora 50 Cadeias de Caracteres 112
Controle do fluxo de execução 55 Operações básicas com Strings 113
Controle de Desvio com comando de decisão if 55 Percurso e processamento de strings 117
Testando nosso programa 58 Métodos de Strings 122
Cláusula else 60 Formatação de strings 125
Controle de repetição com comando while 67
7. Acumulação de Valores 126
A Classe Equação do Primeiro Grau 72
Contagem e o cálculo do sucessor 126
Método Construtor e passagem de parâmetros 73
Acumulação de Valores 133
Métodos como funções que retornam um valor 77
Somatória 133
Mais sobre Funções com devolução 79
Produtório 135
Propriedades e Acessibilidade de Atributos 79
Uma Classe de Somatória 136
Separação de Responsabilidades 83
Acesso protegido aos valores de atributos 137
Execução passo a passo 83
Tratamento de Exceções 142
Expressões Aritméticas 87
Uma Classe de Produtório 151
Variáveis Lógicas 88
Operadores Lógicos e Tabela Verdade 89 8. Arquivos de Dados 153
Condições Compostas 90 Tabela ASCII e caracteres representáveis 154
Diagramas de Execução (Teste de Mesa) 91 Processamento e Análise de Arquivos Texto 156
Funções Pré-definidas de Python 93 Caixa de Diálogo para abertura de arquivos 161
Leitura de números em arquivos texto 163
5. Linguagem de Programação Python 97
Aplicação: conversão de Texto para Fala 163
Elementos Básicos da Linguagem 97
Forma Geral de uma Classe e de um Programa 97 9. Solução de Problemas Matemáticos 165
Símbolos da linguagem 98 Aplicações Matemáticas da Contagem 165
Uma Classe de Problemas Matemáticos 168
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 3

Parte 2 – Programação de Aplicações e Estruturas Básicas de Dados


Desenvolvimento passo a passo de aplicativo 328
10. Vetores e Listas 190
Ferramenta bidirecional de programação 343
Indexação 224
Controle do número de posições usadas 226 14. Classes e Vetores de Objetos Genéricos 345
Leitura e Montagem de Vetor 226 A Ideia Central 345
Percurso sequencial de um vetor 228 Interfaces e a classe de entidade genérica 346
Programa de demonstração 229 Implementação da classe de vetor genérico 351
Classe VetorInteiro 230 Unindo as ideias 355
Casamento de Vetores 233
15. Manutenção de arquivos relacionados 361
Como evitar os erros mais frequentes 235
16. Matrizes 405
11. Processamento de dados 237
Conceitos Fundamentais 392
Arquivos Texto para Processamento de Dados 168
Operações Usuais com Matrizes 393
Arquivos JSON para Processamento de Dados 184
Controle DataGridView 395
Acesso a Banco de Dados Relacional
Uma aplicação: soma de matrizes 396
12. QtSyde: Interface Gráfica com o Usuário 238 Uma aplicação: tratamento de Imagens 407
Programação Visual 239
Ambiente de Desenvolvimento de Interfaces 241 Comandos de Desvio num formulário 151
Criação de um Formulário de Cadastro 247 Menus Tradicionais e Suspensos 154
Conexão de controles a ações 253 Comandos de repetição num formulário 160
Aplicação: a Calculadora num Formulário 258 Resumo dos componentes estudados 162
Digitação e exibição de dados em formulários 145 Componentes úteis para Tratamento de Dados 239
Uma Aplicação: Animador de Figuras 289 Componentes 239
Controles Comuns e Outros 243
13. Vetores de Objetos 300
Um Programa Visual para Entrada de Dados 248
Manutenção de Dados 305
Vetores nos Componentes Visuais do QtSyde 288
Vetores não-ordenados 305
Dados Ordenados e operações eficientes 312
Operações em Vetores Ordenados 313 17. Palavras Finais 422
Ordenação de Vetores 316
Anexo I – Instalação do Python 423
Pesquisa Binária 319
Anexo II - PySide6, Qt Designer
Acesso e Alteração de elementos do vetor 322
Classe VetorFuncionario Completa 324

Seja bem vindo(a)!


“O homem deve ver que nada existe realmente, mas
que tudo está sempre se tornando e mudando. Nada
fica parado. Tudo está nascendo, crescendo e mor-
rendo. No instante em que alguma coisa atinge o seu
auge, começa a decair. A lei do ritmo está em funcio-
namento constante. Não existe a realidade. Não há
uma qualidade duradoura, fixidez ou substanciali-
dade em nada. Nada é permanente, a não ser a
mudança. O homem deve ver todas as coisas como
reação constante, fluxo ou refluxo, construindo ou
demolindo, criação ou destruição, nascimento,
crescimento e morte. Nada é real, e nada resiste a
não ser a mudança.”
a Cabala
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 4

Quando duas pessoas tentam se


1. comunicar, elas precisam usar uma
forma de comunicação que as duas
Comunicação e entendam, ou seja, uma linguagem
comum às duas.
Linguagens
Na comunicação entre uma pessoa e
um computador, o processo é semelhante.
O computador é uma máquina construída de tal forma que pode proces-
sar informações.
Tais informações são representadas dentro dele
através de impulsos elétricos altos e baixos formando, assim, configurações
de valores 1 (impulso alto) e de valores 0 (impulso baixo).
As configurações de 0 e 1 são códigos que representam as informações
para o computador, da mesma maneira que as letras do alfabeto, quando
agrupadas, podem representar informações na nossa linguagem. Essa lin-
guagem de impulsos elétricos é a linguagem que um computador entende.
Você estudará essa forma de representação em outra disciplina, mas abaixo
ilustramos desse conceito, indicando como a maioria dos computadores representa letras e
dígitos (símbolos dos algarismos). As linhas horizontais e verticais indicam a altura dos impulsos
elétricos:

0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1

Letra ‘A’ (65 em decimal) Algarismo ‘7’ (55 em decimal)


Figura 1.1 – Representação em binário de informações alfanuméricas (letra ‘A’ e dígito ‘7’)

É óbvio que um ser humano não sabe usar esta linguagem de impulsos elétricos. Assim, são
necessários meios de traduzir a linguagem humana para a linguagem de máquina usada por um
computador. Isto é feito através do uso de Linguagens de Programação.
Com uma Linguagem de Programação, uma pessoa pode escrever programas. Programas são
conjuntos de que ordens, dispostas em uma sequência lógica, para o computador executar.
Executar um programa significa que a sequência de ordens dadas no programa será realizada
pelo computador. Assim, é possível estabelecer contato com o computador, que é uma máquina
capaz de receber instruções, efetuá-los e apresentar respostas ao seu operador.
Um programa é, portanto, um conjunto de instruções que são fornecidas a um computador para
que ele as entenda e execute. A seguir, temos dois pequenos exemplos de um mesmo programa,
escrito nas antigas linguagens de programação Pascal e C:
Program Exemplo_1; main()
// Declaração de Variáveis {
Var A, /*Declaraçãoo de Variáveis*/
B, int A,
Soma : Integer; B,
Begin // Programa Principal Soma;
Write(‘Digite o valor A :’); printf(“Digite o valor A :”);
Read(A); scanf(“%i”,&A);
Writeln; // Muda de Linha printf(“\n”); // Muda de Linha
Write(‘Digite o valor B :’); printf(“Digite o valor B :”);
Read(B); scanf(“%i”,&B);
Writeln; printf(“\n”);
Soma := A + B; Soma = A + B;
Write(‘Soma = ‘,Soma); Printf(“Soma = %i”,Soma);
End. }
No exemplo acima, os dois programas efetuam a leitura de dois valores e calculam a soma dos
mesmos, escrevendo a seguir o resultado dessa soma.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 5

Neste momento, não importa como isso é feito. O importante é notar que cada linguagem tem seu
vocabulário próprio. Por exemplo, para escrever uma mensagem em Pascal, usa-se o
procedimento Write, e em C usa-se a função printf. No entanto, a “lógica”, ou a resolução do
problema, é mesma para os dois programas: obter os valores a somar, efetuar a soma e
escrever esse resultado.
Um programa deve especificar instruções que possam ser executadas pelo computador para
resolver o problema, passo a passo, “explicando” como se resolve o problema proposto, seja este
uma equação do 2o grau, uma folha de pagamento, cálculos de órbita de satélites, ou ainda uma
simples soma de dois valores inteiros, como vimos no exemplo acima.
Na verdade, o computador não entende o que significam as palavras dos programas de exemplo
acima, já que a linguagem real do computador é composta apenas pelos impulsos elétricos altos e
baixos que citamos. Para que o computador “entenda” o programa, deve haver uma tradução. A
tradução de um programa escrito numa linguagem de programação para os valores numéricos 1 e
0 da linguagem de máquina é feita por através de outro programa, já existente no sistema do
computador, chamado COMPILADOR. O compilador traduz as instruções da pessoa que
escreveu o programa (programador) para a linguagem de máquina (0s e 1s, ou binário).
Na figura ao lado, temos um
exemplo de um pequeno progra-
ma, que escreve uma mensagem
de boas vindas aos alunos. Ele
apresenta seus resultados numa
janela chamada Console (ou
prompt de comandos).
O texto que vemos na figura é
chamado de código fonte, pois é
a origem do programa. Um desen-
volvedor o digitou, na linguagem
de programação Python, que é a
linguagem que aprenderemos.
Esse texto gerou um arquivo
chamado main.py e foi armazena-
do no disco do computador. O
arquivo é chamado de arquivo
fonte, pois contém o código fonte.
Esse texto não permite ao computador realizar as ações que o texto descreve, mas nós, seres
humanos, conseguimos lê-lo e entender o que representa.
Para que o computador entenda o que esse programa ordena e execute essas ordens, o texto
precisa ser interpretado e traduzido para a linguagem interna do computador, a linguagem de
máquina. Essa tradução vai gerar um arquivo no disco do computador, onde os comandos acima
estarão traduzidos para código binário, seguindo as normas do sistema operacional em uso.
Esse arquivo é chamado de arquivo executável.
E agora, ao lado temos a tela
console, mostrada na execução
do programa executável, depois
que o programa fonte foi traduzi-
do para a linguagem de
máquina pelo compilador da
linguagem python.
Na figura abaixo, vemos uma parte de um programa executável, com a representação de seu
conteúdo em código binário. Entenda que, internamente, o código binário não é representado por
meio de dígitos, mas sim em termos de impulsos elétricos altos (1) e baixos (0). Essa
representação que vemos abaixo, por meio de algarismos 1 e 0, é feita apenas para facilitar o
nosso entendimento, pois se trata de uma analogia, já que não conseguimos ver impulsos
elétricos:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 6

Portanto, o processo de geração de um programa envolve a codificação (escrita dos comandos)


numa linguagem de programação (Python, C#, Java, Delphi, outras), a sua compilação e geração
do código e máquina. Na linguagem que estudaremos, o processo de compilação compreende as
etapas abaixo:

Fonte: https://www.c-sharpcorner.com/article/why-learn-python-an-introduction-to-python/

Essas etapas podem ser feitas com diferentes aplicativos computacionais, mas atualmente
existem ferramentas que permitem realizar todas as tarefas num único ambiente. Por exemplo, na
figura com o código fonte, usamos o ambiente integrado de desenvolvimento PyCharm, que
futuramente aprenderemos e usaremos nas nossas práticas de programação. Podemos usar
outros ambientes, como o Visual Studio Code, por exemplo.
Existem várias linguagens de programação e, geralmente, um programa pode ser escrito em
qualquer uma. Uma linguagem de programação é uma linguagem artificial, criada com regras
rígidas de sintaxe (escrita correta das palavras) e de semântica (significado do texto escrito). Tais
regras devem ser seguidas para que o compilador possa fazer corretamente a tradução para a
linguagem de máquina.
Nos capítulos seguintes veremos como se faz para “ensinar” o computador a processar dados
sem nos importarmos com uma linguagem de programação específica. Veremos tipos de
instruções comuns à maioria das linguagens de programação existentes. No entanto,
enfatizaremos a estrutura usada na linguagem Python para efetuar este estudo.
Esse é outro aspecto interessante sobre as linguagens de programação. São linguagens artificiais,
criadas por pessoas. Essas pessoas podem (e devem) especificar as características que atinjam
os propósitos que as levaram a criar a linguagem.
Quando Niklaus Wirth, em 1971, criou a linguagem Pascal estava interessado numa linguagem
apropriada ao ensino de técnicas de programação. Já a linguagem COBOL, por exemplo, criada
em 1959, foi orientada totalmente para o processamento de arquivos de dados administrativos.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 7

Com o desenvolvimento das teorias de Banco de Dados, surgiram linguagens voltadas ao


processamento de bancos de dados, como SQL e Clipper. Hoje em dia, novas metodologias de
computação estão sendo aplicadas na criação de novas linguagens e aprimoramento das atuais
como, por exemplo, a Orientação a Objetos.
Existe também uma genealogia de linguagens. Por exemplo, a linguagem ALGOL foi baseada em
PL/I, uma das primeiras linguagens estruturadas. Em seguida, ALGOL foi a base de Pascal e
ADA. Pascal, ALGOL, C e FORTRAN são consideradas procedurais, pois enfatizam a criação de
programas usando procedimentos ou subrotinas que descrevem as tarefas, enquanto outras
linguagens possuem características diferentes, como LISP e PROLOG, voltadas à Inteligência
Artificial.
Resumindo, uma linguagem de programação é o meio pelo qual um programador escreve um
programa. Um programa é um conjunto de instruções para o computador executar, normalmente
processando informações e escrevendo respostas sobre esse processamento.

Programador Lê
analisa o Compila
problema e Digita programa
Executa
concebe uma
PRO solução
BLE Converte
a solução
MA! em um
programa Resultado
eo
escreve

Eu preciso saber de tudo isso?

Sempre é bom ter um conhecimento geral, pelo menos, dos lugares onde estamos entrando, para
que as surpresas não sejam tão grandes que nos impeçam de entrar.
No caso dos assuntos que discutimos acima, eles representam uma visão geral do que é
programar um computador.
Certamente que a visão desses números zeros e uns poderá deixar algumas pessoas
apreensivas, pois é assim que o computador representa informações e ele não entende o que
escrevemos, falamos ou nossos gestos. E entender essa linguagem de máquina pode parecer
bastante desafiador e, quem sabe, desanimador.
Aí você poderá se perguntar: mas eu escrevo coisas no meu computador e ele entende, até
mesmo já falei coisas e ele entendeu, ou falei coisas no meu celular e apareceram propagandas
de produtos relacionados ao que eu falei... mesmo gestos que faço numa tela de toque são
entendidos pelo celular ou pelo meu computador. Então, como é que o computador não entende
as linguagens humanas?
O que acontece é que essas operações são processadas por meio de programas que foram
desenvolvidos por programadoras e programadores. Esses programas obtém as informações
(textuais, sonoras, gestuais) as analisam e as traduzem para a linguagem de máquina. Ou seja,
mesmo neste mundo conectado em que habitamos, em que tanta coisa parece ser realizada
automaticamente, o computador somente entende aquilo que programas lhe ordenam e que
esteja ordenado em linguagem de máquina.
A boa notícia é que, para criarmos programas que controlem dispositivos computacionais, como
notebooks, computadores pessoais, smartphones ou outros equipamentos, não precisamos
conhecer linguagem de máquina, código binário e quase nada a respeito do funcionamento
interno desses dispositivos.
Os ambientes de programação nos permitem escrever programas usando linguagens muito
parecidas com as que estamos acostumados, e nos isolam de toda essa complexidade.
A tarefa de programar, ou seja, conceber e codificar as soluções para os problemas que
precisamos resolver, continua sendo de pessoas que desenvolvem e aplicam seu raciocínio
lógico, usando esses ambientes e linguagens de programação. Caberá ao ambiente de
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 8

programação realizar a compilação dos programas para a linguagem de máquina que o


computador utiliza.
Assim, poderemos nos concentrar em produzir soluções, por meio de programas de computador,
cada vez melhores, mais eficientes e que atendam as demandas da nossa sociedade humana.

Preparação do ambiente de desenvolvimento adequado ao nosso estudo


Aproveite para criar uma estrutura de pastas para armazenar os projetos
que desenvolveremos nesta disciplina. Como sugestão, pode ser uma
estrutura como a da figura ao lado.
Conforme for necessário, poderemos alterar essa estrutura ou criar novas
pastas, para outros capítulos de nosso estudo e mesmo para outras
disciplinas.
A unidade de disco que você usará não importa muito, mas é importante
sempre ter uma cópia de segurança em outras localizações, como um
pendrive ou uma unidade em nuvem.
Evite desenvolver projetos em pendrives, porque a possibilidade de ficar
cheio ou de ocorrerem problemas é bem alta, além de serem dispositivos de
acesso lento.
Sempre desenvolva seus projetos numa unidade de armazenamento local
do seu computador, de preferência, ou usando alguma ferramenta na
nuvem. Mas é importante ter uma cópia local de seus projetos, para o caso de perder o acesso à
Internet.
Daqui a algumas aulas, precisaremos do Python e do ambiente integrado de desenvolvimento
Visual Studio Code para as práticas. Por isso este é um bom momento para já providenciá-lo,
através das instruções contidas no Anexo I, ao final deste material didático.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 9

2.1. Algoritmos
2. Como vimos anteriormente, é necessária uma linguagem de
Algoritmos, programação para que o computador entenda e resolva o
problema desejado. Ela permite a comunicação com o
Computadores e computador, através dos conjuntos de instruções que formam
Modelagem de os programas.
Um programa, portanto, é uma sequência de instruções intro-
Objetos duzidas no computador para que ele as execute e obtenha
resultados.
Os resultados dependem diretamente das instruções fornecidas. Portanto, para que o computador
realize corretamente as operações necessárias para a solução desejada do problema, é
obrigatório que as instruções estejam escritas na ordem correta, que é a ordem que define os
passos exatos para a solução.
Todo problema solucionável tem um ou mais processos que devem ser seguidos para se chegar à
solução. Este processo é geralmente composto por uma sequência de operações, ações ou
regras.
Ao processo de resolução de um problema dá-se o nome de ALGORITMO.
Algoritmo, portanto, é um conjunto de operações dispostas numa ordem correta que, ao serem
executadas passo a passo, produzem a solução de um problema. Em outras palavras, é a
sequência ordenada de ações (sequência lógica) que descrevem os passos que devem ser
seguidos para se chegar à solução de um problema.
Como exemplo de problemas que tem algoritmos, podemos destacar:

1. Abrir uma porta


2. Fazer um bolo
3. Adicionar dois números
4. Trocar de roupa
5. Usar um telefone público
6. Trocar um pneu furado
7. Fazer uma omelete

Como se pode ver nessa relação, mesmo as atividades mais triviais do dia-a-dia possuem
algoritmos, pois, obviamente, tudo o que fazemos tem um ou mais modos de ser feito.
As ações feitas para resolver cada um desses problemas constituem os seus algoritmos, ou seja,
a descrição da solução de cada um deles.
Esse modo de realizar as atividades pode ser descrito através das ações individuais que, execu-
tadas na sequência correta, solucionam o problema. Ou seja, as ações tomadas para solucionar
um problema formam o algoritmo que soluciona esse problema.
Os computadores só fazem aquilo que mandamos e, não necessariamente, o que gostaríamos
que eles fizessem. Nas ordens descritas num programa, não pode haver ambiguidades, nem a
possibilidade de interpretações alternativas.
Isso ocorre porque os computadores não interpretam a intenção que tínhamos quando escreve-
mos um programa; pelo contrário, eles procuram executar as ordens exatamente como elas estão
escritas, e não como o programador pensou que as escreveu. Isso significa que, muitas vezes, os
programadores escrevem instruções achando que elas fariam algo que eles gostariam que
fizessem, mas, no entanto, efetivamente não fazem, e isso acaba gerando comportamentos
imprevistos nos programas.
O computador sempre tomará algum caminho em suas ações; por isso o programador tem que
assegurar que o computador siga pelo único caminho correto possível que leve aos resultados
desejados.
Por exemplo, se você ordenar ao computador para “adicionar dois números”, ele não saberá
executar essa instrução apenas com essas palavras lhe sendo ordenadas. Elas precisam ser
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 10

melhor especificadas, pois o computador não sabe nem como nem de onde obter os números
que você quer somar, nem o que ele deve fazer com o resultado. Essa especificação mais
completa precisa ser descrita no algoritmo de solução do problema.
Os passos de um algoritmo devem ser simples e sem ambiguidades; devem estar dispostos em
uma sequência (numa ordem) cuidadosamente definida. Devem também ser efetivos, ou seja,
devem sempre resolver um problema usando um número finito de passos. Se isso não aconteces-
se, os custos de usar programas de computadores seriam astronômicos e, portanto, impraticáveis
no dia-a-dia.
Estudemos a “anatomia” de um algoritmo. O algoritmo do problema 3 (adicionar dois números),
poderia ser escrito, de forma bastante livre, como abaixo:

Algoritmo AdicionarDoisNumeros
1. {
2. Obter o 1o número e chamá-lo de valor1
3. Obter o 2o número e chamá-lo de valor2
4. Calcular o resultado de valor1 + valor2
5. Chamar esse resultado de SOMA
6. Escrever SOMA
7. } [Algoritmo 1]

O algoritmo acima tem sete passos, ou ações. Todo algoritmo tem um Início ( { ) e um Fim ( } )
(passos 1 e 7, respectivamente), como tudo que se executa na vida.
Os passos 2 e 3 são semelhantes; a única diferença entre eles é o destino dos dados (valores)
obtidos em cada um. O primeiro dado é chamado de valor1 para diferenciá-lo do segundo,
identificado pelo nome valor2. Assim, essas palavras valor1 e valor2 foram usados para
identificar dados ou valores. São, portanto, chamadas de IDENTIFICADORES. SOMA também é
um identificador, e guardará o resultado da EXPRESSÃO valor1 + valor2. O símbolo + é o
operador de adição da Aritmética.
O passo 6 mostra o resultado da adição. Essa exibição, em termos de algoritmos para computa-
dor, pode ser feita em papel, em tela ou em um armazenamento diverso como, por exemplo, uma
unidade de disco ou um pendrive. Escrever é, portanto, uma instrução de saída, pois faz com que
resultados sejam conhecidos fora do computador. Por outro lado, obter é uma instrução de
entrada, pois faz com que valores externos sejam “lidos”, colocados dentro do computador. Isto
será estudado com maior detalhamento adiante.
Outra característica de um algoritmo é que certas ações precisam ser feitas antes de outras. Por
exemplo, não se pode calcular o valor da soma antes de se obter os valores iniciais; da mesma
forma, não se pode obter o segundo valor antes de se obter o primeiro. Portanto, pode-se dizer
que:

Algoritmo é uma sequência de ações que descrevem a solução de um problema.


Essas ações, executadas uma após a outra, de forma ordenada e lógica, geram a
solução do problema apresentado.

A sequência de execução dos passos do algoritmo é chamada de FLUXO DE EXECUÇÃO:


esse fluxo começa desde o primeiro passo do algoritmo, e segue sequencialmente até o último
passo, executando cada instrução conforme passa por ela, como mostram as setas verdes do
exemplo acima. O primeiro passo é a instrução Início ({), o segundo passo é a instrução Obter o
primeiro valor, o terceiro passo é outra instrução Obter, o quarto e o quinto passos são o cálculo
da adição, o sexto passo é a Exibição do resultado, e o sétimo passo é o Fim (}), onde acaba a
execução do algoritmo. Esses sete passos, nessa ordem, formam o fluxo de execução do
algoritmo.
A tarefa de raciocinar sobre quais passos serão escritos e o ato de escrevê-los no algoritmo é
chamada de Codificação. Codificar um algoritmo, portanto, é escrever os comandos que esse
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 11

algoritmo realiza ao ser executado. Esses comandos não podem ser quaisquer ações: eles
precisam descrever precisamente o que será feito e a ordem em que cada ação deve ser feita.
Em nosso estudo, nos interessa criar algoritmos que possam ser executados em computadores.
2.2. Computador, Processador, Memória e Variáveis

Computador é um conjunto de componen-


tes eletrônicos (máquina) capaz de executar
variados tipos de algoritmos e tratamento de
informações (processamento de dados).[1]
Um computador pode possuir inúmeros atri-
butos, como armazenamento de da-
dos, processamento de dados, cálculo em
grande escala, desenho industrial, trata-
mento de imagens gráficas, realidade
virtual, entretenimento e cultura.
Assumiu-se que os computadores pes-
soais e laptops são ícones da Era da Infor-
mação;[2] e isto é o que muitas pessoas con-
sideram como "computador". Entretanto,
atualmente as formas mais comuns de
computador em uso são os sistemas embar-
cados, pequenos dispositivos usados para LEGENDA: 01- Monitor; 02- Placa-Mãe; 03-
controlar outros dispositivos, tais co- Processador; 04- Memória RAM; 05- Placas de
Rede, Placas de Som, Vídeo, Fax...; 06- Fonte de
mo robôs, câmeras digitais ou brinquedos.
Energia; 07- Leitor de CDs e/ou DVDs; 08- Memória de
Fonte: https://pt.wikipedia.org/wiki/Computador massa; 09- Mouse (Rato); 10- Teclado

Um computador é uma máquina construída para processar dados. Em sua


memória, são armazenadas informações que serão processadas pelo programa
executado no momento. Esse programa deve especificar ao computador quais são, como são, e
onde serão armazenados esses dados, identificando cada um com nomes únicos e agrupando-
os em elementos que chamaremos de objetos.
Em seguida, o programa deverá “dizer” ao computador como processar os objetos em sua
memória, ou seja, indica ao computador as ações do algoritmo do problema, executando as
operações previstas nos objetos para obter o resultado desejado.
As informações e objetos são guardados em posições de memória. Cada uma dessas posições
é chamada de VARIÁVEL.
Variável, portanto, é uma posição da memória do computador onde fica guardada uma
informação que será ou já foi processada por um programa.

A informação armazenada em uma variável não é fixa nem eterna, pois pode ser alterada
quando necessário, pela execução de ações do algoritmo que mudem o valor armazenado.
Como exemplo, temos os identificadores A, B e SOMA, no algoritmo abaixo. Note que ele é
muito semelhante ao algoritmo [1], mas mudamos os nomes das variáveis valor1 e valor2 para A e
B, respectivamente.
1a 2a
Algoritmo SomarDoisNumeros() execução execução
{
Obter o 1o valor e chamá-lo de A A 3 2
Obter o 2o valor e chamá-lo de B
Calcular o resultado de A + B B 1 -8
Chamar esse resultado de SOMA
Soma 4 -6
Escrever SOMA
}
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 12

Em um momento qualquer de execução desse algoritmo, pode-se digitar o valor 3 para A e o valor
1 para B, de modo que SOMA valerá 4 após o cálculo da expressão A+B. Em outro momento de
execução, pode-se digitar 2 para A e -8 para B; assim SOMA, após calculada, guardará o valor -6.
Devido ao fato de poderem ter suas informações modificadas, esses identificadores são chama-
dos de VARIÁVEIS. As variáveis armazenam, na memória do computador, valores que podem ser
modificados quando necessário. Esse é um conceito bastante parecido com o da Matemática
quando usamos incógnitas como x e y, por exemplo.

Nomes adequados para variáveis


A e B, como citado antes, foram usados para armazenar os valores que seriam somados. O
computador não se importa com os nomes que damos às variáveis, desde que esses nomes ini-
ciem com uma letra. Assim sendo, podemos dar outros nomes às mesmas variáveis se quisermos
ou mesmo para aumentar clareza sobre o funcionamento do programa.
Nomes de variáveis são palavras que devem começar com uma letra e esta pode ser seguida
por dígitos, letras e o símbolo ”_”. Podemos misturar números com letras, desde que o primeiro
caracter do identificador seja uma letra. Assim,
x1, x2, soma, Sub_Total, subTotal → são nomes válidos
01ab, Tot+1, a/b2-c → são nomes inválidos.
Algumas linguagens de programação diferenciam letras maiúsculas e minúsculas nos nomes das
variáveis, classes e métodos. Portanto, é importante manter um padrão de nomenclatura para que
não haja confusão. Nessas linguagens, subTotal e Subtotal seriam duas variáveis diferentes. É o
caso na linguagem Python, que usaremos em nosso estudo.

Declaração de variáveis
Toda variável tem que ser declarada antes de ser usada nos comandos do programa, pois para
usar uma variável, o comando tem que saber seu nome e que tipo de informação ela armazena.
Ao declarar uma variável, escrevemos o nome dessa variável e nela guardamos um dado,
escrevendo a declaração de acordo com a regra da forma geral abaixo:
nomeDaVariavel = valor inicial
O símbolo = é chamado de operador de atribuição, e faz com que a variável à sua esquerda
receba o valor à sua direita. Em geral, lemos esse comando como “variável recebe valor”
O valor inicial guardado na variável definirá o tipo de dado dessa variável.
O Tipo de dado indica a natureza dos valores armazenados na variável. Uma variável pode
armazenar informações de tipo numérico, lógico ou literal (letras e caracteres). Os tipos básicos, já
disponíveis na nossa linguagem de algoritmos, são:
• Inteiro: variáveis desse tipo armazenam valores numéricos inteiros.
• Real: variáveis desse tipo armazenam valores numéricos reais, ou seja, valores numéricos
com parte inteira e com parte decimal.
• Cadeia de Caracteres: variáveis desse tipo armazenam um ou mais caracteres, formando
uma sequência de símbolos tipográficos, como letras, dígitos, sinais de pontuação.
• Lógico: variáveis desse tipo armazenam um valor lógico: verdadeiro ou falso.
Abaixo temos exemplos de dados dos tipos básicos que podemos usar em um algoritmo:
Inteiro 281 508 -76
Real 2.57 -13.02 7.0
CadeiaDeCaracteres “Maria” “A” ‘ABC’ “98” “Rua Silvana Moraes, 131”
‘A’ ‘a’ “n” “9” ‘8’ ‘-‘ ‘@’ ‘”’ ‘!’ “ “ ‘_’
Lógico false true
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 13

Uma cadeia de caracteres atribuída a uma variável deve ser delimitada por aspas (“) ou por
apóstrofes (‘), ao serem escritas nos algoritmos (“Maria tinha um carneirinho”, ‘Bloco 1A').
Cadeias de caracteres são chamadas de strings em inglês.
Seguem exemplos de declaração de variáveis:
quantidadeDePacotes = 183 # inteiro (int)
sobra = -12 # inteiro (int)
consumo = 0 # inteiro (int)
salario = 1320.0 # real (float)
impostoDeRenda = -190.47 # real (float)
totalPago = 0.0 # real (float)
pergunta = ‘Deseja parar?’ # cadeia de caracteres (string)
resposta = ”N” # cadeia de caracteres (string)
operacaoDesejada = None # valor NULO, fica vazia (não vale 0)

É importante saber que um mesmo algoritmo não precisa ser reescrito quando os dados
são diferentes a cada execução; simplesmente mudam-se os valores (informações) que serão
digitados e armazenados nas variáveis. O algoritmo continua a ser o mesmo; mas calcula valores
que poderão ser diferentes dos calculados anteriormente, já que os valores de entrada mudaram.
Assim, um mesmo algoritmo (ou programa) serve para resolver uma mesma categoria (uma
mesma classe) de problemas, mesmo que os valores sejam diferentes para cada problema
específico.
Quando você está jogando em um computador ou em um console de games, está executando um
programa e, assim, as diferentes informações que o jogo manipula são guardadas em variáveis.
Por exemplo, a velocidade de um carro ou o número de vidas restantes a um personagem tem
seus valores guardados em variáveis que serão, de acordo com o decorrer do jogo, trabalhadas
de forma que, em cada momento, armazenem os valores adequados para aquele momento do
jogo. Esses valores vão sendo modificados (ou não) de acordo com as ocorrências no jogo.
Para processar as informações que ficam armazenadas na memória, o computador possui um
processador, também chamado de Unidade Central de Processamento (UCP) ou Central
Processing Unity (CPU) em inglês. O processador trabalha com as informações, nelas realizando
operações aritméticas (cálculos) e lógicas (comparações) de acordo com as instruções de um
programa (algoritmo escrito em linguagem de programação e já compilado para código binário).
As informações não surgem do nada na memória. Elas precisam ser introduzidas na memória
através de Operações de Entrada como, por exemplo, digitação de dados no teclado ou leitura
de dados que estão gravados em arquivos do disco do computador, de um pendrive ou mesmo
recebimento desses dados através de conexões com outros computadores ligados em rede.
As informações são armazenadas na memória,
dentro das variáveis, e são levadas para o
processador quando necessário, para realizar
operações com os valores armazenados, de
acordo com as ações definidas no programa
sendo executado no momento.
Os resultados dessas operações são novamente
enviados à memória e armazenados em variáveis
destinadas a guardar tais resultados.
Aqui e aqui você poderá acessar mais materiais
sobre o relacionamento entre processador, memó-
ria e dispositivos de entrada e saída.
Na figura abaixo temos um esquema simplificado
das variáveis armazenadas para o Algoritmo
SomarDoisNumeros.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 14

Memória
Unidade Variáveis Programa
Central de Executável
Processamento A B Soma
(algoritmo)
(CPU)

3 -1 2

Para podermos codificar o algoritmo de soma


de dois valores em Python, podemos usar o
VSCode para abrir uma pasta onde desejamos
criar o projeto de software. Fazemos isso com a
opção Arquivo | Abrir Pasta e selecionamos nas
unidades de armazenamento do computador a
pasta que desejamos. Pode ser necessário criar
essa pasta antes.

Após isso, pressionamos o botão [Selecionar Pasta]:

Aparecerá um conjunto de pequenos botões que


permitirão criar arquivos, atualizar a tela, criar
pastas, dentre outras operações. Vamos clicar no
botão que permite criar um arquivo:

Uma caixa de digitação aparecerá, como vemos


na figura ao lado. Digite o nome de arquivo
main.py, que será o arquivo em que digitaremos o
código fonte de nosso programa em Python.
A extensão “.py” indicará ao VSCode que
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 15

programaremos na linguagem Python, de forma que esse ambiente de desenvolvimento facilitará


nosso trabalho de programação nessa linguagem.

Blocos de Comandos

Quando programar em Python, você encontrará ou criará blocos de comandos, que são trechos
de texto de programa Python que podem ser executados como uma unidade, tais como um
módulo, uma definição de classe ou de uma função.
Nesse trecho, use uma tecla [Tab]
antes de cada linha dentro da
Em seguida, digite as linhas abaixo: função SomarDoisValores

Variável valor1 recebe o número


73.5

Variável soma recebe o resultado da


adição de valor1 (73.5) com valor2 (-
15.0)

Pressione o botão com o triângulo para que o computador interprete esse código, aponte
eventuais erros de digitação (que você deverá corrigir) e gere a tradução desses comandos para a
linguagem de máquina que o computador entende e pode executar. Se não houver erros de
digitação (também conhecidos como erros de sintaxe), seu programa será executado e o
resultado abaixo aparecerá:

Essa parte da tela do VSCode é chamada de Terminal. Nela aparecerá a interação do usuário
com o programa. No caso acima, foram escritos frases e resultados do programa.

Podemos executar esse programa passo a passo, ou seja, uma linha de código por vez. Isso nos
permitirá observar o fluxo de execução, que é a sequência que o computador segue para realizar
cada instrução.
Para permitir a execução passo a passo, clique
ao lado direito do número da linha 11, onde o
comando SomarDoisValores() é executado.
Aparecerá um círculo vermelho nesse local.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 16

É o que se chama de breakpoint ou ponto de interrupção. A execução do programa será


suspensa quando chegar nesse ponto, que é o ponto de partida do programa:
O interpretador Python precisa ter sido instalado antes de usar o VSCode. O interpretador Python
é usado para analisar o código fonte e gerar sua versão executável em linguagem de máquina.
Sem ele, o computador em uso não conseguirá trabalhar com a linguagem Python.
Para indicar ao VSCode onde se encontra o interpretador precisaremos, na primeira vez que
usamos o VSCode para programas em Python nesse computador, abrir ou criar um arquivo
Python (extensão .py) e seguir as instruções do Passo 5 do Anexo I.
Deve aparecer um botão triangula do lado direito da tela,
na parte superior.

Caso esse botão não apareça, clique no botão que possui um triângulo e uma joaninha
(um bug) do lado esquerdo da tela e clique no link botão “crie um arquivo launch.json” e
escolha a opção [Python Debugger]. Em seguida, escolha a opção [Arquivo Python
Deputrar o arquivo Python ativo no momento]. Feche a janela com o arquivo launch.json
que aparecerá.
Ao acessar a janela com o arquivo
main.py, o triângulo deverá aparecer
no lado superior direito da tela. Ao
lado dele, há uma seta para baixo que,
ao ser clicada, exibe a tela ao lado:
Selecione a opção Depurador do
Python: Depurar arquivo Python.

Se você fizer isso, a execução do seu programa será iniciada em modo de depuração (debug),
permitindo que você visualize a sequência de execução, comando a comando, desde o ponto de
início da execução (onde colocamos o breakpoint). Aparecerá uma janela na parte superior do
VSCode com os controles de depuração, como vemos abaixo.
Esses botões permitem que controlemos a execução passo a
passo, para visualizarmos os valores das variáveis e como o
computador executa esse programa.
Na janela [Terminal] será feita a interação com o usuário do programa, tanto para exibição de
saídas quanto para leitura de dados digitados no teclado.
Pressione [F11] ou o botão da seta para baixo.
Isso executará o primeiro comando, que é o
início do programa e onde se chama a função
SomarDoisValores().

A execução, que estava


parada no comando
SomarDoisValores(), saltará
para o código interno dessa
função, como vemos na figura ao lado.
Pressione [F11] novamente o observe que aparecerá, no
Terminal, a mensagem Vamos somar dois valores.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 17

Um novo [F11] fará a criação da variável valor1 e o


armazenamento do número 73.5 (um float) nela:

Aparecerá, na janela Depurador do lado esquerdo do VSCode,


o nome e o valor armazenado na variável valor1.

O próximo [F11] executará o comando que declara a


variável valor2 e nela armazena o número -15.0 (um
float) e ele será mostrado na janela Depurador:

Em seguida, um novo [F11] efetuará o cálculo


da adição de valor1 com valor2, usando o
símbolo matemático da adição (+).
Essa conta é chamada de expressão aritmética e o seu resultado
deve ser guardado em uma variável para não ser pedido. Por esse
motivo, a varíavel soma é declarada e recebe o resultado da
adição de valor1 com valor2.

Nesse ponto, nosso programa já calculou a adição dos dois valores e pode exibir o resultado na
tela. Isso será feito na execução do comando seguinte e, para executá-lo, novamente
pressionamos [F11]:

print é o comando de Python que escreve


textos na tela console. O uso da letra f no início { e } substitui a variável pelo seu
do texto indique que haverá formatação de valor na cadeia que será
dados. Durante a formatação da cadeia de exibida.
caracteres, as variáveis colocadas entre { e }
serão substituídas pelos valores que elas armazenam, de maneira que os dados dessas
variáveis aparecerão no texto exibido:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 18

Vimos nesse estudo inicial alguns conceitos importantes que podem ter passado desapercebidos.
Observamos na prática o Fluxo de Execução: a sequência pela qual os comandos de um
algoritmo (e de programas) são executados pelo computador. Podemos perceber que é
sequencial, ou seja, um comando após o outro.
Também vimos o conceito de função, que é uma sub-rotina que agrupa comandos que realizam
uma tarefa específica. São importantes para que as diversas tarefas que um programa possa ter
não se misturem e fiquem bem delimitadas.
Além disso, vimos que variáveis podem ser usadas em cálculos, como na soma de dois valores,
usando operadores aritméticos (+ e -, por exemplo).
Por fim, o comando print é bastante poderoso, pois tem flexibilidade para aceitar valores de
variáveis no texto que será escrito na tela. Esse comando é considerado um comando de saída,
pois ele escreve na tela um resultado para o usuário tomar conhecimento.
Mas esse programa tem um problema que você já pode ter percebido.
Ele sempre calculará a mesma soma, apresentando sempre o mesmo resultado. Isso ocorre
porque as variáveis valor1 e valor2 sempre receberão os mesmos dados: 73.5 e -15.0. Valores
escritos diretamente no código do programa são chamados de constantes, e nunca mudam.
Para que nosso programa possa calcular a soma de outros valores, eles não podem ser
constantes. O usuário ou usuária de nosso programa deveria digitar os valores que deseja somar,
da mesma maneira que fazemos ao realizar uma soma em uma calculadora. Mas esses valores
são de conhecimento do usuário e não do programa. Em outras palavras, eles estão fora do
programa e, para que possam ser processados pelo programa, precisam ser colocados “dentro”
do mesmo.
Para isso, usaremos a instrução de entrada de dados input.
No código que digitamos, troque a atribuição dos valores 73.5 e -15.0 para input(), de forma que
os comandos fiquem como na figura abaixo:

input() é a chamada da função que efetua uma leitura de dados a partir do teclado do
computador.
Assim, nesses novos comandos, o computador fica esperando que o usuário digite algum valor
quando usamos a função input(). O usuário deverá digitar o valor desejado e pressionar a tecla
[Enter] quando tiver terminado de digitar cada um dos valores. O valor digitado será guardado na
variável que recebe o valor de input().
Vamos executar novamente, passo a passo, para observarmos e discutirmos os resultados dessa
execução:
Conforme executamos cada passo (usando a tecla
[F11]), vamos observar que o computador ficará
esperando que digitemos os valores desejados a cada
input(). Quando terminarmos de digitar cada valor,
pressionaremos a tecla [Enter] para que o computador
saiba que o valor foi completamente digitado.

Nesse momento, o computador lerá o que digitarmos e colocará esses valores digitados nas
variáveis, ou seja, dentro da memória do computador.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 19

Mas observe que o dado digitado estará entre apóstrofes. Ou seja,


a função input() lê dados, mas os trata como strings (cadeias de
caracteres).
Portanto, Python não lê dados como inteiros ou reais, mas sim
como strings, afinal não há como a linguagem saber que tipo de
dado o usuário queria digitar.
Continuando a execução do nosso
programa, vamos efetuar a adição
dos dois valores e armazenar seu resultado na variável soma,
como vemos ao lado.
Veja que a variável soma também ficou contendo uma string,
pois o operador + foi aplicado a duas variáveis que contém strings
(valor1 e valor2). +, quando usado com strings, não faz a
adição que estamos acostumados da matemática, mas
sim junta as duas strings em uma só. Essa junção é
conhecida como concatenação.

Prosseguindo na execução, temos o


comando print(), que mostrará na tela
o resultado calculado. Observemos
que o texto exibido dirá que 12 + -
47.5 é igual a 12-47.5, de forma que
não mostramos ao usuário o resultado
esperado.
Embora nosso programa possa parecer que está correto do ponto de vista do raciocínio lógico,
temos um erro que impede que ele faça o cálculo corretamente. Ao fazermos a leitura dos dados
de entrada, Python não sabe qual o tipo de dados é desejado para esses dados e, assim, os trata
como strings.
Para resolver essa situação, teremos de dizer ao Python que trate os dados lidos como valores
reais. Isso é feito por meio de uma conversão de tipo, na qual usaremos a função float():
O resultado do input() deverá ser
convertido de string para float (real) e
isso é feito com a mudança dos
comandos como vemos ao lado:

input() sempre retorna


um texto (uma string)

float() recebe essa string e


a converte para valor real
Após a conversão de tipo, as variáveis que receberem os valores lidos e convertidos serão do tipo
float (real) e poderão ser usadas em cálculos matemáticos.
Se executarmos nosso programa novamente, passo a passo,
veremos que, agora, as variáveis valor1 e valor2 receberão
valores de tipo float, ou seja, reais (não mais como strings entre
apóstrofes).
Continuando a execução, a variável soma será declarada e seu
valor calculado, recebendo a adição (aritmética) de dois valores
reais. Assim, soma será também uma variável real, já que valor1
e valor2 são de tipo real. As figuras abaixo mostram como ficarão
as variáveis e a tela console, com o resultado sendo exibido:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 20

Quando você executou esse programa, deve ter observado que não havia nenhuma indicação de
que você deveria digitar qualquer um dos dois valores. Isso acontece porque a função input(), na
forma como a usamos, não exibe nenhuma marca ou solicitação de digitação. Seria muito melhor
para os usuários de nosso programa se este “pedisse ao usuário” que digitasse cada um dos
valores. Ou seja, o programa ficaria melhor se exibisse mensagens solicitando ao usuário que
digitasse o primeiro valor e, depois de este ter sido lido, uma mensagem solicitando a digitação do
segundo valor.
Já aprendemos que exibição de mensagens e textos na tela é feita pela função print. Portanto,
agora use essa função para solicitar ao usuário que digite os dados, mediante uma mensagem
antes da leitura de cada um dos dois valores.
O resultado esperado está
ao lado.

Esse resultado é proveniente do código


ao lado. Usamos a função print() para
escrever uma mensagem na tela,
solicitando a digitação dos dados e
fizemos a leitura usando a função
input().
Na maioria das linguagens de programação você deverá usar essa abordagem, usando um
comando de saída para exibir a solicitação de digitação de dados e um comando de entrada para
efetivamente ler o dado desejado. Mas, em Python, é possível colocar a mensagem de solicitação
da digitação dentro do próprio input(), entre ( e ), como vemos na versão abaixo de nosso
programa:

A execução de nosso programa com essas


alterações nos leva ao resultado ao lado na
tela Console:

Portanto, a versão final de nosso primeiro programa em Python ficou com o código abaixo:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 21

Sempre se lembre de salvar seus arquivos de código continuamente, para evitar perdas que
exijam retrabalho. No VSCode, as teclas [Ctrl-S] ou a opção Arquivo | Salvar do menu principal
realizam o salvamento dos arquivos de um projeto.
No programa acima podemos notar alguns importantes aspectos de Python:
1. Funções (também chamadas de rotinas ou procedimentos) são trechos de código
separados da parte principal do programa. As funções são identificadas por um nome. Este
nome é chamado (ou invocado) toda vez que se desejar executar o código da função.
2. Os comandos internos a uma função precisam ser indentados. Indentação é a operação
de digitação de linhas com um recuo em relação à margem esquerda (em Python). Isso é
feito para definir uma hierarquia em blocos de comandos. Na função SomarDoisValores(),
os comandos que estão com uma indentação à direita (feita com a tecla [Tab]) são
considerados internos a essa função, ou seja, são hierarquicamente dependentes dessa
função e são executados somente quando a função é chamada na parte principal.
3. A execução de um programa sempre inicia na parte principal, que começa no primeiro
comando não indentado (que não esteja dentro de uma função). A chamada a essa
função, na linha 11, não tem indentação, sendo assim um comando que não está
subordinado a nenhum bloco de comandos e, como é o primeiro nessa situação, é onde a
execução do programa começa.
Codificar seu programa usando funções torna o código mais legível, principalmente se cada
função for responsável por resolver um aspecto específico do problema, o que torna o código
também mais organizado.

Operadores Aritméticos

Em Python, além do operador +, que realiza adição de valores numéricos e concatenação de


strings, existem também os operadores aritméticos abaixo:

Operador O que faz Exemplos


- Subtração valor1 – valor2
* Multiplicação valor1 * valor2
/ Divisão real valor1 / valor2 (13 / 4 → 3.25)
// Divisão inteira valor1 // valor2 (13 // 4 → 3)
% Resto de divisão inteira valor1 % valor2 (13 % 4 → 1)
** Exponenciação valor1 ** valor2 (1.5 ** 4 → 5.0625)
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 22

Exercícios
Baseados em https://wiki.python.org.br/EstruturaSequencial
e
Tudor, James. Python Programming For Beginners: Learn The Basics Of Python
Programming (Python Crash Course, Programming for Dummies) (p. 46). Edição do
Kindle.

Codifique programas que leiam os dados necessários, façam os cálculos e exibam os resultados
solicitados nos exercícios abaixo. Salve os nove programas em arquivos separados na sua pasta.
2.1. Declare as variáveis e lhes atribua valores para exibir o valor de x usando os detalhes
abaixo:
x = y + (a * b / c)
y = a + 151
b = c * (144 + y)
a = 10
c=7
pedir ao usuário que informe o valor de a
2.2. Peça ao usuário que digite quatro notas bimestrais, calcule e mostre sua média aritmética.
2.3. Converta um valor digitado em metros para centímetros e exiba o resultado.
2.4. Peça o raio de um círculo, calcule e mostre sua área.
2.5. Pergunte quanto você ganha por hora e o número de horas trabalhadas no mês. Calcule e
mostre o total do seu salário no referido mês.
2.6. Peça ao usuário que digite dois números inteiros e um número real. Calcule e mostre:
o o produto do dobro do primeiro com metade do segundo.
o a soma do triplo do primeiro com o terceiro.
o o terceiro elevado ao cubo.
2.7. Tendo como dados de entrada a altura (h) de uma pessoa do gênero feminino, construa um
programa que calcule seu peso ideal, utilizando a seguinte fórmula: (62.1 x h) - 44.7
2.8. Leia dois números inteiros, calcule e exiba a soma dos valores, a subtração do primeiro
valor do segundo, a multiplicação dos dois, o resultado da divisão real entre eles, o
resultado da divisão inteira entre eles, o valor do primeiro elevado ao segundo e o resto da
divisão desse cálculo anterior pelo segundo valor digitado.
2.9. Uma loja de tintas deseja um programa que peça o tamanho em metros quadrados de uma
área a ser pintada. Considere que a cobertura da tinta é de 1 litro para cada 3 metros
quadrados e que a tinta é vendida em latas de 18 litros, que custam R$ 80,00. Informe ao
usuário a quantidades de latas de tinta a serem compradas e o preço total.

2.3. Pensamento Orientado a Objetos


Existem várias maneiras de se pensar nos algoritmos que resolvem os problemas. Uma dessas
maneiras pode ser chamada de “Pensamento Orientado a Objetos”. Nele, ao invés de pensarmos
num problema apenas do ponto de vista das ações para se chegar aos resultados, procuramos
criar um modelo da realidade que envolve o problema. A partir dessa realidade, definiremos
quais são as partes nela envolvidas e como cada uma se comporta e interage com as demais
partes.
As partes de um problema podem ser vistas como entidades que interagem entre si. Uma entida-
de é algo que pode ser definido através de suas características e do seu comportamento.
Por exemplo, pensemos novamente no problema de somar dois valores numéricos. Ao invés de
partirmos para a solução pensando nas ações que devem ser feitas para somá-los, ou seja,
pensar do ponto de vista de alguém que faz a soma, raciocinemos do ponto de vista de um objeto
que realiza a própria operação, como uma pequena calculadora bem simples que, neste
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 23

momento, “sabe” apenas realizar somas. Essa calculadora é uma entidade diferenciada e que faz
parte da solução do nosso problema específico, que é somar dois valores. Em outras palavras,
podemos ver a calculadora como a entidade principal desse problema, e as suas características
físicas, o seu estado atual e o seu comportamento é que ditarão a solução do problema. Mas, por
que essa abordagem, já que poderíamos simplesmente pensar em COMO fazer uma soma
diretamente, como fizemos na página anterior?
Afinal, podemos até mesmo imaginar uma calculadora que realizasse a adição por meio de
mágica, ou de adivinhação ou decompondo cada valor em unidades, dezenas, centenas,
somando-as separadamente. No entanto, no mundo real essas soluções não seriam adequadas
pois, na vida real prática, adições não são feitas assim.
Na abordagem de desenvolvimento de algoritmos pelo Pensamento Orientado a Objetos,
devemos criar um modelo da realidade que envolve o problema, como dissémos acima. Portanto,
nossas soluções precisam se basear em objetos do mundo real que, com sua existência, suas
características, seu estado e seu comportamento levem à solução do problema. Criaremos um
modelo da realidade, e esse modelo terá, dentro de si, a descrição de como resolver o problema,
a partir de como as entidades envolvidas no problema são e se comportam. Essas entidades
serão chamadas, por nós, de objetos.
Portanto, o “Pensamento Orientado a Objetos” é uma forma de modelar a solução de um
problema, com a criação de uma representação da sua realidade, definindo as interações que
ocorrem entre as entidades ou objetos nele envolvidos.
Então, a partir de agora, vamos basear nosso raciocínio nessa abordagem. Em primeiro lugar,
temos que entender a realidade do problema e, para isso, vamos discutir: o que é uma adição?

Adição é uma das operações básicas da aritmética. Na sua forma mais simples, a
adição combina dois números em um único número, chamado de soma, total ou
resultado. Adicionar mais números corresponde a repetir a operação. Por extensão,
a adição de zero, um ou uma quantidade infinita de números pode ser definida.
Pode também ser uma operação geométrica: a partir de dois segmentos de reta
dados é possível determinar um terceiro segmento cujo comprimento seja igual à
soma dos dois iniciais.
A adição também é usada na teoria musical dos conjuntos.
No conjunto dos números reais a adição possui as seguintes propriedades:
• Comutativa: a ordem das parcelas não altera o resultado da adição. Assim, se 2 + 3 = 5, então 3 + 2 = 5;
• Associativa: o agrupamento das parcelas não altera o resultado. Assim, se (2 + 3) + 1 = 6, então 2 + (3 +
1) = 6;
• Elemento neutro: A parcela 0 (zero) não altera o resultado das demais parcelas. O zero é denominado
como o "elemento neutro" da adição. Assim, se 2 + 3 = 5, então 2 + 3 + 0 = 5;
• Fechamento: A soma de dois números reais será sempre um número real.

[https://pt.wikipedia.org/wiki/Adição]

Vamos agora obter, a partir dessa definição, as informações que realmente são relevantes para
a modelagem da calculadora e do que ela faz. De posse dessas informações e
comportamentos, chegaremos à solução do nosso problema, ou seja, ao algorítmo que permite
somar dois números.
Uma calculadora de adição é, portanto, uma entidade que pode ser aplicada em várias situações,
existe para gerar novos números e, em determinadas situações, pode ser aplicada a elementos
não numéricos (ou outros objetos). Ela atua sobre dois números (dados ou valores), tem um
resultado do mesmo tipo que os valores somados e utiliza um símbolo (+) para ser representada
matematicamente. Existe inclusive um valor que não altera o resultado da soma (o zero).
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 24

Essa é a descrição das características da adição. Pode-


mos pensar em algo mais, como se os valores são
inteiros, reais, frações, positivos ou negativos, se atua
sobre notas musicais ou vetores… Mas, em nosso
Pensamento Orientado a Objetos, devemos apenas
focar nosso raciocínio em características e compor-
tamentos que são realmente essenciais para a resolu-
ção do problema atual. Caso seja necessário,
poderemos facilmente incluir mais características,
posteriormente. Mas, agora, devemos apenas incluir as
características que sejam estritamente necessárias,
sem colocar coisas a mais no nosso modelo, para man-
tê-lo o mais simples possível para atender as neces- Um tipo de soma que não nos interessa agora
sidades atuais do problema que desejamos resolver.

Abstração
Esse processo, de observar, durante a análise das entidades que fazem parte de um problema,
apenas as características e comportamentos essenciais, deixando de lado informações e compor-
tamentos irrelevantes no momento, é chamado de Abstração. Por exemplo, neste momento não
importa a nós se um dos comportamentos da calculadora é somar forças (como na figura acima),
apenas nos importa que ela possa adicionar números (inteiros, reais) e em que situações ela pode
fazer isso.
Note que estamos tratando a calculadora como se ela fosse um “ser ativo”, que “sabe” agir ou
realizar ações dentro da sua natureza de objeto aritmético. Assim, ela “saberia dizer” se tem os
dois valores disponíveis, sabe somá-los, sabe expor (exibir, mostrar) o resultado. Claro que, na
vida real, é um ser humano (geralmente) que faz essas operações acontecerem, mas, na análise
orientada a objetos, vamos supor que é a entidade modelada (a calculadora) que possui essas
capacidades.
Aspectos da calculadora, como sua cor, formato, material, etc, são, neste caso, irrelevantes e
podem ser deixados de fora neste momento. Assim, não precisamos pensar numa calculadora
concreta, fisicamente existente, mas sim num modelo abstrato, mais simples do que seria uma
real.
A abstração é um processo de pensamento através do qual modelamos uma entidade,
priorizando as características e comportamentos importantes, para reduzir a sua
complexidade. O resultado da abstração é o modelo que soluciona o problema. Portanto, a
abstração será sempre dependente da situação (ou contexto) em que se insere o problema que
desejamos resolver.
O problema de fazer uma calculadora somar será diferente do problema de fabricar a calculadora.
Em geral, a modelagem dos dois problemas será diferente. Em comum aos dois haverá a
calculadora, mas as ações realizadas em cada um serão diferentes, ou seja, comportamentos
diferentes serão obtidos nos dois modelos.

Classificação
Observe que o ideal é que as operações que modelaremos funcionem para operar qualquer tipo
de calculadora, sem levar em conta suas características físicas. Isso é recomendável porque,
assim, poderemos usar o que aprendermos no modelo de uma calculadora básica para a criação
de modelos de calculadoras mais complexas. Em outras palavras, todas as calculadoras do
mundo podem ser diferentes, mas elas possuem características comuns que nos permitem
classificá-las como calculadoras.
Classificação é a operação mental, de raciocínio abstrato, que nos permite agrupar entidades
semelhantes numa mesma categoria. Por exemplo, um Fiat Stilo e o Ford Focus do seu vizinho
são diferentes entre si, mas tem a mesma categoria, ou classe: são automóveis. Portanto, esses
dois objetos pertencem à classe Automóvel. São entidades reais e concretas, que possuem as
características e comportamentos esperados de objetos da classe Automóvel. Essas entidades,
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 25

reais e concretas, criadas a partir do modelo que uma classe define, são chamados de instâncias
da classe ou, simplesmente, objetos.
Observe que o carro de seu vizinho não é, fisicamente, o seu Fiat Stilo mas ambos são
automóveis e assim são classificados. Concluímos que cada objeto tem sua própria existência e
características, e todos os objetos que apresentam os mesmos atributos (características) e
comportamentos (operações) podem ser considerados como pertencentes a uma mesma
classe ou categoria.
Uma classe, portanto, é uma categoria de objetos que possuem atributos e comportamentos
comuns. A operação mental de Classificação nos permite definir qual a classe de um objeto, ou
seja, quais as características e comportamentos que um objeto tem em comum com outros.
Assim uma Calculadora, enquanto entidade, é uma classe de objetos que possuem
características comuns, como seu objetivo, suas operações, o material de que são feitas.
No entanto, cada calculadora específica será baseada nessa classe genérica de entidades
“Calculadora”, pois seguem o modelo definido para esse tipo de entidade, mesmo que suas
características físicas, como cor, material, operações que realiza, etc., sejam diferentes para cada
exemplar físico de calculadora existente no mundo real.

Modelo de Classe
Após essa discussão “filosófica”, vamos passar a pensar no modelo propriamente dito. Como já
discutimos, a descrição de uma classe é composta por duas partes:
• Atributos – descrevem características e informações que exemplares dessa classe possuem
• Comportamentos – descrevem operações que os exemplares dessa classe podem realizar
Podemos representar o modelo de uma classe na forma de um Classe
Diagrama de Classe, como vemos na figura ao lado:
Atributos
Os atributos são os valores (os dados) que a classe armazena, aqueles
valores que serão processados pela classe. Na nossa classe Comportamentos
Calculadora, quais seriam os atributos? São os dados que a calculadora
irá usar para realizar suas funções.
Calculadora
Vendo a definição do que é uma adição, lemos que ela opera sobre dois
primeiroNumero
números e gera um resultado. Esses números e o resultado são os
segundoNumero
valores processados na Calculadora e, por isso, eles podem ser
resultado
colocados no diagrama de classe, como vemos ao lado:
Veja que colocamos no diagrama indicações sobre valores que poderão
ser usados nessa classe, sem nos importar ainda com quais operações deverão ser realizadas no
uso desses valores. Assim, podemos acoplar quaisquer operações aritméticas que atuem sobre
dois números e gerem um resultado, pois não especificamos ainda o que a Calculadora fará.
Neste momento da nossa análise, ainda não começamos a pensar no que uma calculadora é
capaz de fazer. Assim, por enquanto, podemos deixar a parte de comportamento sem descrição.
Essa é uma outra vantagem do pensamento abstrato: coisas e informações que ainda não
conhecemos podem ser deixadas de lado, para serem incorporadas ao modelo quando tivermos
maior conhecimento dos detalhes dessa entidade.
A nossa calculadora “saberá” realizar algumas operações que, em conjunto, definem o
comportamento da calculadora. Pense no que uma calculadora real faz: ligar, desligar, limpar o
visor, ler os valores digitados, calcular operações aritméticas, mostrar os resultados, guardar
resultados numa memória interna, por exemplo.
Podemos pensar nessas operações como um script (roteiro) de uma peça de teatro, que descreve
as ações que um ator poderá realizar no decorrer da peça. Assim, nossa calculadora será como
uma atriz, que deverá obter os números, deverá fazer somas, deverá mostrar o resultado, deverá
limpar esse resultado no visor, bem como saber ligar-se ou desligar-se.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 26

Para uma peça de teatro funcionar, ela precisa de um diretor, que define os papéis de cada ator e
atriz, papéis esses que englobam as falas e atitudes (ações) que cada ator terá de realizar. Esse
diretor também indica em que momento cada ação do script deverá ser realizada,e por qual ator.
Em computação, essas tarefas serão realizadas por um módulo chamado programa principal,
que será bastante parecido com o algoritmo de soma que estudamos no início deste capítulo.
Aquele primeiro algoritmo foi feito com uma abordagem de Pensamento Estruturado, onde
focamos diretamente na sequência de ações que faríamos sem uma calculadora, caso
seguíssemos nós mesmos a maneira de resolver uma soma, que aprendemos nas aulas de
Matemática.
Mas, quando trabalhamos com o Pensamento Orientado a Objetos, teremos pelo menos dois
módulos:
1. a classe que engloba os diversos aspectos da solução do problema, sem se preocupar
com a forma como esses aspectos serão usados; no nosso caso, a classe Calculadora;
2. o programa que organiza a sequência de uso dos recursos que um objeto dessa classe
possui, como se fosse o diretor da peça da nossa analogia.
Levando isso em conta, pensemos um pouco mais como seria um programa principal (Main) que
usasse um objeto da classe Calculadora para definir o roteiro de trabalho (o papel, a fala, as
atitudes) desse objeto (como se este fosse o ator ou atriz) e vamos descrevê-lo abaixo:

Função Principal(): # organiza a sequência de atuação da classe


Obtenha uma Calculadora como a que estamos modelando;
Calculadora → Ligar-se;
Calculadora → Obter Números; Calculadora → ação
Calculadora → Somar; Pede para a calculadora executar essa
Calculadora → Exibir Resultado; ação (atuar). Essa ação deverá estar
Calculadora → Desligar; prevista no modelo da classe

O algoritmo acima está escrito de forma bastante livre, pois é um primeiro esboço de como seria o
roteiro de trabalho com o objeto calculadora. Veja que o foco, aqui, é nesse objeto. É ele que
realiza as operações que o programa determina, e é o programa que define a ordem de realização
dessas tarefas.
Mas, para que isso funcione, precisamos que todos esses aspectos estejam previstos no modelo
da classe.
Portanto, continuaremos a criar nosso modelo abstrato da classe Calculadora, passando agora a
definir as ações que ela realizará, algumas das quais até mesmo pensamos há pouco.
Poderíamos ter pensado apenas na operação de somar, mas vamos nos lembrar das calculadoras
“fisicas” que já usamos antes: antes de somar os valores, o que era necessário fazer?
Basicamente, ler os valores no enunciado do problema do livro de Matemática e digitar esses
valores no teclado da calculadora, para que sejam conhecidos e, assim, possam fazer parte da
soma. Portanto, uma das operações que nosso objeto virtual de calculadora precisa realizar é a
obtenção dos números que serão processados por ela. Assim, já temos duas operações
necessárias: obter os números e somar os números que já foram obtidos.
Além disso, quando estamos resolvendo um problema de soma de dois números do livro de
Matemática, temos que escrever o resultado da soma, não é mesmo? O mesmo acontece com a
calculadora física: o resultado é exibido no seu visor.
Além disso, nos lembramos de algumas outras operações que a calculadora física tem: ligar,
desligar, limpar o visor, e algumas outras possíveis que são irrelevantes neste momento da
nossa análise do problema de somar dois números (como, por exemplo, fazer a divisão dos
números).
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 27

Assim, nosso modelo de classe passa a ter o diagrama ao lado, onde Calculadora
já incorporamos algumas das operações que podem ser realizadas por
uma calculadora e que atendem à solução do nosso problema especí- primeiroNumero
fico, que é conhecer o resultado da soma de dois números: segundoNumero
resultado
Ações irrelevantes, mesmo que possíveis, não serão postas no
modelo. Ligar()
Assim, já temos as duas partes da classe descrita: seus atributos e Desligar()
seu comportamento. O diagrama de classe é uma maneira útil de ObterNumeros()
agrupar o conhecimento e raciocínio que desenvolvemos sobre a Somar()
classe que soluciona o problema, enquanto estamos analisando e ExibirResultado()
aprendendo sobre esse problema, buscando organizar mentalmente a LimparVisor()
sua solução. ...
Os itens descritos na parte superior são chamados de atributos ou
variáveis membro da classe. Quando essa classe estiver sendo usada por um programa de
computador, os atributos agem como variáveis internas dessa classe e, assim, guardarão
valores que serão tratados pelas operações da classe, cujos algoritmos internos solucionam o
problema.
Os itens descritos na parte inferior, o comportamento, são chamados de métodos da classe.
São blocos de código (instruções de algoritmos) que farão tarefas específicas para obter os
resultados.
Essa separação das operações em blocos distintos ou métodos, evita a mistura de assuntos.
Por exemplo, os comandos para obtenção dos números não se misturam com os comandos que
fazem a soma, nem se misturam aos comandos que exibem o resultado na tela.
Portanto, cada método é responsável por realizar apenas instruções bastante específicas,
relacionadas com a função (o objetivo) de cada método dentro do que se espera dessa classe.
Assuntos diferentes não ficam misturados na codificação do algoritmo e isso o torna mais legível,
compreensível, gerenciável e facilita o aprimoramento desses métodos quando for necessário.

Encapsulamento

Observe, então, o diagrama de classe acima: podemos vê-lo como uma cápsula, que contém,
dentro de si, duas partes principais: os atributos da categoria de objetos que modelamos (os
dados), e as operações que objetos dessa categoria são capazes de realizar (os métodos).
Esse resultado da abstração é chamado de encapsulamento: as características e comportamen-
tos de um objeto são “intrínsecas” (internas) a ele, não existindo separadas desse objeto. Assim,
Ligar() da classe Calculadora é diferente de Ligar() da classe Avião ou da classe Automóvel.
As operações de um objeto ficam dentro dele mesmo, encapsuladas, junto aos atributos. Não se
pode ter um objeto sem ter, ao mesmo tempo, as características e os comportamentos que
esse objeto possui e que, juntos, o definem.
Observe na figura da cápsula de remédio que ela é composta por duas
partes. Imagine que uma delas contenha os ingredientes (estado, atributos,
características) e a outra contenha as ações (comportamento, métodos)
que o remédio tem sobre a pessoa que o ingere (que o executa).
A pessoa que ingere o remédio é como se fosse o programa que direciona
as operações que esse remédio fará no organismo. No entanto, somente
os ingredientes que estão dentro da cápsula e os efeitos (ações) que essa
cápsula proverá é que poderão ser usados pelo organismo.
Por isso é que, no algoritmo inicial acima, o programa manipula a Calculadora (remédio) e pede a
ela que execute as suas funções (operações), que atuarão sobre os dados (ingredientes da
cápsula), para obter o resultado esperado (efeitos do remédio).
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 28

Essas responsabilidades estão bem divididas, entre o programa (organismo da pessoa que usa o
remédio) e o objeto remédio (entidade que possui ingredientes e efeitos medicamentosos). Em
conjunto, elas chegam ao resultado esperado, que seria a cura de alguma doença.

Instanciação

Quando construímos um objeto a partir da descrição feita no modelo da classe, estamos fazendo
uma operação de instanciação, ou seja, estamos dando existência (uma instância) a um exem-
plar específico dessa classe. Os comandos do programa poderão usar esse exemplar em suas
operações, dando valores aos atributos e executando os métodos previstos na classe, para
realizar os cálculos e as ações codificados em cada método.
Calculadora
primeiroNumero
segundoNumero
resultado
Nome da classe Classe ou
Categoria
Ligar()
Desligar() Estado ou Atributos (dados)
ObterNumeros()
Somar() Comportamento ou Métodos (operações)
ExibirResultado()
LimparVisor()
...

é uma
Calculadora de
seu vizinho
Objetos
sua calculadora Instâncias
Programa Entidades

Num programa, usaremos a descrição da classe como um modelo (exemplo) para a criação de
instâncias dessa classe, ou seja, criar objetos que terão seus dados processados pelo
computador por meio das operações que a classe realiza. Observe que a instância possui um
relacionamento com a classe, chamado de relacionamento “É um” ou “É uma”. A sua calculadora
é uma instância da classe Calculadora, da mesma forma que a de seu vizinho. Cada uma delas é
uma Calculadora.
O programa é o ambiente de software onde a calculadora existirá, como se desse vida a ela, a
criasse e acompanhasse seu ciclo de vida.

Codificando o algoritmo da soma

Agora que já temos um esboço inicial da nossa classe, feito com o diagrama, podemos passar a
pensar na codificação de um algoritmo que solucione nosso problema, usando as ideias presentes
no Pensamento Orientado a Objetos.
Usando o processo de Pensamento Orientado a Objetos, criamos um modelo da calculadora, no
qual descrevemos suas características (estado, atributos) e seu comportamento (operações que
realiza, métodos). Esse modelo será transformado em um texto que o computador possa
entender, e será a base para o desenvolvimento da lógica do programa que queremos
desenvolver.
O modelo, em formas de diagrama e de linguagem de programação (texto), está descrito a
seguir.
Na prática, teremos dois arquivos de texto, um com a descrição da classe e outro com
comandos de um programa. Nesses dois arquivos codificaremos as declarações de variáveis e
os comandos que especificam o que nossa classe e nosso programa farão em conjunto.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 29

Assim, expressaremos o significado do diagrama de classe por meio de textos escritos de tal
forma que atendam às regras da linguagem de programação (sintaxe) e nos permitam criar a
aplicação que, depois de compilada e traduzida para a linguagem de máquina, o computador
poderá executar.
Cada tipo de declaração e de comando, tem uma maneira correta de ser escrito, e o computador
não consegue fazer a tradução para a linguagem de máquina se essas regras de escrita não
forem seguidas fielmente. Vamos conhecer algumas dessas regras a seguir.
Em programação, uma classe é um grupo de dados e operações que será processado pelo
computador. Para declararmos uma classe e seus componentes internos, sempre usaremos a es-
trutura geral descrita abaixo e digitaremos essas palavras e símbolos no arquivo texto onde a
classe ficar:
class <Entidade modelada>: → indica que estamos descrevendo uma classe de objetos
Atributos <...> → descrição das propriedades/características de um objeto
Comportamento <...> → descrição do que um objeto é capaz de fazer

Substituiremos os itens entre < e > por palavras adequadas a cada local onde esses itens
apareçam.
Para a nossa classe de Calculadora, vamos seguir a forma geral acima, pensando primeiramente
em como descrever as características de uma calculadora qualquer. Já pensamos nas
propriedades descrevem uma calculadora e que são comuns à maioria delas, como cor, material,
formato, botões, tipo de visor, nos números que serão usados nos cálculos, dentre outras
características. Dessas propriedades, selecionamos aqueles essenciais para a solução do nosso
problema e deixamos de lado as irrelevantes, e registramos esses atributos no diagrama de
classe. Assim, com base no diagrama de classe, começamos a codificar a classe Calculadora e
seus atributos da forma abaixo:

class Calculadora: Em Python, atributos de classe são declarados


def __init__(self): dentro do método inicializador __init__()
self.priNumero = 0.0 precisaremos de dados, que são os números a
self.segNumero = 0.0 serem somados e um resultado. Esses valores
self.result = 0.0 são representados por essas variáveis ao lado,
... aqui definiremos outros métodos (comportamento)

O comportamento agrupa os métodos (funções), que realizarão as operações que se espera


que a classe realize. Continuamos a codificar, declarando os métodos que previmos no
diagrama de classe. A declaração de um método exige a palavra def, o nome do método e (self).
Todo método, portanto, tem ( e ) logo após seu nome. Essa é uma regra de nomenclatura seguida
por várias linguagens de programação e, também, a usaremos em Python. No entanto, Python
também exige que cada método de uma classe receba um valor especial, que chamaremos de
self, escrito entre os parênteses após o nome do método. self representa (referencia) a classe à
qual um atributo ou um método pertencem (self.priNumero é o atributo priNumero da própria
classe Calculadora).

def Ligar(self):
… comandos desse método
def Desligar(self):
… comandos desse método
def ObterNumeros(self):
… comandos desse método
def Somar(self):
… comandos deste método
def ExibirResultado(self):
… comandos desse método
def LimparVisor(self):
… comandos desse método
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 30

Dentro de cada método, codificaremos os comandos que realizam as ações necessárias para
chegar à solução do problema, usando os dados que serão guardados nos atributos da classe.
Por exemplo, dentro do método Somar(), escreveremos ordens (comandos, ações, instruções)
que explicarão ao computador como realizar a soma do priNumero com o segNumero e calcular
o result. Esses comandos serão escritos indentados (deslocados com uma tabulação) em cada
método declarado acima.
Como ainda não nos aprofundamos no raciocínio sobre como cada método realizará suas
operações, não sabemos quais comandos existirão dentro de cada método. Por isso, acima
indicamos apenas “… comandos desse método” para representar os comandos que ainda serão
inseridos dentro de cada bloco, indentados à função definidora do método.

Diagrama de Classe Código (arquivo com codificação da classe)

class Calculadora:
Calculadora def __init__(self):
self.priNumero = 0.0
primeiroNumero self.segNumero = 0.0
segundoNumero self.result = 0.0
resultado
def Ligar(self):
Ligar() … comandos deste método
Desligar() def Desligar(self):
ObterNumeros() … comandos deste método
Somar() def ObterNumeros(self):
ExibirResultado() … comandos deste método
LimparVisor() def Somar(self):
… comandos deste método
def ExibirResultado(self):
… comandos deste método
def LimparVisor(self):
… comandos deste método
O programa é um elemento de software que usa uma classe para resolver um problema. A
solução do problema está descrita na classe, nos comandos de cada método, que tratam os
valores segundo o algoritmo da solução. O programa é como se fosse um hospedeiro, que dá vida
à classe (cria uma instância da classe) e usa os seus comportamentos para solucionar um
problema. Na maioria das vezes, cabe ao programa interagir com o usuário, solicitando dados e
mostrando resultados.
A classe Calculadora já está iniciada, mas o que faremos agora? Codificaremos os comandos in-
ternos de cada método, ou seja, escreveremos o algorítmo de cada método. Essa é a programa-
ção propriamente dita. Detalharemos cada método, codificando as ações que ele realiza.
Para somar dois valores, devemos antes obter esses valores, correto? Assim, descrevemos as
operações de obtenção dos números com os comandos de algoritmo que se seguem. Vamos
supor que eles serão digitados pelo operador do programa através do teclado.
Seria interessante avisar esse operador para digitar os dados pois, sem isso, ele não terá como
adivinhar que o programa está esperando pelos dados. Portanto, complementamos o método
ObterNumeros() da classe com as linhas abaixo:

def ObterNumeros(self):
self.priNumero = float(input(“Digite o 1º valor: ”))
self.segNumero = float(input(“Digite o 2º valor: ”))
Quando este método for chamado pelo programa principal, seus
comandos internos serão executados um a um, do primeiro ao último, na
ordem de cima para baixo em que foram codificados .

self é uma variável que representa o objeto que está usando essa classe (a instância da classe).
Como as variáveis priNumero e segNumero são atributos encapsulados na classe Calculadora,
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 31

self.priNumero significa que estamos acessando o atributo priNumero desse objeto, e que nele
será armazenado o valor digitado e convertido no input. O mesmo acontece com self.segNumero.
input() realiza entrada de dados, lendo uma string do teclado do computador. Nos comandos
acima, a string lida é passada para a conversão de tipo da função float(), cujo resultado é
armazenando no atributos à esquerda do símbolo = . Portanto, os atributos são variáveis onde
guardamos os dados que serão processados. Os atributos também são chamados de variáveis
de classe, pois seus valores variam de acordo com a necessidade do processamento.
Quando os comandos acima forem executados pelo programa, os dados que o usuário digitar
serão guardados (armazenados) nos atributos priNumero e segNumero. Após disso, eles poderão
ser usados quando for necessário. Para isso, basta a classe Calculadora usar esses nomes de
atributos nos comandos que formam o algoritmo e que serão executados pelo computador.
Observe, na página anterior, a descrição textual da classe. À direita de cada nome de atributo,
temos a atribuição de um valor 0.0. Isso determina o tipo de dado dessas variáveis, que serão
reais. Existem vários tipos de informação que um programa pode processar como, por exemplo,
números inteiros, números reais, cadeias de caracteres, dentre vários outros. Assim, todos os
atributos de nossas classes precisam, ao ser declarados, receber um valor que determinará o tipo
de dado, para que o computador saiba quais operações podem ser feitas com esse atributo.

Comando de atribuição de valor a variável

Os valores digitados serão armazenados nas variáveis membro (ou atributos) primeiroNumero e
segundoNumero, e isso é feito por meio do comando =, também chamado de comando de
atribuição, pois sua função é atribuir, para a variável à sua esquerda, o valor que foi informado,
digitado ou calculado à sua direita. Podemos ler e interpretar esse comando como
priNumero recebe o que foi digitado no teclado
Vamos imaginar que o programa já está pronto e sendo executado, e que o programa “chamou” o
método ObterNumeros(). Assim, as mensagens pedindo a digitação dos dados foram exibidas,
uma a uma e usuário digitou, no teclado, os valores 17 e 42. O nosso objeto de calculadora
(umaCalc) ficará na memória do computador, e terá aparência semelhante à abaixo:
umaCalc
priNumero 17 segNumero 42 result 0

Na figura acima, supomos que o método ObterNumeros() já foi executado pelo programa.
Continuemos a codificação da nossa classe e do programa que dará vida a ela. O método
ObterNumeros() resolve o problema de somar dois valores? Ainda não, pois esses comandos
apenas escrevem na tela um pedido para o usuário digitar os valores a serem somados e os leem
quando o usuário os digitar. Então, temos que continuar codificando os comandos de cada
método descrito na classe.

def Somar(self):
self.result = self.priNumero + self.segNumero;

O método Somar() primeiramente calculará o valor da adição dos valores que estiverem
guardados (armazenados) nos atributos priNumero e segNumero e, logo em seguida, guarda o
valor calculado no atributo result.
A codificação desse comando segue a notação matemática que estamos acostumados. O símbolo
+, como já sabemos, realiza a adição dos dois valores que estão, respectivamente, à sua
esquerda e à sua direita. O computador acessará os locais da sua memória onde esses valores
estão guardados, traz uma cópia deles para o processador (CPU), que ai faz o cálculo da adição.
Em seguida, o valor resultante dessa adição será armazenado na variável resultado, e isso é feito
por meio do comando de atribuição = . Podemos ler esse comando como
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 32

resultado recebe primeiroNumero mais segundoNumero

O valor que foi calculado ficará armazenado (guardado) no atributo resultado e estará disponível
sempre que for necessário. Imaginemos, então, que depois de chamar o método ObterNumeros(),
o programa chamou o método Somar(). Logo depois que Somar() for executado, a disposição do
objeto umaCalc na memória do computador será a seguinte:
umaCalc
priNumero 17 segNumero 42 result 59

Ou seja, a “caixinha” resultado recebeu o valor da adição de 17 com 42, e seu valor passou de 0
para 59, que é o valor resultante da soma dos valores digitados e armazenados anteriormente.
Podemos usar essa figura para visualizar, de uma maneira inicial, o conceito de encapsulamento:
a instância (ou objeto) umaCalc encapsula (guarda dentro de si) os valores dos atributos
primeiroNumero, segundoNumero e resultado.
Para acessar o valor armazenado no atributo resultado, basta aos comandos do programa
referenciarem o nome desse atributo, como vemos no próximo método:

def ExibirResultado(self):
print(f"{self.priNumero} + {self.segNumero} vale {self.result}")

Em Python, o símbolo f na função print() informa ao computador que


os elementos entre { e } são variáveis e que seus valores devem ser
buscados na memória para serem escritos na tela.

Em ObterNumeros() usamos a função input() para escrever uma mensagem textual na tela, ou
seja, as palavras e demais símbolos entre “ e “ foram escritos literalmente, letra a letra, símbolo a
símbolo. Já na função print() acima, temos que escrever o valor dos dados digitados,
armazenados nos atributos priNumero e segNumero e o valor da adição, armazenado no
atributo result.
A forma de escrever acima indica que, dentro da mensagem após o f, as variáveis e expressões
colocadas entre { e } serão substituídas pelos respectivos dados que estão armazenados nesses
atributos (variáveis), ou calculados no momento da execução desse comando (se há uma
expressão aritmética entre { e } ). f indica que a string a ser exibida será formatada e essa
operação de inserir valores de variáveis dentro de strings é conhecida, em algumas linguagens de
programação, como “Interpolação de String”.
O computador, ao ser informado disso, buscará na memória os valores armazenados em cada um
dos atributos referenciados entre { e } e exibirá seus dados na tela, exatamente no local onde
aparecem os nomes dos atributos. Nessa exibição, os símbolos { e } não serão mostrados, mas
os demais símbolos entre “ e “ serão mostrados como, por exemplo, espaços, o símbolo + e a
palavra vale. Para os valores citados anteriormente, teríamos a mensagem abaixo exibida na tela:

17 + 42 vale 59

Como você pode observar, codificar um programa envolve habilidades mentais de várias nature-
zas. Temos o próprio raciocínio lógico, que determina quais dados serão processados e quais
ações deverão ser realizadas para solucionar um problema. Mas, além desse tipo de habilidade,
precisamos também adquirir novas habilidades para conhecer e saber seguir as regras da
línguagem de programação usada para desenvolver nosso programa. Por exemplo, escrever ( e )
após o nome de um método é uma regra que devemos sempre aplicar.
Assim, será mais simples a transição entre este momento de desenvolvimento de habilidades
mentais de raciocínio lógico e de habilidades de codificação e o momento de codificação de
programas reais mais complexos.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 33

Mas é importante salientar que a principal ferramenta das programadoras e programadores é o


seu próprio cérebro, as habilidades mentais que já possui e que serão essenciais para o
desenvolvimento das novas habilidades necessárias para a criação de programas. Criar um
programa não necessita de um computador ou de um ambiente de desenvolvimento, mas sempre
se inicia com a análise dos aspectos que envolvem um problema, a concepção mental de uma
solução e a modelagem dessa solução usando o Pensamento Orientado a Objetos para, somente
depois disso, usar um computador e uma ferramenta computacional de desenvolvimento de
programas para gerar a solução em um computador e várias pessoas poderem usar seu programa
para solucionarem os problemas que seu programa resolve.
É essencial entender que, neste momento, estamos criando as bases para o desenvolvimento de
seu raciocínio lógico (que você certamente já possui) em conformidade com os preceitos do
Pensamento Orientado a Objetos. Certamente que haveria formas mais simples de criar a solução
para o problema de somar dois valores. Veja a solução descrita no início deste capítulo, que usa
uma forma de pensamento chamado Pensamento Estruturado, e que era bastante usado até a
década de 1990. Mas, atualmente, formas mais avançadas de desenvolvimento são exigidas pelo
mercado de trabalho e, por isso, aqui estamos aprendendo uma dessas formas. O que estamos
aprendendo aqui será muito usado com problemas mais complexos e, consequentemente, que
precisam de classes mais avançadas e robustas para serem usadas na solução de problemas.
Dando sequência à nossa classe e, logo mais, ao nosso programa, vamos codificar os métodos
que restam na classe Calculadora, e que explicitamos abaixo:

def LimparVisor(self):
os.system('cls') or None # Este comando limpa a tela no Windows
self.result = 0.0 # para usá-lo, precisamos importar biblioteca os
def Ligar(self):
self.LimparVisor()
self.priNumero = 0.0
self.segNumero = 0.0
def Desligar(self):
self.LimparVisor()
print("Saindo da memória!")

Observe que o método Ligar(), em seu primeiro comando interno, usa o método LimparVisor().
Esse tipo de comando tem o nome de “chamada de método”. O fluxo de execução, quando
chega nesse comando, invoca (chama, pula para) o método LimparVisor() e, assim, passa para
ele, ou seja, vai executar, sequencialmente, todos os comandos internos ao método LimparVisor().
Nesse caso, o atributo resultado receberá o valor 0. Após os comandos do método chamado
(LimparVisor()) terem sido executados, o fluxo de execução retorna para o comando seguinte ao
comando em que essa chamada foi feita. Assim, após executar LimparVisor(), o fluxo retorna para
o segundo comando do método Ligar() e, na sequência, zera priNumero e segNumero.
Ou seja, um método de uma classe pode chamar outro método dessa classe. O fluxo de
execução sai do método que chama, entra no início do método chamado, executa os comandos
internos do método chamado um após o outro e, por fim, retorna para o local original da chamada
e continua a execução do método que chamou, a partir do comando seguinte ao de chamada.
Cada um dos métodos que já codificamos não tem grande serventia quando tratados isoladamen-
te, mas, se forem executados em sequência com outras ações da classe, servirão para solucionar
o problema sob análise, através da realização das várias ações individuais que uma calculadora
contempla e que, executadas na sequência correta, realizam as operações aritméticas desejadas.
Essa classe resolve o problema de somar dois valores? Aparentemente sim, desde que o usuário
digite números (pois os atributos, quando declarados, receberam valores de tipo real / float). Caso
digite uma palavra, haverá um erro de execução e execução do programa será cancelada. Nossa
classe, neste momento, não prevê o tratamento de situações de digitação errada dos dados.
Isso nos mostra que uma classe só pode realizar as ações que estão previstas, descritas,
encapsuladas nela. Não podemos, por exemplo, fazer subtrações nessa classe, a menos que o
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 34

usuário digite o segundo número como um valor negativo. Mas se quiséssemos fazer
multiplicações ou divisões, essa classe não o faria, embora se chame Calculadora e, em geral,
calculadores realizem vários tipos de operações aritméticas.
Mas o ato de realizar subtrações, multiplicações e divisões não estão encapsulados na classe
Calculadora que modelamos neste momento (ainda não, ao menos).
Essa situação é, na verdade, vantajosa para o desenvolvimento de programas, pois o modelo
também serve para que não inventemos coisas que não estão previstas, a fim de achar a
solução usando subterfúgios, sem pensar realmente no problema. Isso quer dizer que o modelo
de uma classe nos informa o que os objetos dessa classe podem fazer, mas, por exclusão,
impede que objetos dessa classe realizem operações não previstas.
Em outras palavras, o modelo de classe limita o que um objeto pode realizar e, assim, evita que
façamos “coxambras” nem "gambiarras". Somente os atributos declarados no método inicializador
__init__() e os métodos codificados dentro da classe podem ser usados pela classe. Caso outras
operações ou atributos passem a serem necessários, eles devem ser codificados após terem sido
analisados por meio do raciocínio lógico.
A classe Calculadora está, neste momento, com o seguinte código de programação:

Declaração de atributos é feita no método


inicializador __init__(self); escrevemos o
nome do atributo e lhe atribuimos um valor
inicial, que definirá o tipo do atributo (real).
self indica que é variável própria da classe

Declaração de método de classe envolve


escrever o comando def, o nome do método,
seguido obrigatoriamente de (self):

Métodos de classes em Python sempre devem


ter o parâmetro self. Se necessários, outros
parâmetros podem ser declarados após self.

Os comandos internos de um método devem


ser indentados em relação ao comando def.

Esses comandos chamam o método


LimparVisor(), porque se deseja executar
as ações programadas nesse método.
Observe o uso de ( e ) quando se chama
um método.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 35

Ao programarmos num computador, esse texto será digitado em um arquivo texto, chamado de
calculadora.py e será usado por um programa principal, que ficará em outro arquivo (main.py).
Os dois arquivos devem ser salvos na mesma pasta na unidade de armazenamento.
O programa usará a classe Calculadora para realizar, na ordem correta, as operações que
permitem a soma de dois números. Assim, vemos o texto acima sozinho não é um programa, mas
fará parte de uma Solução Computacional que terá dois módulos: (1) a classe que encapsula
atributos e operações que resolvem o problema e (2) o programa que usará essa classe, fará a
comunicação com o usuário (teclado, tela, etc.) e organizará a execução dos métodos da classe,
chamando-os na ordem correta para que a soma possa ser feita.
As ações que a classe prevê e sabe realizar não podem ser executadas em qualquer ordem.
Assim, o programa, como ambiente de execução da classe, seu hospedeiro, ou organizador
da execução dos métodos, será responsável por dar vida à classe, fazer a leitura antes da soma,
a exibição do resultado apenas depois que os dados foram digitados e somados.
Vamos agora, codificar o programa que usará essa classe para interagir com o usuário e permitir
que se calcule e se conheça a soma dos valores digitados.

Programa Principal e Instanciação de Objetos


Neste momento, a classe Calculadora já contém os atributos e os métodos que precisamos para
modelar uma calculadora que sabe somar dois números. Mas, para que a calculadora realmente
some valores, precisamos de um programa principal que utilize essa classe e crie uma instância
da classe Calculadora para processarmos. Ou seja, precisamos do programa para hospedar um
exemplar (uma instância) da classe Calculadora na memória do computador, porque todas as
informações que um computador processa precisam ficar na sua memória para poderem ser
acessadas e tratadas (usadas) pelos comandos programados. O programa principal ficará, em
geral, em outro arquivo. Podemos chamá-lo de main.py, por exemplo.
Para podermos usar a classe Calculadora no arquivo main.py, precisamos importá-la. Isso é feito
com o comando de importação import, que será o primeiro comando do arquivo com o programa
principal, como vemos abaixo:

import calculadora
Esse comando deixará o arquivo com o código completo da classe Calculadora disponível para
uso.
Em seguida, devemos codificar o programa principal, cuja execução se inicia no primeiro
comando não indentado do arquivo main.py. Para que nosso programa possa usar a classe
Calculadora, temos de declarar uma variável que encapsule essa classe. Em outras palavras,
essa variável será um objeto que conterá, dentro de si, todos os comandos e declarações da
classe Calculadora, ou seja, será uma instância da classe Calculadora.
Para criarmos uma instância de uma classe temos que chamar um método especial, que toda
classe possui, e que é chamado de método Construtor. Em Python, o construtor tem o mesmo
nome que a classe. Quando quisermos realizar as ações de uma classe, como a Calculadora, por
exemplo, temos de criar um exemplar dessa classe antes, com um comando semelhante a:

umaCalc = calculadora.Calculadora()
instância recebe arquivo construtor da classe

Acima, a variável umaCalc recebe uma instância da classe Calculadora, ou seja, passa a ser um
objeto da classe Calculadora e poderá acessar os métodos e atributos dessa classe.
Isso ocorre na memória do computador e, com esse comando, passamos a ter uma instância
(uma cópia, um exemplar, por assim dizer) da classe Calculadora para que o programa possa
usá-la.
Consequentemente, precisamos do que chamamos de Programa Principal, que é um outro
elemento de código e que fica, geralmente, num arquivo separado da classe. Esse código deverá
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 36

instanciar os objetos das classes que o programa utiliza e chamar seus métodos na ordem correta
para obter os resultados corretos desejados. Abaixo temos o código de nosso programa principal:

Aqui chamamos o Construtor, para termos


uma instância (um exemplar) da classe
Calculadora com a qual podemos fazer a
soma

umaCalc.Ligar()
objeto.método():
Indica que serão executadas as ações que
a classe tem codificadas para o método()

umaCalc é uma variável (uma região da memória) que armazenará um objeto criado com base na
descrição da classe Calculadora. umaCalc terá dentro de si os atributos declarados na classe
Calculadora e os métodos que foram codificados nessa classe. Calculadora() constrói esse objeto.
Compare o código de nosso programa com o algoritmo inicial que fizemos anteriormente, quando
raciocinamos sobre as ações que um objeto Calculadora deveria realizar e, também, em qual
sequência essas ações deveriam ocorrer, para chegarmos na soma dos dados.
O fluxo de execução se inicia no comando da linha 4 (primeiro comando não indentado) do
bloco principal e segue de cima para baixo até o final do programa, executando comando a
comando.
No entanto, quando um método da classe Calculadora for chamado, o fluxo de execução saltará
para esse método, entrará nele, executará seus comandos sequencialmente e, ao final, retornará
para o comando seguinte ao que fez a chamada do método. Acima, chamamos os métodos
Ligar(), ObterNumeros() e os demais em sequência. A cada chamada, o fluxo de execução
saltará para o método chamado, o executará e retornará para o comando seguinte ao que fez a
chamada.
Vemos esse percurso do fluxo nos quadros abaixo. Cada comando ocupa uma única linha,
mesmo que, nesses quadros, alguns ultrapassem uma linha devido ao espaço disponível:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 37

import calculadora class Calculadora:


def __init__(self):
# bloco principal self.priNumero = 0.0
umaCalc = self.segNumero = 0.0
calculadora.Calculadora() self.result = 0.0
def LimparVisor(self):
umaCalc.Ligar() os.system('cls') or None
self.result = 0.0

def Ligar(self):
self.LimparVisor()
umaCalc.ObterNumeros() self.priNumero = 0.0
self.segNumero = 0.0

def ObterNumeros(self):
self.priNumero = float(input("Digite o
umaCalc.Somar() 1º valor: "))
self.segNumero = float(input("Digite o
2º valor: "))

def Somar(self):
umaCalc.ExibirResultado() self.result = self.priNumero +
self.segNumero

def ExibirResultado(self):
umaCalc.Desligar() print(f"{self.priNumero} +
{self.segNumero} vale {self.result}")

def Desligar(self):
self.LimparVisor()
print("Saindo da memória!")
Arquivo main.py Arquivo calculadora.py
As setas mostram o fluxo de execução chamando os métodos, realizando comando a comando e
retornando para o local em que o método foi chamado.
Logo no início da execução, chamamos o Construtor ( Calculadora() ) e atribuímos seu
resultado para a variável umaCalc, que se tornou, assim, um exemplar da classe Calculadora.
Esse exemplar ficará na memória do computador e será usado para chamar os métodos que: liga
a calculadora, lê os valores, calcula sua soma, exibe o resultado e desliga a calculadora.
A classe Calculadora é apenas um modelo, ela não existe fisicamente na memória do computador
e não se pode executar seus comandos diretamente. Quem existe na memória do computador e
pode ter comandos executados é a instância que foi criada pelo Construtor. A classe é usada,
apenas, como um modelo que define os componentes que ficam dentro da instância. Para
solucionar o problema, precisamos usar a classe Calculadora e executar os métodos que
resolvem o problema, mas, para isso, o programa tem que instanciar um objeto dessa classe.
Acima, quando declaramos e instanciamos o objeto umaCalc, ele passa a ser um exemplar da
classe Calculadora na memória, onde podemos armazenar valores digitados e calculados, e o
fluxo de execução segue sequencialmente para executar o método Ligar() de umaCalc. O fluxo de
execução “salta” para esse método e executa os seus comandos internos, na sequência do
primeiro ao último, quando então retorna para o comando seguinte do programa, onde é feita a
chamada ao método ObterNumeros(). Dentro do método Ligar(), é chamado o método
LimparVisor(). O fluxo salta para esse método, o executa e, quando chega ao seu final, retorna
para o comando seguinte dentro de Ligar(), que é self.priNumero = 0.0 e continua executando
sequencialmente cada comando.
Note que poderiam existir mais métodos que ainda não descrevemos. Como estamos apenas
abstraindo uma Calculadora, não é necessário, pelo menos agora, que esses métodos sejam
codificados concretamente, pois nem mesmo estamos testando o código em um computador.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 38

Neste momento inicial de nosso aprendizado, podemos imaginar que os métodos funcionariam
de acordo com o que pretendemos deles.
Toda classe tem duas partes, os Atributos e o Comportamento. Mas note que removemos as
palavras Atributos: e Comportamento: da descrição da classe Calculadora. Na nossa
linguagem, os atributos são as variáveis membro declaradas no início da classe e, quando a
palavra def aparece, o computador saberá que a parte do comportamento começou a ser
codificada.
Esse tipo de abordagem de codificação de soluções facilita o desenvolvimento das habilidades de
raciocínio lógico e aprendizado de programação. Mas, para não ficamos apenas no âmbito do
raciocínio teórico, vamos começar a praticar a codificação em Python usando o ambiente de
desenvolvimento PyCharm, colocando as mãos na massa. Logo mais criaremos outras classes
para solucionar outros problemas e praticar a linguagem e o ambiente de programação.

Execução passo a passo do programa da Calculadora

Tendo os dois arquivos acima digitados e gravados numa pasta


aberta pelo VSCode, podemos executar o programa passo a
passo, para vermos os detalhes de um programa orientado a
objetos. A estrutura do projeto será semelhante à que vemos na
figura ao lado.
Note que, conforme você vai digitando seu código e o VSCode já
conhece parte das classes e métodos que você já digitou, “dicas”
começam a ser dadas para você, auxiliando a sua digitação. Por exemplo:

Clique em Ligar(self) e pressione [enter], e esse método


será copiado em seu código. Ao invés de clicar no item
Ligar(), você pode digitar a letra L e uma dica mais
específica será exibida, como vemos abaixo. Leve o cursor
ao item desejado e tecle [Enter] para copiar esse item em
seu código:

Deixe o arquivo main.py aberto no editor de código e coloque um breakpoint na linha onde
instanciamos o objeto umaCalc:
Para colocar o breakpoint nessa linha,
clicamos uma vez ao lado direito do número
da linha, de forma que apareça o círculo
vermelho, que indica o ponto de parada.
Em seguida, execute o projeto em modo de
depuração, clicando no botão com o bug
verde. Uma tarja azul aparecerá sobre a
linha marcada com o breakpoint, pois essa é
a linha onde a execução se inicia.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 39

Pressione [F11] uma vez e observe que, na linha do comando que acabamos de executar
aparecerá uma mensagem informando um endereço de memória onde esse objeto foi
armazenado:

Ou seja, o objeto umaCalc foi instanciado e agora ocupa um local da memória em que
armazenará os atributos e métodos da classe Calculadora.
Já na janela inferior onde aparecem as variáveis, o objeto umaCalc será exibido.
Clique no símbolo > do lado esquer-
do da variável umaCalc para que
esse objeto seja expandido, de
forma que possamos ver os
atributos que ele encapsula:

Veremos os três atributos definidos na classe


Calculadora, cada um deles de tipo float e
valendo 0.0. esses atributos estão encapsulados
no objeto umaCalc, ou seja, estão armazenados
dentro desse objeto.
Pressione novamente [F11] para que o próximo
comando do programa principal seja executado.
Como esse comando chama o método Ligar() da
classe Calculadora, o fluxo de execução se
dirigirá para o código desse método e o
executará, de cima para baixo:

Mas o primeiro comando desse método chama o método LimparVisor do objeto self que, neste
momento, se refere ao objeto que contém a classe, ou seja, o objeto umaCalc que declaramos no
programa principal. Pressione [F11] de novo:

O fluxo saltou para o método LimparVisor() e executará seus comandos. Ao final desse método,
retornará para o método Ligar().
Pressione [F7] até retornar ao método Ligar() e continue pressionando [F7] até que o fluxo de
execução retorne ao programa principal.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 40

Pressione [F7] para chamar o


método ObterNumeros(). O fluxo de
execução se dirigirá, novamente,
para o arquivo calculadora.py e
começará a executar o método
ObterNumeros() da classe
declarada nesse arquivo (Classe
Calculadora).

Pressione [F10] e observe que, na janela Console, aparecerá


uma mensagem solicitando que você digite o 1º valor. Usamos
F10 aqui para que o fluxo não entre em arquivos internos do
Python que tratam da leitura do teclado e isso atrapalhe nossa
sequência.
Em seguida, digite algum valor real (por exemplo, 18.42) e tecle
[Enter]. Em seguida, observe na janela de variáveis que a variável umaCalc tem seu atributo
primeiroNumero com um novo conteúdo.
Pressione [F8] novamente e digite o 2º valor após aparecer a mensagem solicitando essa
digitação. Por exemplo, o valor -2.42.
A variável umaCalc terá os valores digitados nos atributos primeiroNumero e segundoNumero,
como vemos abaixo:

Novamente pressionando [F7], iremos executar o comando que chama o método Somar do objeto
umaCalc:

Pressionando [F7] para o fluxo entrar


nesse método e continuando a
pressionar essa tecla, veremos que é
feito o cálculo do atributo resultado:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 41

Veja na janela de variáveis que, agora, o atributo


resultado ficou com o valor da soma dos valores
digitados anteriormente:
Pressione [F7] até que o fluxo de execução entre no
método ExibirResultado.

Coloque o cursor sobre a variável self.priNumero


dentro do comando print() e observe que o PyCharm
mostra o valor dessa variável:

Essa é uma outra forma de vermos o valor de uma variável, sem necessidade de usar a janela de
variáveis.
Pressione [F11], até retornar ao programa principal.
Observe a mensagem com o resultado, escrita ao final
da janela Console:
Por fim, pressione [F11] para chamar o método
Desligar(). Vá pressionando [F11] até retornar ao
programa principal e a execução terminar.

E quando ocorrem erros de digitação?

Imagine que você digitou um dado não inteiro quando o primeiro ou o segundo valores são lidos.
Por exemplo, o valor 17,2 ou uma sequência não numérica como 123Quatro, como na tela abaixo.
Isso acarretaria um erro de execução e o programa seria “abortado”.

Ou seja, quando valores numéricos são lidos, é preciso prestar atenção no que está sendo
digitado, para que não acarrete erros de execução.
Por outro lado, existem comandos, como try-except e raise, que aprenderemos futuramente, que
nos permitirão detectar erros sem que o programa seja abortado, e trabalhar com essa situação
avisando o usuário que o valor digitado não está adequado, para que possa digitá-lo
corretamente.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 42

Exercícios
Todos os exercícios abaixo envolvem a modelagem de classes que representem a realidade do
problema apresentado e contenham os métodos adequados.
Use o diagrama de classe para ter uma primeira versão da organização de seu modelo.
Em seguida, usando comandos escritos em Python, codifique a classe que armazena os dados
necessários e os métodos que realizam os cálculos solicitados e métodos para ler os valores e
exibir os resultados desses cálculos.
Escreva também as aplicações (programas) que usam as classes descritas, instanciam
objetos dessas classes, providenciem a leitura, cálculos e exibição dos resultados, através da
chamada dos métodos adequados de cada classe, como esquematizado abaixo:

Classe de solução

Atributos
variáveis com
valores lidos e
calculados

Comportamentos
O tipo de dados float é usado para converter strings para o conjunto
Métodos que lêem,
calculam e exibem numérico real. int faz a conversão para inteiros.

2.10. Codifique uma classe que armazene e leia uma temperatura dada na escala Celsius e
permita convertê-la para a temperatura correspondente em Fahreinheit, usando a fórmula
de conversão abaixo. Temperaturas são valores reais. Use o símbolo aritmético / e
parênteses para fazer divisões.
9 o
o
F= C + 32
5
2.11. Codifique uma classe que armazene e leia um valor real, que representa o raio de um
círculo, e permita calcular sua área, seu comprimento e diâmetro. Use o símbolo aritmético
* para fazer multiplicações, ao invés de “x” ou “.”.
2.12. Codifique uma classe que armazene e leia dois valores reais, que representam,
respectivamente, as medidas dos dois catetos de um triângulo retângulo, e permita
calcular o valor da hipotenusa, o valor do perímetro desse triângulo e sua área. Use o
método math.sqrt() para calcular a raiz quadrada de um valor numérico, como no exemplo
abaixo, que calcula o valor da raiz quadrada do valor já armazenado na variável real soma:

valorDaRaizQ = math.sqrt(soma);

A biblioteca math pode ser importada no arquivo que a for usar, da mesma maneira que
importamos o arquivo com a classe Calculadora anteriormente mas usando o comando
abaixo logo no início da classe:

from math import sqrt

Assim, você não precisará colocar o prefixo “math.” antes de usar sqrt().
2.13. Codifique uma classe que armazene e leia a quantidade de dias, horas, minutos e
segundos solicitada ao usuário. Calcule o total em segundos e exiba o resultado.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 43

3.1. Instruções de Uso do VSCode


3.
Para navegar entre as linhas, usamos as teclas abaixo:
Uso do
VSCode

Seta para cima – sobe o cursor para a linha superior àquela onde ele está posicionado.
Seta para baixo – desce o cursor para a linha abaixo daquela onde ele está posicionado
Seta para direita – move o cursor um caracter à direita
Seta para esquerda – move o cursor um caracter à esquerda
[Ctrl+Seta para direita] – move o cursor para a palavra seguinte àquela onde ele está
[Ctrl+Seta para esquerda] – move o cursor para a palavra anterior àquela onde ele está
[Page Down] – desce uma página na tela (aproximadamente 20 linhas)
[Page Up] – sobe uma página na tela
[Home] – o cursor é colocado no primeiro caracter da linha atual
[End] – o cursor é posicionado no último caracter da linha atual
[Ctrl+PageUp] – leva o cursor para o próximo arquivo aberto na tela atual
[Ctrl+PageDown] – leva o cursor para o arquivo anterior aberto na tela atual
[Ctrl+Home] – leva o cursor para o primeiro caracter do arquivo atual
[Ctrl+End] – leva o cursor para o último caracter do arquivo atual
[Del] – apaga o caracter atual sobre o cursor. Os caracteres que ficam à sua direita são trazidos
uma posição à esquerda
[BackSpace] – apaga o caracter anterior ao cursor. Os caracteres que ficam à sua direita são
trazidos uma posição à esquerda
[Shift + seta para cima ou para baixo] – marca a região do programa que está sendo visitada pelo
cursor. A região marcada pode, posteriormente, ser apagada com [Del], copiada em outro
local do texto com [Ctrl+C] e [Ctrl+V] ou [Shift+Insert].
[Ctrl+Z] – Menu Edit | Undo, desfaz os resultados da última operação feita no texto.
[Alt-Seta para baixo] – leva para baixo o conteúdo da linha atual do cursor
[Alt-Seta para cima] – leva para cima o conteúdo da linha atual do cursor.
Com o menu Editar, podemos procurar e/ou substituir um texto (Localizar / Substituir).
Com o menu Acessar podemos posicionar o cursor em uma determinada linha [Ctrl+G] e
informando o número da linha e coluna desejadas e, também, posicionar o cursor na definição de
métodos, símbolos e outras definições.
Com o menu Executar podemos executar o programa atual diretamente, ou passo a passo (Step
Over [F10] e Step Into [F11]), realizando Depuração (Debug).
Observe ao lado o resultado do processo Code Completion do VSCode: conforme você digita as
instruções, o ambiente informa “dicas” e auxilia sua digitação.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 44

3.2. Prática de Uso do VSCode


import é uma diretiva que declara que o programa usará os recursos de um arquivo cujo nome
segue a palavra import. Esse arquivo pode ser visto como um pacote ou biblioteca de software
que agrupa os arquivos e recursos já prontos usados pelo projeto.
Há inúmeros pacotes disponíveis para uso em Python, como Colorama, Curses, TensorFlow,
TKinter, PyGame, dentre muitos outros.
Vamos importar o pacote Colorama para colocar um pouco de cores em nossa tela de saída.
Note que Python não é uma linguagem feita para execução em janelas de texto com muitos
recursos gráficos, como posicionamento de cursor em determinados locais, criação de janelas
dentro da janela de saída (chamada de Janela Console) e, assim, os recursos de tratamento de
telas de texto são bastante simples. Caso seja necessária uma interface com usuário mais
elaborada, é recomendado que se use outros tipos de abordagem, como os módulos TKinter e
QT, que permitem criar interfaces gráficas mais modernas e flexíveis, ou o uso de outros
ambientes de desenvolvimento, como o Delphi da Embarcadero que permite usar a imensa gama
de recursos de interface gráfica da linguagem Delphi de forma compatível com Python.
No VSCode, abra a janela Terminal, usando o
menu Terminal | Novo Terminal:

Alternativamente, clique na guia Terminal que deve ser visível na parte inferior da janela do
VSCode:
Na janela que foi aberta, digite o
comando pip install colorama, como
vemos ao lado.
Esse comando buscará, em sites que
disponibilizam pacotes para Python, a
biblioteca colorama, que nos permitirá
usar cores de forma simples (porém
não muito esteticamente bonita) em
nossos programas Python executados
em modo texto.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 45

Essa biblioteca será copiada para seu computador e ficará disponível para uso.
Para saber quais bibliotecas estão instaladas no seu computador, digite pip freeze na linha de
comando do Terminal.
Para conhecer detalhes e explicações sobre o pacote colorama, acesse na Internet o endereço
https://pypi.org/project/colorama/:

Agora, importe o pacote PySound, para podermos tocar áudio em programas Python. Para isso,
vá na janela Terminal, digite pip install playsound [enter]. Após instalado, você pode ver sua
documentação na janela Python Packages:

Agora que aprendemos o básico de instalação de pacotes, podemos passar ao nosso programa,
usando esses pacotes na prática. No programa cores, vá ao arquivo main.py e digite o código
abaixo, alterando o que já estiver escrito anteriormente (criado pelo VSCode):
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 46

Pacotes que estamos importando em


nosso programa

escrever() é uma função que recebe


uma variável “texto” para ser escrita
no comando print. Observe o “:” e a
indentação no print

A execução de um programa começa no primeiro comando executável sem indentação que, no


caso acima, é o comando if, sobre o qual falaremos posteriormente com maior detalhamento.
No Python, os arquivos de código “.py” são também conhecidos como módulos. Cada módulo
pode ser executado diretamente, como um programa em si, ou importado por outro módulo.
Portanto, para saber se a execução está sendo feita no módulo principal ou em outro módulo,
precisaremos identificar o nome do módulo. Para isso, Python possui uma variável pré-definida
que que contém o nome do módulo em execução. Essa variável se chama __name__.
Mas quando o módulo é executado diretamente como um programa, sem ser chamado por outro
módulo, ele é considerado o módulo do programa principal e, assim, a variável __name__ é
definida com o conteúdo “__main__”. Quando um módulo é executado a partir de outro módulo, a
variável __name__ conterá, de fato, uma string igual ao nome do módulo (por exemplo,
calculadora.py).
Assim, o comando if acima pergunta “se o nome do módulo é igual a __main__, então temos o
protrama principal e começamos a executar os comandos que controlam o funcionamento desse
programa, comandos esses que estão todos indentados ao comando if, ou seja, são subordinados
ao comando if.
Clicando no botão com o triângulo
(execução), você executará o programa
na janela Terminal e terá o seguinte
resultado:
Quando você se cansar de ouvir a
música tema de Battlestar Galactica
1978, digite o caracter # logo antes da
letra p do comando playsound. # indica
um comentário, ou seja, essa linha
passará a ser ignorada pelo
interpretador do Python e não será
executada. Para todos os efeitos, essa
instrução foi “congelada”.
Você pode executar esse programa fora
do VSCode, no ambiente de linha de
comando, que terá um resultado
diferente. Abra a janela CMD, vá até o local da rede onde gravou o arquivo main.py e digite o
comando “python main.py [Enter]:”
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 47

Ao fazer isso, a execução será em ambiente Console (linha de comando de texto do Windows,
Linux, MacOS ou outro sistema operacional) e você terá um
resultado como o da tela ao lado:
Quando você executa um programa Python no Terminal,
pode até mesmo mudar a posição do cursor na tela e tem
alguns outros recursos.
Para posicionar o curso na tela do Terminal, precisamos
usar uma sequência de escape, que são caracteres
especiais que controlam a exibição de dados na tela. Essas
sequências podem diferir em sistemas operacionais
diversos.
No Windows, para posicionarmos o cursor na linha lin e na coluna col da tela, temos que escrever
na tela uma sequência igual a “\033[lin;colH”, onde lin e col são números inteiros iniciando em 1.
O código abaixo modifica a função escrever para que ela também receba variáveis contendo p
número da linha e o número da coluna em que se deseja escrever o texto (que já era recebido na
versão anterior):

def escrever(linha, coluna, texto):


print(f"\033[{linha};{coluna}H", end="")
print(f'{texto}')

, end=”” significa que o comando print não deverá pular de linha na tela depois de escrever o texto
anterior.
Para que posicionemos os textos a escrever, no programa principal teremos de também informar
os números de linha e de coluna onde queremos posicionar o texto, como vemos abaixo:

escrever( 4, 4, cor.Fore.RED+cor.Back.LIGHTWHITE_EX+"Olá, bem vindo(a)!")


escrever( 7, 3, cor.Fore.LIGHTYELLOW_EX + cor.Back.GREEN + 'Brasil')
escrever(10, 12, cor.Fore.LIGHTWHITE_EX + cor.Back.BLUE + 'França')
escrever(11, 12, cor.Fore.LIGHTRED_EX + cor.Back.GREEN + 'Portugal')

Fazendo essas modificações e executando o programa na janela Terminal, teremos o resultado


abaixo:
Observe o posicionamento dos
textos. O canto superior esquerdo
da janela Terminal tem a
coordenada (1, 1), ou seja, linha 1
e coluna 1.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 48

Depuração do código – Execução passo a passo

Vamos colocar um breakpoint no comando if, clicando


uma vez no espaço à esquerda do númera da linha,
para aparecer o círculo vermelho do breakpoint:

Agora, clique na seta para baixo ao lado


direito do botão triângulo verde e escolha
a opção “Depurador do Python: depurar
Arquivo Python”:
Isso dará início à execução passo a
passo do arquivo aberto no VSCode.
Espera-se, no caso, que o arquivo
main.py seja o arquivo aberto nesse
momento.
Usaremos as teclas [F10] e [F11] para
executarmos cada instrução
sequencialmente, como já fizemos anteriormente.
Mas, desta vez, observe que valores serão enviados do programa principal para a função
escrever. As variáveis dessa função, que estão entre parênteses na linha em que a função é
definida, receberão os valores que estão informados em cada um dos momentos em que a função
é chamada.
No comando if, coloque o cursor sobre a
variável __name__ e observe que ela
contém a string “__main__”. Assim, o
resultado dessa comparação feita pelo
comando if é verdadeira, ou seja,
__name__ é igual à string “__main__”;
assim, temos uma condição (ou decisão) verdadeira e os comandos internos ao comando if serão
executados, um a um, na sequência em que estão codificados.
Pressione [F11] para executarmos
esse comando, e veja que o fluxo de
execução entra no bloco de comandos
internos ao if. Pressione [F10] para
executar o cor.init() sem entrar dentro
dos comandos internos dessa função. Faça o mesmo com os.system, de forma que a tela do
terminal será apagada.
Pressione [F11] para que a primeira chamada à função escrever() seja executada. Observe que o
fluxo de execução saltará para essa função e poderemos ver que os parâmetros linha, coluna e
texto receberam os valores que foram informados no comando de chamada:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 49

Pressione [F11] até retornar ao programa principal.


Observe a janela console, com o texto escrito na
posição (4, 4) da tela:

Continue pressionando [F11] e, novamente, observe


que os parâmetros da função escrever() receberam os
valores que foram informados em cada comando de
chamada, como vemos na figura abaixo:

Esses caracteres estranhos que aparecem na variável


texto são as sequências de escape que codificam a
mudança de cores na tela do terminal, e que são
definidas pelos itens cor.Fore.RED e
cor.Back.LIGHTYELLOW_EX, por exemplo, definidos
no pacote colorama.
Pressionano [F11] até o comando cor.deinit(), teremos
a tela console como vemos ao lado:
Caso você pressione [F11] no comando cor.deInit(), o
fluxo de execução entrará dentro dos comandos do
pacote colorama. Aproveite para fazer isso e veja um
pouco da codificação desse pacote.

Na string exibida por print() podemos informar


“sequência de escape”. Essas sequências
introduzem caracteres especiais no texto
exibido, como vemos abaixo:

Sequência de Escape Descrição


\n Newline. Posiciona o cursor na linha seguinte
\t Tabulação horizontal
\r CR, Enter. Posiciona o cursor no início da linha atual
\\ Usado para exibir o caracter \ (barra)
\” Usado para exibir o caracter “ (aspa)
\’ Usado para exibir o caracter ’ (apóstrofe)
\0 Caracter nulo
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 50

4.1. Melhorando a Classe Calculadora


4.
Programação Vamos remodelar a classe Calculadora para que ela represente
uma calculadora de forma mais semelhante à realidade,
e Orientaçao a acrescentando outras operações aritméticas básicas: subtração,
divisão e multiplicação, e teremos um programa mais
Objetos completo e mais útil.

Faça uma cópia da pasta onde você codificou o projeto pyCalc para uma pasta chamada cap04,
dentro da estrutura de pastas que você está usando para os exemplos deste estudo. Seria algo
como vemos na figura abaixo:

Execute o VSCode e, na janela inicial, clique no botão [Abrir Pasta]. Na janela que será exibida,
acesse a pasta cap04/pyCalc e pressione [Selecionar Pasta]:

Usando a janela Explorer do VSCode, clique no arquivo main.py para abrí-lo no editor.
Agora, vamos aprimorar essa versão do projeto da Calculadora.
O uso das quatro operações aritméticas básicas é um problema com que nos deparamos muito no
dia-a-dia, quando vamos ao supermercado, por exemplo. Temos de somar os valores dos
produtos que compramos, para verificarmos se o valor total da compra está dentro do nosso
orçamento ou do dinheiro que levamos conosco para o pagamento. Em algumas vezes,
precisamos também multiplicar valores, subtrair e até mesmo dividir valores. Em outras palavras,
estamos diante da necessidade de realizar as quatro operações matemáticas básicas entre dois
valores numéricos.
Assim, a nossa classe precisa ter dois atributos numéricos, onde armazenaremos os valores que
serão afetados pelas quatro operações. Da mesma maneira, nossa classe terá o comportamento
de realizar essas quatro operações e encontrar resultados. Portanto, teremos também de guardar
o resultado da última operação realizada. Já tínhamos pensado em muitas dessas ideias na
primeira versão da nossa classe e, assim, já temos um código base para ela e a podemos
estender com novas funcionalidades.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 51

Os nomes que damos às variáveis, atributos, métodos e classes não tem significado algum
para o computador, que não interpreta se eles são coerentes ou não com o uso que se faz
delas no algoritmo.
No entanto, nomear as variáveis de forma coerente ao uso das mesmas facilita a
compreensão do algoritmo pelas pessoas que o analisam.
Já o computador não procura nenhum significado “oculto” ou “escancarado” no nome que
damos às variáveis, atributos, métodos e classes.

Assim, podemos expandir nossa classe para:

Ao lado, vemos três novos métodos na


classe.
Na programação Python, pass é uma
instrução nula, usada como um espaço
reservado para código futuro.
Suponha que tenhamos um método que
ainda não esteja implementado, mas
queremos codifica-lo no futuro.
Nesses casos, podemos usar pass para
já deixarmos o método já declarada,
para logo mais o codificarmos.
A declaração dos atributos (também
conhecidos como variáveis membro ou
campos da classe) deve ser feita no
método inicializador __init__() e devem
ser precedidas por “self.”. Fazemos isso
para que esses atributos fiquem
atrelados a cada instância que criarmos dessa classe, sem misturar as variáveis internas, caso
haja mais de instância da Calculadora sendo usada no mesmo programa.
Um computador já conhece as quatro operações fundamentais de adição, subtração, multiplicação
e divisão, indicadas pelos símbolos +, -, * e /, respectivamente. A divisão entre números reais e
inteiros é feita pelo operador /. Quando quisermos fazer uma divisão com resultado inteiro,
desprezando as casas decimais, devemos usar o operador //.
Dando sequência à codificação dos novos métodos, vemos sua implementação abaixo.
Removemos o comando pass e o trocamos pelos comandos que realizam as operações
aritméticas correspondentes a cada método:

= é o comando de atribuição. Ele


armazena na variável à sua esquerda
o valor à sua direita.
Esse comando pode ser lido como:
result “recebe” valor à direita

Usa-se * para multiplicação, pois “.” é


usado para separar casas decimais e
“x” seria confundido com uma
possível variável chamada x

/ faz divisão real


COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 52

Um programa que usasse essa classe para fazer as quatro operações com dois valores reais
deveria também obter os valores e exibir o resultado, concorda? Portanto, desde sua primeira
versão a classe Calculadora também já possui métodos para preencher os valores dos campos
priNumero e segNumero, para digitação de dados pelo teclado e para exibir o valor da soma.
O método ObterNumeros() lê dados digitados no teclado e os armazena nos atributos priNumero e
segNumero.
Dá-se o nome de “cabeçalho de método” ou “assinatura de método” para a linha onde um
comando def declara um método. Como esses métodos são funcionalidades da classe que
estamos desenvolvendo para representar uma calculadora, eles devem ser codificados dentro
dessa classe (encapsulados). Como estão sendo declarados dentro da classe, eles devem ser
indentados perante o comando que declara a classe ( class Calculadora: ).
Em Python, todo método de uma classe possui um parâmetro self, que representa a instância (o
objeto) do programa principal que está usando essa classe.
Assim, os cabeçalhos dos métodos devem ser escritos internamente à classe, indentados, e os
comandos internos de cada método devem ser indentados em relação ao cabeçalho, para indicar
ao interpretador que esses comandos estão subordinados ao método.
Vemos abaixo a codificação da classe (arquivo calculadora.py) até este momento:

import os # biblioteca que permite limpar a tela do SO Windows


class Calculadora:
def __init__(self): Indentação em relação a class
self.priNumero = 0.0
self.segNumero = 0.0
self.result = 0.0
Indentação em relação a def
def ObterNumeros(self):
self.priNumero = float(input("Digite o 1º valor: "))
self.segNumero = float(input("Digite o 2º valor: ")

def Somar(self):
self.result = self.priNumero + self.segNumero

def Subtrair(self):
self.result = self.priNumero - self.segNumero

def Multiplicar(self):
self.result = self.priNumero * self.segNumero

def Dividir(self):
self.result = self.priNumero / self.segNumero

def ExibirResultado(self):
print(f"Resultado dessa operação: {self.result}")
Importante lembrar
def LimparVisor(self):
os.system('cls') or None Os vários métodos da classe Calculadora resolvem
self.result = 0.0 os problemas de cálculos aritméticos mas não são
executados por essa classe.
def Ligar(self): A classe organiza o raciocínio da solução do
self.LimparVisor() problema, mas, sozinha, não o resolve.
self.priNumero = 0.0
self.segNumero = 0.0 É necessário um programa que crie um objeto dessa
classe, objeto esse que conterá os atributos (dados) e
os métodos (operações) ao lado, e chamará esses
def Desligar(self): métodos na ordem correta para resolver o problema
desejado.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 53

self.LimparVisor()
print("Saindo da memória!")

Observe que alteramos a mensagem do método ExibirResultado(), para que ela não mais cite a
operação de soma, pois nossa classe poderá realizar outras operações entre os valores digitados.
Vamos, agora, atualizar o código do programa (arquivo main.py), que criará o objeto da classe
Calculadora, colocará os valores dentro desse objeto e realizará as operações aritméticas.

import calculadora

print("\x1b[2J\x1b[1;1H") # Apaga a tela


# bloco principal
umaCalc = calculadora.Calculadora()
umaCalc.Ligar()
umaCalc.ObterNumeros()
umaCalc.Somar()
umaCalc.ExibirResultado()
umaCalc.Subtrair()
umaCalc.ExibirResultado()
umaCalc.Dividir()
umaCalc.ExibirResultado()
umaCalc.Multiplicar()
umaCalc.ExibirResultado()
umaCalc.Desligar()
espera = input("Digite [Enter] para terminar este programa:")
print("Obrigado!")
Se executarmos esse programa, ele pedirá os dois valores e apresentará uma sequência de
resultados. Mas, quase sem dar tempo para lermos os resultados, a tela será apagada, pois o
método Desligar foi chamado. Uma mensagem pedindo para pressionar [Enter] aparece logo
depois, mas não conseguimos ler os resultados. Portanto, temos um erro de lógica ai: a chamada
ao método Desligar (que apaga a tela) só deve ser feita após o usuário pressionar [Enter]. Temos,
portanto, que mudar a ordem dos
comandos “umaCalc.Desligar()” e
“espera = input(...)”.
Após mudar a ordem desses coman-
dos, execute o programa. Agora ele
pedirá ao usuário que digite os dois
números, instanciará um objeto da
classe Calculadora (variável umaCalc)
e, chamando os métodos desse obje-
to, realizará as quatro operações em
sequência, exibindo os resultados,
sempre atuando sobre os dois valores digitados inicialmente, como vemos na figura ao lado.
Mas será que esse resultado é exatamente o que esperávamos de uma calculadora? Ou será que
esse resultado é útil quando vamos ao supermercado? Será que sempre precisamos do resultado
das 4 operações em sequência? E, para piorar algo que já está ruim, não sabemos que operação
foi realizada em cada resultado.
Não seria muito mais útil que o usuário pudesse escolher qual operação deseja realizar, e dai o
programa se encarregaria de executar apenas essa operação, de forma mais similar ao que uma
calculadora de verdade permite realizar?
A responsabilidade pelo resultado não ser tão bom assim não é do computador ou do programa
em si, mas, pelo contrário, o computador apenas executou o que o programa lhe ordenou, e o
programa realizou o que nossa lógica de programação determinou: nós, programadores, pedimos
para o objeto umaCalc executar seus métodos de cálculo em série, sem que nada determinasse
qual operação era realmente desejada pelo usuário.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 54

import calculadora
print("\x1b[2J\x1b[1;1H") # Apaga a tela
# bloco principal
umaCalc = calculadora.Calculadora()
umaCalc.Ligar()
umaCalc.ObterNumeros()
umaCalc.Somar() E se o usuário somente quisesse fazer
umaCalc.ExibirResultado() uma divisão?
umaCalc.Subtrair()
umaCalc.ExibirResultado()
umaCalc.Dividir()
umaCalc.ExibirResultado()
umaCalc.Multiplicar()
umaCalc.ExibirResultado()
espera = input("Digite [Enter] para terminar este programa:")
umaCalc.Desligar()
print("Obrigado!")
Essa situação demonstra que nem sempre a execução dos comandos de um programa seguindo
a sequência natural do fluxo de execução é a mais adequada. Há situações, como essa que
acabamos de ver, em que não devemos executar todos os comandos na sequência; nessas
situações será necessário que o fluxo de execução se desvie de alguns dos comandos da se-
quência, deixando de executá-los, apenas para executar os comandos estritamente necessários.
Assim, precisamos de uma maneira de desviar o fluxo de execução dos comandos que não são
interessantes em determinados momentos. Isso é feito por meio de uma estrutura de
programação chamada Desvio de Fluxo Condicional, que é implementada pelo uso do
comando de decisão if (se), que estudaremos logo a seguir.
Só devemos executar o método Somar() se e somente se o usuário quiser fazer uma adição. Só
devemos executar o método Subtrair() se e somente se o usuário quiser fazer uma subtração. O
mesmo ocorre com a multiplicação e com a divisão.
Assim, a execução de uma operação aritmética específica é um caso especial do uso da
calculadora, que só ocorre se o usuário quiser fazer essa operação específica.
Portanto, existem momentos em que não podemos executar qualquer método, mas em que ações
alternativas são necessárias. Para isso, precisamos de um comando que nos permita decidir qual
caminho seguir.
Ao invés de chamar, em sequência, todos os métodos de operação aritmética, precisamos
conhecer antecipadamente a intenção do usuário em relação a qual operação realizar. Podemos
pedir ao usuário que informe a operação desejada (+, -, *, /), ler sua resposta e executar apenas o
método que corresponde a essa operação.
Para fazer uma pergunta ao usuário, usaremos input() para escrever na tela uma string contendo
a pergunta e ler a resposta dada, como fizemos anteriormente para ler os números.
Vamos então codificar, no módulo principal, a exibição da pergunta sobre a operação desejada e
a leitura da resposta do usuário:
...
umaCalc.ObterNumeros()
operacao = input("Que operação deseja fazer (+, -, *, /) ?")
umaCalc.Somar()
umaCalc.ExibirResultado()
umaCalc.Subtrair()
...

Acima, input() com a mensagem perguntando a operação desejada e, logo depois, lemos a string
que o usuário digitou como resposta.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 55

4.3. Controle do fluxo de execução


Controle de desvio com o comando de decisão if

Voltando ao nosso programa, se o executarmos agora, a mensagem solicitando a operação


desejada será exibida e o computador ficará esperando uma resposta, que o usuário digita e,
após pressionar a tecla [Enter], é guardada na variável operacao.
Em seguida, o programa continua no fluxo
sequencial e executa o método Somar(),
exibe o resultado da soma, executa o
método Subtrair(), exibe o resultado da
subtração e assim por diante, basicamente
como fazia antes. A única diferença foi ter
sido solicitada e lida a operação desejada.
A figura ao lado ilustra essa situação.

Ler a operação desejada não valeu de nada para o computador decidir qual operação deveria ser
realizada, pois o computador não adivinha o que deveria ser feito com a variável operacao, nem
mesmo sabe que ela contém alguma informação útil para decidir qual operação aritmética realizar.
É a nossa codificação que tem que fazer isso: decidir qual operação aritmética será realizada e
realizar apenas essa operação, e não as demais.
Essa decisão se baseará no valor da variável operacao, de acordo com as regras abaixo:
• Se operacao contiver “+”, executaremos o método Somar().
• Se operacao valer “-”, executaremos o método Subtrair().
• Se operacao for igual a “*”, executaremos o método Multiplicar().
• Se operacao for igual a “/”, executaremos o método Dividir().
Para decidir se um comando será executado ou não, usamos o comando de decisão if, que nos
permite fazer uma pergunta ao computador. Toda pergunta feita a um computador terá como
resultado um conceito lógico, ou seja, um valor false (falso) ou um valor true (verdadeiro).
Podemos perguntar se operacao é igual a “+” e, se for, o resultado da pergunta será true. Se
operacao for diferente de “+”, o resultado da pergunta será false.
Em Python, para compararmos dois dados, usamos símbolos especiais chamados operadores
relacionais, que listamos abaixo:
== → pergunta se dois valores são iguais: operacao == “/”
!= → pergunta se dois valores são diferentes: resposta != “N”
> → pergunta se um valor é maior que outro: delta > 0
>= → pergunta se um valor é maior ou igual a outro: hipotenusa >= cateto1+cateto2
< → pergunta se um valor é menor que outro: delta < 0
<= → pergunta se um valor é menor ou igual a outro: raio <= totalSomado
Python usa o operador == para comparação de igualdade porque o símbolo = já é usado para
atribuição de valores a variáveis. Dessa forma, não há confusão sobre o uso de nenhum desses
símbolos.
Da mesma maneira, não existem símbolos no teclado para ≤, ≥, ≠ e, assim, os símbolos <=, >= e
!= são respectivamente usados no lugar desses. Também por isso não usamos ÷ para divisão, e
sim a /. Em programas também não usamos conceitos matemáticos como ±, ∞ e ≡. Alguns
ambientes de desenvolvimento, como PyCharm, mudam o símbolo exibido quando digitamos !=
para um ≠ mas, no arquivo texto do código, continuam os símbolos != para representar diferença.
O resultado de cada uma dessas perguntas será um valor lógico: Tru ou False e esse valor
influenciará diretamente o funcionamento do if. Vemos abaixo a forma geral do comando de
decisão if:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 56

Substitua <condição> por uma decisão lógica


if <condição> : (pergunta de resultado verdadeiro ou falso).
<comandos-do-if>
Substitua aqui por um ou mais comandos que
a sua lógica precise executar se a <condição>
<comando seguinte (fora) ao if>
tiver resposta verdadeira.
Observe a indentação, que define os
comandos internos ao if.

<comandos-do-if> serão executados se e somente se <condição> for verdadeira (o fluxo de


execução entra nos comandos indentados ao if). Caso <condição> seja falsa, <comandos-do-if>
não serão executados e o fluxo segue para o comando seguinte ao comando if, e que, por não
estar subordinado ao comando if, sempre será executado na sequência.
Em nosso programa, basicamente teremos de perguntar ao computador se operacao é igual a
um dos símbolos que representa a operação desejada e, se for igual, executaremos os
comandos adequados a essa operação. Abaixo temos um exemplo do uso desse comando para a
adição:
Aqui, é feita uma pergunta ao computador, por
if operacao == "+" : meio da condição operacao == “+”.
umaCalc.Somar()
Ela comparará o conteúdo de operacao com “+”.
umaCalc.ExibirResultado() Se forem iguais, a condição resultará em true e
o fluxo de execução entra nos comandos
... → comando seguinte (fora) ao if indentados (internos) e os executa.
Se operacao é diferente de “+”, a condição
resulta em false e o fluxo se desvia dos
comandos indentados, que não serão
executados.
O comando if determina uma DECISÃO para DESVIO DE FLUXO no programa: se a condição
operacao == “+” for verdadeira, o fluxo de execução será desviado para os comandos
subordinados ao if (indentados), os executará e, depois, retomará o fluxo de execução
sequencial natural e seguirá para o comando seguinte ao if (representado aqui por ..., fora da
indentação). Ao contrário, se a condição for falsa (ou seja, operacao é diferente de “+”), a
execução pula esses comandos subordinados ao if e vai diretamente ao comando seguinte ao if
(...). Nesse caso, os comandos indentados ao if serão ignorados.

O comando if, portanto, especifica uma decisão sobre que caminho tomar no fluxo de
execução, ou seja, um desvio de fluxo condicional:
Sob uma determinada condição, o fluxo é ou não desviado. Se a condição
apresentada ao comando if for verdadeira, o fluxo é desviado para o bloco de
comandos que vem logo em seguida e que indentado ao comando if. Se a condição
for falsa, o fluxo não é desviado, e vai para o comando seguinte ao if.

Os comandos indentados ao if formam um bloco de instruções ou bloco de comandos ou


comando composto. Em outras palavras, os comandos indentados acima estão subordinados ao
comando if: se operacao for igual a “+”, deseja-se que os dois comandos dentro do bloco sejam
executados (chama-se Somar() para calcular a adição e, logo depois, chama-se ExibirResultado()
para que o resultado seja escrito na tela).
Esse bloco de comandos, na verdade, faz parte do comando if. Usamos a indentação para
delimitar o bloco iniciado pelo comando if, indicando onde começa e onde termina o grupo de
ações que serão executadas quando a condição resultar em true.
Em algumas linguagens de programação como, por exemplo, Delphi, Java e C#, usam-se os
símbolos { e } para definir blocos e, quando houver apenas um único comando subordinado a um
comando if, não é obrigatório delimitar um bloco com { e } (begin e end em Delphi). Em Python
usamos a identação feita com espaços ou tabulação
Abaixo temos um outro exemplo de uso do comando if:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 57

Para decidir o que fazer, substituímos


<condição> por uma comparação, cujo
resultado pode ser verdadeiro ou falso.
if delta < 0 :
print(“Equação sem raízes reais porque delta é negativo”)

Aqui substituímos <comando-do-if> pelo


VerificarValorDeA() comando que desejamos executar se a
pergunta delta < 0 tiver resposta
Este é o comando seguinte ao if (fora da verdadeira.
indentação do if), e será executado sempre,
seja a <condição> verdadeira ou falsa.

O computador usa o resultado da condição delta < 0 para decidir se executa o comando dentro
do if (print()) ou se o fluxo vai diretamente para o comando seguinte ao if (VerificarValorDeA()).
Retornemos ao nosso programa principal, codificando os comandos de decisão necessários para
executar as demais operações apenas quando elas tiverem sido escolhidas pelo usuário:

Aqui operacao é lida


umaCalc.ObterNumeros()
operacao = input("Que operação deseja fazer (+, -, *, /) ?")

if operacao == "+" : Se operacao é igual a “+”, então:


Se a condição umaCalc.Somar() • Somamos os números
der False umaCalc.ExibirResultado() • Exibimos o resultado
Se não for igual, o fluxo vai ao comando seguinte (if)
if operacao == "-" :
umaCalc.Subtrair()
Se operacao é igual a “–”, então:
umaCalc.ExibirResultado()
• Subtraímos os números
• Exibimos o resultado
if operacao == "/" : Se não for igual, o fluxo vai ao comando seguinte (if)
umaCalc.Dividir()
umaCalc.ExibirResultado()
Se operacao é igual a “/”, então:
• Dividimos os números
• Exibimos o resultado
Se não for igual, o fluxo vai ao comando seguinte (if)

if operacao == "*" : Se operacao é igual a “*”, então:


umaCalc.Multiplicar() • Multiplicamos os números
umaCalc.ExibirResultado() • Exibimos o resultado
Se não for igual, o fluxo vai ao comando seguinte
(espera = input(...)
espera = input("Digite [Enter] para terminar este programa:")
umaCalc.Desligar()

Suponha que o usuário deseja realizar a


divisão de -19 por 3. Nesse caso, ele
deverá digitar os dois números e informar /
como a operação que deseja fazer. Vemos
isso na figura ao lado, que representa a
execução do programa.

Coloque um breakpoint no comando que faz a leitura da operação desejada. Clique no botão de
depuração, digite os valores desejados e escolha “/” como operação. Supondo que estamos
executando o programa passo a passo, com as teclas [F10] e [F11], podemos seguir o fluxo de
execução pelas figuras a seguir:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 58

O fluxo de execução está posicionado no comando if


que compara operacao com “+”. A variável operacao
vale “/”. Na parte superior da janela de variáveis da
Depuração (Debug) digite a condição desse comando if
( operacao == “+” ) e observe seu resultado, clicando
no ícone do óculos:
Quando esse comando for executado, o computador
perguntará: operacao é igual a + ? Verdadeiro ou
falso?
Essa pergunta terá falso como resposta. Ou seja, a
condição resulta em false e, por isso, o computador
decide que o fluxo de execução não entrará dentro dos comandos dentro desse if e, ao contrário,
se desviará para o comando seguinte, no if que
compara operacao com “-“.
Pressione [F11] para visualizar esse movimento do
fluxo de execução acontecer, como vemos ao lado.
Quando esse comando for executado, o computador
perguntará: operacao é igual a - ? Verdadeiro ou
falso?

Essa pergunta terá falso como resposta. Ou seja, a condição


resulta em false e, por isso, o computador decide que o fluxo de
execução não entrará dentro dos comandos dentro desse if e,
ao contrário, se desviará para o comando seguinte, no if que
compara operacao com “/“.
Pressione [F11] para visualizar esse movimento do fluxo de
execução acontecer, como vemos a seguir.
Quando esse comando for executado, o computador
perguntará: operacao é igual a / ? Verdadeiro ou
falso?

Essa pergunta terá verdadeiro como resposta. Ou seja, a


condição resulta em true e, por isso, o computador decide que
o fluxo de execução entrará dentro dos comandos dentro
desse if e os executará um a um. Somente após ter executado
os comandos dentro do if, o fluxo irá para o comando
seguinte, no if que compara operacao com “*“.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 59

Pressione [F11] em sequência para visualizar o


movimento do fluxo de execução para dentro dos
comandos internos ao if, executando o método
Dividir() , entrando dentro deste método na classe
Calculadora, retornando para o programa e executar
ExibirResultado().

Vemos aqui o resultado do cálculo da divisão:

Executando ExibirResultado():

Após sair do bloco de comandos indentado (subordinado) ao if acima, o fluxo de execução


prossegue para o comando seguinte, que é o comando if em que a condição testa se operacao é
igual a “*”.

Essa pergunta dará falso e, por isso, o fluxo


desviará do bloco de comandos subordinado a
esse último if e irá para o comando que envia a
mensagem solicitando que o usuário tecle
[enter] para terminar o programa.

Vemos ao lado a tela que exibe as saídas


desta execução:

O fluxo de execução, após isso, se dirige para


a chamada ao método umaCalc.Desligar() e o
programa, por fim, termina. Pressione [F10]
para acompanhar o fluxo de execução até a
finalização do programa.

Execute o programa outras vezes, digitando outras operações. Observe que, sempre que um dos
comandos if resulta em true, o fluxo de execução se desvia para os comandos dentro desse if e,
depois continua na sequência, indo para os comandos seguintes. Como os próximos comandos if
comparam operacao com cada uma das operações possíveis, apenas um deles poderá dar
resultado verdadeiro e, assim, apenas um deles terá seus comandos internos executados e os
demais serão ignorados.
Podemos melhorar bastante nosso programa. Na versão atual, se o usuário digitar alguma
operação diferente de +, -, * ou /, nada será avisado ao usuário e o programa simplesmente
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 60

terminará. Também, caso o usuário deseje realizar mais de uma operação, ele terá de executar o
programa novamente, pois somente uma operação é lida e realizada a cada execução.
Assim, fica claro que apenas o comando if, embora nos permita realizar ações de acordo com
decisões baseadas nos dados processados (sendo um enorme ganho), não consegue fazer tudo.
Temos que aprender como realizar ações alternativas quando a condição de um if resulta em
false e, também, aprender a repetir operações como, por exemplo, fazer com que esse programa
seja repetido para permitir novos cálculos, sem ter que ser finalizado e reexecutado. Isso envolve
controlar o fluxo de execução de uma maneira ainda mais refinada.
Para formalizar o estudo sobre como controlar o fluxo de execução, é importante ressaltar
que o fluxo de execução de um programa pode seguir um ou mais dos padrões abaixo:
• Sequência – execução passo a passo natural dos comandos, um após o outro
• Desvio – executar um ou mais comandos indentados apenas se a condição é satisfeita
• Repetição – executar um ou mais comandos indentados de forma repetida, enquanto
for necessário, ou seja, enquanto uma dada condição for satisfeita.
Esses três padrões são chamados de Construtores Estruturados, e nos permitem criar
qualquer tipo de algoritmo. Já estudamos e vimos funcionando a sequência e o desvio. Em
seguida aprofundaremos nosso estudo do comando de desvio condicional if e
aprenderemos a repetir comandos, usando para isso os comandos while e for.

No código que já programamos, depois que um dos comandos if dá verdadeiro e sua operação é
realizada, os ifs que vem em seguida continuam sendo testados, pois são comandos distintos.
Imagine que o usuário digitou a operacao como +. Apenas o primeiro if teria efeito, mas o fluxo de
execução seguiria para todos os demais, testando suas condições sem necessidade, pois todas
dariam falso. Seria interessante que isso fosse evitado, para termos uma execução mais rápida.
Também seria interessante que o programa avisasse o usuário caso a operação digitada não
fosse uma das previstas, ou seja, se a operacao lida não contiver os caracteres +, -, * e /, será
emitida uma mensagem advertindo o usuário.
Para resolver essas questões, podemos usar a segunda forma do comando if. O comando if
possui uma forma alternativa, chamada de if-else, que nos permite realizar ações alternativas
caso a condição seja falsa. A estrutura geral dessa forma segue abaixo:

if <condição> :
<comandos-1> → realizados se <condição> é True
else :
<comandos-2> → realizados se <condição> é False
A palavra else indica que o comando if será composto por uma condição com duas ações
mutuamente exclusivas, ou seja, se uma das ações for executada, a outra não o será, e vice-
versa. else não é um comando de Python, e sim uma cláusula opcional que é parte do if.
A ação <comando-1> é executada se <condição> é verdadeira, e então <comando-2> é pulado
na execução. Se <condição> é falsa, <comando-1> é pulado e a ação <comando-2> é
executada. ‘<comando-1> e <comando-2> podem ser substituídos por quaisquer comandos
válidos, inclusive outro if ou if-else. Por exemplo:
...

if primeiroNumero >= 0 :
printf(f"{primeiroNumero} é positivo.")
else :
print(f"{primeiroNumero} é negativo.")
... comando seguinte ao if-else

Acima, se a variável primeiroNumero contém um valor maior ou igual a zero, a condição do if será
verdadeira, de forma que o fluxo de execução entra no comando logo após o if (comandos-1) e
pula o else e os comandos a ele subordinados.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 61

Caso contrário, se primeiroNumero contém um valor menor que zero, a condição do if será falsa,
e o fluxo de execução pula comandos-1, e se encaminha para o else, executando o comando
subordinado ao else (comandos-2).
É importante ressaltar que todos esses comandos no quadro acima fazem parte do comando if-
else, de forma que, depois de executar exclusivamente a parte do if ou a parte do else, o fluxo
de execução se dirige para o comando seguinte ao if-else e o executa.
Aplicando esse conceito ao nosso programa, podemos impedir que o if que testa “-“ seja
executado, caso o teste anterior (do “+”) tenha dado verdadeiro e a operação de soma tenha sido
realizada. Isso é mostrado abaixo, onde incluímos um else entre o primeiro e o segundo if,
fazendo com que o segundo if faça parte do else do primeiro if:

operacao = input(“Que operação deseja fazer (+, -. *, /) ?”)

<condição>
if operacao == "+" :
Executado se a <condição> dá true (operacao
<comando-1> umaCalc.Somar()
é igual a “+”).
umaCalc.ExibirResultado()
Depois de executar, pula else e <comando-2>
else :
if operacao == "-" :
Fluxo pula para o else se <condição>
<comando-2> umaCalc.Subtrair() dá false (operacao diferente de “+”) e
umaCalc.ExibirResultado() vai executar <comando-2>, que é outro
if, que testará se operacao é igual a “-“
...
umaCalc.Desligar()

Esse tipo de construção é chamado de if-else encadeado, pois dentro do else há outro if. Esse
segundo if está subordinado ao else, ou seja, constitui-se no <comando-2> da forma geral do
comando if-else e, portanto, só será executado se a condição do primeiro if der false.
Podemos estender esse raciocínio para o teste da operação “*”, que só deve ser executado se a
operação não é igual a “+” e, também, não é igual a “-“. Esse raciocínio é codificado abaixo:

if operacao == "+" :

<comando-1> umaCalc.Somar()
umaCalc.ExibirResultado()

else :
<comando-2> if operacao == "-" :

umaCalc.Subtrair()
<comando-1> deste if-else
umaCalc.ExibirResultado()

else :
if operacao == "/" :

umaCalc.Dividir() <comando-2> deste if-else


umaCalc.ExibirResultado()

O terceiro if somente será testado se operacao for diferente de “-“. O segundo if somente será
testado se operacao for diferente de “+”. Portanto, o fluxo de execução desses if-elses está
encadeado (ou aninhado, pois um está dentro do outro).
Estendendo o raciocínio para o quarto if, que testa se operacao é a multiplicação, teremos o
código abaixo. Imagine que a operacao vale “*” e teremos o fluxo seguindo as setas pontilhadas:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 62

if operacao == "+" : <condição> dá false. Fluxo pula para o else e executa o


comando do else, que é o segundo if, onde se testa se operacao
umaCalc.Somar() == “-“
umaCalc.ExibirResultado()

else:
if operacao == "-": <condição> dá false. Fluxo pula para o else e executa o
comando do else, que é o terceiro if, onde se testa se operacao
== “/“
umaCalc.Subtrair()
umaCalc.ExibirResultado()

else:
if operacao == "/" : <condição> dá false. Fluxo pula para o else e executa o
comando do else, que é o quarto if, onde se testa se operacao
== “*“
umaCalc.Dividir()
umaCalc.ExibirResultado()

else:
<condição> dá true. Fluxo entra nos comandos do if e os
if operacao == "*" : executa, realizando a multiplicação e exibindo o resultado.
Ao final, fluxo segue para o comando que lê a tecla [Enter]
umaCalc.Multiplicar()
umaCalc.ExibirResultado()

espera = input("Digite [Enter] para terminar este programa:")

Veja que a lógica da indentação de cada comando faz com que um if-else fique indentado a outro.
O que aconteceria se a operacao digitada não fosse igual a nenhum dos símbolos de operação
previstos? O fluxo de execução seguiria adiante, pediria para o usuário teclar [Enter], iria para a
chamada de umaCalc.Desligar() e terminaria o programa, sem avisar ao usuário que ele digitou
um símbolo não previsto. Para resolver isso, podemos estender mais uma vez o encadeamento
de if-elses, como vemos abaixo, ao final do bloco principal:

# bloco principal
umaCalc = calculadora.Calculadora()
umaCalc.Ligar()
umaCalc.ObterNumeros()
operacao = input("Que operação deseja fazer (+, -, *, /) ?")
if operacao == "+" :

umaCalc.Somar() Este else é deste if


umaCalc.ExibirResultado()

else:
if operacao == "-":
Este else é deste if
umaCalc.Subtrair()
umaCalc.ExibirResultado()

else:
if operacao == "/" :
Este else é deste if
umaCalc.Dividir()
umaCalc.ExibirResultado()

else:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 63

if operacao == "*" :

umaCalc.Multiplicar() Este else é deste if


umaCalc.ExibirResultado()

else:
print("A operação digitada não está prevista.")
espera = input("Digite [Enter] para terminar este programa:")
umaCalc.Desligar()
print("Obrigado!")

Acima temos, em vermelho, o fluxo de execução quando uma das condições dá false e, em
verde, o fluxo nos comandos internos do if quando a condição de um deles dá true. Em azul,
indicamos a correspondência entre cada else e o if do qual faz parte, ao qual pertence.
else não é um comando, não pode ser usado isoladamente. Ele é uma cláusula (parte) do
comando if, e somente pode ser usado junto ao if. O else, portanto, não é obrigatório. Nós o
usaremos quando tivermos de executar uma ação alternativa quando a condição do if der false.

Testando nosso programa

Depois de digitar, compilar e corrigir eventuais erros de compilação que ocorram, execute esse
programa passo a passo, usando diferentes operações para acompanhar o fluxo de execução
enquanto este pula os ifs e se dirige aos elses.
Você pode usar, também, a tecla [F11] para seguir os passos sequencialmente. A diferença com
[F10] é que esta tecla executa os métodos chamados sem mostrar o fluxo de execução entrando
nesses métodos, dentro da classe onde estão codificados. Assim, a depuração ficará mais rápida
e focada no funcionamento da estrutura de desvio de fluxo condicional if-else.
Abaixo, mostramos o fluxo de execução para a operação “/”, aplicada aos valores 81 e 15. Oculta-
mos a visualização dos comandos internos de cada if, clicando no símbolo [v] visível à direita de
cada if no editor do VSCode, para focarmos nos comandos que nos interessam no momento:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 64

Vamos aperfeiçoar a lógica do programa. Observe que sempre exibimos um resultado, menos
quando o usuário digita uma operação inválida. Cada teste de igualdade com um dos símbolos de
operação contém uma chamada a umCalc.ExibirResultado(), chamado depois que o método da
operação foi executado. Assim, se tivermos certeza de que a operação digitada é válida,
podemos ter somente um comando de exibição, como vemos abaixo:

if operacao == "+" :
umaCalc.Somar()
else:
if operacao == "-" :
umaCalc.Subtrair()
else:
if operacao == "/" :
umaCalc.Dividir()
else:
if operacao == "*" :
umaCalc.Multiplicar()
umaCalc.ExibirResultado()

Mas, para que essa lógica funcione, e somente exiba o resultado caso a operação seja válida,
precisamos garantir que operacao armazene um valor adequado, ou seja, que pertença ao
conjunto de operações esperadas: “+-*/”.
Uma maneira relativamente fácil de fazer isso é, justamente, seguindo a frase acima: verificar se
operacao pertence ao conjunto de caracteres “+-*/”; em outras palavras, se esse conjunto contém
o valor que está guardado em operação. Isso pode ser feito pelo uso do método find() que as
strings possuem na linguagem Python, que usaríamos em nosso programa com o comando
abaixo:
operacoesPrevistas.find(operacao)
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 65

Procuraremos pela string guardada em operacao na string operacoesPrevistas vendo, assim, se


operacao está contida em operacoesPrevistas, ou seja, se operacoesPrevistas encontrou
operação dentro de si.
O método find retorna um resultado inteiro, que indica a posição da string procurada dentro da
string original. Se não encontrar a string procurada dentro da string original, esse método retorna -
1. Assim, podemos usá-lo como condição de um comando if, como vemos abaixo:
...
umaCalc.ObterNumeros() Aqui declaramos uma variável com
operacoesPrevistas = "+-*/" as operações previstas

operacao = input("Que operação deseja fazer (+, -, *, /) ?")

if operacoesPrevistas.find(operacao) != -1:
if operacao == "+" :
umaCalc.Somar()
else: Este bloco está subordinado ao
primeiro if.
if operacao == "-":
umaCalc.Subtrair() Este bloco só será executado se o
else: primeiro if for verdadeiro, ou seja, se
if operacao == "/" : a operacao digitada foi encontrada
em operacoesPrevistas
umaCalc.Dividir()
else: O método ExibirResultado é chama-
if operacao == "*" : do somente uma vez, após a opera-
umaCalc.Multiplicar() ção desejada ter sido executada.
umaCalc.ExibirResultado()
else: Este else é do primeiro if
print("A operação digitada não está prevista.")
espera = input("Digite [Enter] para terminar este programa:")
Agrupamos a sequência de if-else aninhados dentro de um bloco de comandos indentado
(subordinado) ao primeiro if, que testa se o valor lido em operacao pertence à string “+-*/”,
guardada na variável operacoesPrevistas. Se operacoesPrevistas contiver um caracter igual ao
armazenado em operacao, o usuário terá digitado uma operação válida e poderemos processá-la.
Se esse primeiro if (o externo) der false, o fluxo de execução pula o bloco de comandos do if e se
dirige para o último else quando executará o print() que exibe a mensagem ao usuário avisando-o
de que digitou uma operação não prevista.
Abaixo temos o resultado se digitarmos @ como operação desejada:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 66

O comando if de Python possui, também, cláusula elif, específica para situações de if-else
aninhados como acima. Ela agrega o else com um if indentado, de forma que podemos escrever
os if-else aninhados acima da maneira que segue:
if operacoesPrevistas.find(operacao) != -1:
if operacao == "+" :
Uma sequência de if-else aninhados pode ser
umaCalc.Somar() escrita usando o comando elif.
elif operacao == "-" :
umaCalc.Subtrair() elif sempre precisa estar associado a um
elif operacao == "/" : comando if inicial.
umaCalc.Dividir() Observe que, ao usar elif, as indentações
else : estão sempre no mesmo nível do if que origina
umaCalc.Multiplicar() a sequência.
umaCalc.ExibirResultado()
else:
print("A operação digitada não está prevista.")

Execute essa nova versão passo a passo, para ver como a cláusula elif se comporta.
Nosso programa melhorou bastante com essas alterações. Mas, e se quiséssemos executar
outras operações, até mesmo com outros valores? Na forma como o programa está, será
necessário esperar que ele termine e executá-lo de novo.
No entanto, lemos recentemente que há comandos que nos permitem repetir trechos do código.
Podemos usar o comando while para definir um determinado trecho de código que desejamos
repetir e associar uma condição de repetição a esse trecho. Enquanto essa condição for
verdadeira, o trecho será repetido.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 67

Controle de repetição com o comando while

Um dos construtores estruturados que citamos anteriormente é a Repetição. Esse tipo de controle
de fluxo pode ser implementado com o uso do comando while (enquanto), descrito a seguir:
O comando while especifica um ou mais comandos indentados de que serão repetidos,
sob uma determinada condição.
Enquanto essa condição for True os comandos subordinados ao while serão repetidos.
Essa condição deverá, em algum momento da execução repetida desses passos, passar a
valer False, para que as repetições terminem e o fluxo volte a seguir sequencialmente a
partir do primeiro comando não subordinado ao while.

A forma geral do comando de repetição while é:


Substitua aqui por uma condição (pergunta)
de resultado True ou False.
while <condição de repetição> :
<comandos-subordinados-ao-while>
Substitua aqui por um ou mais comandos que a
<comando seguinte ao while>
sua lógica precise executar várias vezes
(repetidamente) se a <condição de repetição>
tiver resposta True.
Cada comando de uma linguagem de programação possui uma FORMA GERAL e
REGRAS DE ESCRITA, que devem ser seguidas para que o computador consiga analisar
os comandos, traduzí-los para a linguagem de máquina e executá-los.

Na construção acima, <comandos-subordinados-ao-while> serão repetidos até que <condição>


fique false, ou em outras palavras, enquanto <condição> ocorrer (for true), <comando-
subordinado-ao-while> será repetido. Exemplo:

Aqui substituímos <condição de repetição> por uma


decisão (condição, pergunta) com resultado verdadeiro
ou falso. Enquanto o valor x for maior que o valor y, o
while x > y : <comando-subordinado-ao-while> será repetido e a
x = x - 1 condição será verificada novamente a cada repetição.
print(x) Aqui substituímos <comando-subordinado-ao-while>
pelo comando x = x - 1, que desejamos repetir
enquanto a pergunta x > y tiver resposta True.

Este é o <comando seguinte ao while>, e será


executado logo depois que a <condição> x > y ficar
falsa, ou seja, quando x ficar menor ou igual a y.

Caso haja mais de uma ação a repetir no comando while, indentamos um bloco dos vários
comandos que serão repetidos enquanto a condição de repetição for satisfeita (resultar True).
Se <condição de repetição> nunca deixar de ocorrer, então <comando-subordinado-ao-while>
será repetido indefinidamente. A isto chama-se LOOP infinito.
Portanto, para podermos aprimorar ainda mais nosso programa, é interessante repetir várias
vezes o trecho que pergunta quais números serão processados e qual operação será calculada.
Assim, nosso usuário poderá realizar várias operações, com diferentes valores. Mas quantas
vezes esse trecho deve ser repetido?
A priori não sabemos quantas vezes o usuário desejaria repetir o processo, mas ele também
poderá mudar de ideia enquanto realiza os cálculos. Assim, talvez a melhor abordagem seja
perguntar, sempre depois que um cálculo for realizado, se o usuário gostaria de continuar o uso
da calculadora ou se deseja terminar seu uso. Em outras palavras, a determinação do momento
de parar a execução passaria a ser do usuário.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 68

Assim, depois de executar um cálculo, perguntaremos ao usuário se deseja sair do programa. Se


desejar, não repetiremos o processo de perguntar quais os valores, qual a operação, calcular essa
operação e exibir seu resultado. Caso contrário, se o usuário desejar realizar mais alguma outra
operação, faremos com que o código que realiza as tarefas citadas seja repetido. Portanto,
teremos de emitir uma mensagem ao usuário, perguntando se deseja parar o programa, ler essa
resposta e repetir ou não essas tarefas, de acordo com a resposta do usuário.
Como o trecho que realiza essas tarefas será repetido, ele deverá passar a ser subordinado
(indentado) a um comando while. Esse comando while deverá ter uma condição que informe qual
o critério de repetição dos comandos subordinados a ele.
Vamos supor que perguntemos ao usuário se ele quer continuar a execução do programa, e que
responda S para continuar e N para terminar o programa. O critério de repetição será a resposta
do usuário ser igual à letra S. Assim, o início do nosso módulo principal seria modificado para:

operacoesPrevistas = "+-*/"
umaCalc = calculadora.Calculadora()
umaCalc.Ligar() O fluxo entra no while, testa a
resposta = "S" condição e, se ela for true, entra no
while resposta == "S" : bloco e executa cada um de seus
# obter os valores para cálculo comandos e retorna à condição,
# obter a operação desejada que será testada novamente.
# realizar o cálculo desejado O while e o bloco a ele subordinado
# exibir os resultados serão executados repetidamente
# perguntar se deseja continuar ou parar enquanto a condição for true.
espera = input("Digite [Enter] para terminar este programa:")
umaCalc.Desligar()

Vamos então expandir nossa lógica, substituindo os comentários dentro do while acima pelos
comandos que já codificamos antes.

resposta = "S"
while resposta != "S" :
umaCalc.ObterNumeros()
operacao = input("Que operação deseja fazer (+, -, *, /) ?")
if operacoesPrevistas.find(operacao) != -1:
if operacao == "+" :
umaCalc.Somar()
elif operacao == "-":
umaCalc.Subtrair()
elif operacao == "/" :
umaCalc.Dividir()
else:
umaCalc.Multiplicar()
umaCalc.ExibirResultado()
else:
print("A operação digitada não está prevista.")
resposta = input("Deseja continuar a execução (S/N)? ")
espera = input("Digite [Enter] para terminar este programa:")

Mas o que aconteceria se o usuário quisesse usar os mesmos valores que já tinha digitado, mas
em outra operação aritmética? Na forma que o programa tem acima, o usuário deveria digitar
novamente os mesmos valores. Isso não é muito interessante para o usuário.
Para resolver isso, criaremos um seletor de opções (também conhecio por menu) no lugar da
pergunta sobre que operação o usuário deseja calcular, pois agora teremos mais uma opção, que
é a digitação dos números.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 69

Se o usuário desejar digitar novos números, basta digitar essa opção. Caso deseje manter os
mesmos números que na operação anterior, basta digitar o símbolo da nova operação:

import calculadora
# bloco principal
print("\x1b[2J\x1b[1;1H") # Apaga a tela
operacoesPrevistas = "+-*/"
umaCalc = calculadora.Calculadora()
umaCalc.Ligar()
resposta = "S"
while resposta == "S" :
print("Operações disponíveis")
print("========= ===========\n")
print("9 : Digitar os dois números")
print("+ : Adicionar os dois números")
print("- : Subtrair os dois números")
print("* : Multiplicar os dois números")
print("/ : Dividir os dois números\n")
operacao = input("Informe qual operação deseja:")
if operacao == "9" :
umaCalc.ObterNumeros()
elif operacoesPrevistas.find(operacao) != -1:
if operacao == "+" :
umaCalc.Somar()
elif operacao == "-":
umaCalc.Subtrair()
elif operacao == "/" :
umaCalc.Dividir()
elif operacao == "*" :
umaCalc.Multiplicar()
umaCalc.ExibirResultado()
else:
print("A operação digitada não está prevista.")
resposta = input("Deseja continuar (S/N)? ")
espera = input("Digite [Enter] para terminar este programa:")
umaCalc.Desligar()
print("Obrigado!")

No código acima, a variável resposta recebe a string S. Em seguida, o fluxo de execução passa
para o comando while, que verificará o resultado da decisão que lhe é oferecida, ou seja, a
condição resposta == “S” será testada e, como resposta acabou de ser iniciada com a string “S”,
nessa primeira execução do while a condição será verdadeira e o fluxo de execução entra nos
comandos indentados (subordinados) ao while. Assim, o seletor de opções será exibido, o usuário
digitará a opção desejada, esta será verificada e executada e, no final do bloco indentado ao
while, será perguntado ao usuário se deseja continuar a execução. A variável resposta receberá,
agora, o que o usuário digitar e o fluxo retorna para o comando while, onde novamente será
verificado se o conteúdo da variável resposta é igual a “S”. Caso seja, os comandos indentados ao
while serão novamente executados, repetindo o ciclo. Se o usuário digitou qualquer caracter
diferente de “S”, a condição do while resultará em false e o fluxo de execução para as repetições
do while e pula para o comando que lê a variável espera.
Podemos criar um método separado que apresente o seletor ao usuário, pois dessa maneira
colocaremos dentro dele apenas os comandos que realmente são relacionados à exibição da lista
de opções do programa.
Isso torna o módulo principal mais organizado, pois separa as diversas atividades que compõe o
programa em partes separadas, da mesma maneira que se separam os assuntos de um livro em
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 70

capítulos, para que não se misturem todos os assuntos e seja mais fácil de encontrar um assunto
específico.
Vamos, também, criar uma opção “0” para saída do programa, para que o usuário não tenha que
responder à pergunta se deseja continuar ou não. Isso pode ser feito como vemos a seguir:

import calculadora

def SeletorDeOpcoes() :
print("\nOperações disponíveis")
print("========= ===========\n")
print("0 : Terminar este programa")
print("9 : Digitar os dois números")
print("+ : Adicionar os dois números")
print("- : Subtrair os dois números")
print("* : Multiplicar os dois números")
print("/ : Dividir os dois números\n")
escolha = input("Informe qual operação deseja:")
return escolha

# bloco principal
print("\x1b[2J\x1b[1;1H") # Apaga a tela
operacoesPrevistas = "+-*/"
umaCalc = calculadora.Calculadora()
umaCalc.Ligar()
operacao = "S"
while operacao != "0" : Usamos aqui vários comandos de
decisão para que o computador decida
operacao = SeletorDeOpcoes() qual método do objeto umaCalc deverá ser
if operacao != "0" : chamado. Essa decisão se baseia no valor
da variável operacao, que foi recebeu o
if operacao == "9" : resultado retornado pelo método
umaCalc.ObterNumeros() SeletorDeOpcoes()
elif operacoesPrevistas.find(operacao) ! = -1:
if operacao == "+" :
umaCalc.Somar()
elif operacao == "-":
umaCalc.Subtrair()
elif operacao == "/" :
umaCalc.Dividir()
elif operacao == "*" :
umaCalc.Multiplicar()
umaCalc.ExibirResultado()
else:
print("A operação digitada não está prevista.")
espera = input("Digite [Enter] para terminar este programa:")
umaCalc.Desligar()
print("Obrigado!")

Execute esse programa passo a passo, usando as várias opções e buscando acompanhar o fluxo
de execução nas diversas possibilidades de desvio de fluxo e repetição de fluxo.
Da mesma maneira, podemos agrupar os comandos que tratam de determinar e executar a
operação que foi escolhida pelo usuário. Assim, o programa fica ainda mais organizado e seus
diferentes assuntos menos misturados. Observe o código abaixo e o compare com a primeira e
segunda versões, e note como os métodos ajudam a delimitar os diversos assuntos e tornar mais
fácil o entendimento de cada parte que forma o programa:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 71

import calculadora
def SeletorDeOpcoes() :
print("\nOperações disponíveis")
print("========= ===========\n")
print("0 : Terminar este programa")
print("9 : Digitar os dois números")
print("+ : Adicionar os dois números")
print("- : Subtrair os dois números")
print("* : Multiplicar os dois números")
print("/ : Dividir os dois números\n")
escolha = input("Informe qual operação deseja:")
return escolha Realizar() recebe parâmetros,
ou seja, valores para usar nos
def Realizar(oper, umaCalc) : seus comandos internos.
operacoesPrevistas = "+-*/" Esses valores são a operação
if oper == "9": que foi digitada pelo usuário e o
umaCalc.ObterNumeros() objeto da classe Calculadora já
elif operacoesPrevistas.find(oper) != -1: instanciado
if oper == "+":
umaCalc.Somar()
elif oper == "-":
umaCalc.Subtrair()
elif oper == "/":
umaCalc.Dividir()
elif oper == "*":
umaCalc.Multiplicar()
umaCalc.ExibirResultado()
else:
print("A operação digitada não está prevista.")
Este bloco é chamado de Bloco Principal, pois
# bloco principal o fluxo de execução do programa começa a ser
print("\x1b[2J\x1b[1;1H") # Apaga a tela executado logo no primeiro comando não
umaCalc = calculadora.Calculadora() indentado fora de uma classe ou de definição
umaCalc.Ligar() de função (def). Esse bloco é responsável por
instanciar objetos de classes que codificamos e
operacao = "S" chamar funções e os métodos desses objetos
while operacao != "0" : para resolver o problema.

Comandos que serão REPETIDOS enquanto


operacao = SeletorDeOpcoes() resposta for igual a ‘S’.
if operacao != "0": Ao fim do bloco, o fluxo retorna ao while e testa
Realizar(operacao, umaCalc) a condição de novo.

espera = input("Digite [Enter] para terminar este programa:")


umaCalc.Desligar()
print("Obrigado!")

Observe que, depois que codificamos a classe Calculadora, quase não mexemos mais nela,
dedicando-nos a melhorar a forma como ela seria usada no programa. Assim, a separação da
lógica entre objetos e programas ajuda na montagem do programa, pois depois que realizamos a
parte principal da lógica de solução do problema (que é feita na classe Calculadora), podemos
pensar de forma mais profunda em como o programa usará essa classe e como o usuário usará o
programa.
Um usuário, na verdade, não sabe se o programa contém classes ou não, pois ele apenas verá,
na tela, aquilo que o programa lhe apresenta, não vendo o código fonte das classes e do
programa.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 72

Teste esse programa no computador, passo a passo, usando [F10] e [F11], para ver como o fluxo
de execução se comporta no comando while e como entra nos métodos internos e retorna ao
while após executá-los.
Como desafio, modifique o código para termos uma opção “T – terminar o programa”, colocando-a
no seletor e tratando-a, de forma que não seja preciso perguntar ao usuário, no final do while, se
deseja continuar a execução do programa. Ou seja, terminar o programa passaria a ser uma
opção do usuário, apresentada a ele dentro do seletor de opções.

Exercícios
Todos os exercícios abaixo envolvem a modelagem de classes que representem a realidade dos
problemas apresentados e contenham os atributos e métodos adequados para solucioná-los.
Usando a linguagem Python e o PyCharm, descreva a classe que armazena os dados
necessários e que também contenha métodos ler os dados, para realizar cálculos e para exibir os
resultados desses cálculos. Escreva também uma única aplicação que exibe um seletor de
opções repetitivo, que permita ao usuário informar qual exercício deseja executar e, a partir do
exercício informado, instancia um objeto da classe que soluciona o exercício e chama seus
métodos de leitura, cálculo e exibição dos resultados, para atender ao solicitado pelo usuário.
4.1. Escreva uma classe que armazene a temperatura de uma amostra de água e exiba uma
mensagem informando se a água está congelada, fervendo ou em outro estado.
4.2. Escreva uma classe que armazene um número inteiro e um método lógico que verifique se
ele possui quatro dígitos. Deve também ter um método que, caso o valor inteiro possua
quatro dígitos, verifique se o número atende a seguinte propriedade: a soma entre o
quadrado dos dois primeiros dígitos com o quadrado dos dois últimos dígitos é igual ao
número original. O programa deverá solicitar ao usuário que digite o valor inteiro, verifique
se ele possui 4 dígitos. Se não possuir, exibe-se mensagem de advertência ao usuário. Se
possuir, deve chamar o método que verifica se o número atende ou não a propriedade
acima descrita e informar o usuário desse resultado. Você pode separar os dígitos
formadores de um valor inteiro usando operação de cálculo do quociente de divisão inteira
e resto de divisão inteira.

4.4. A Classe Equação do Primeiro Grau


Discutiremos o problema, suas partes e escreveremos o algoritmo de resolução. Uma equação do
1o grau tem o seguinte formato geral:

ax + b = 0

onde a e b são chamados os coeficientes da equação, e x é chamado de incógnita. a, b e x


podem assumir quaisquer valores reais.

O objetivo é calcular o valor de x, que é dado pela relação

x = -b / a

O coeficiente a deve ser diferente de zero, para que não aconteça uma divisão por zero.
Assim, o programa que desenvolveremos deverá levar em conta essa condição ( a ≠ 0 ) para
calcular x. Podemos pensar num objeto cujos atributos armazenem os coeficientes da equação (a
e b) informados pelo usuário e, quando solicitado, calcule o valor da incógnita.
Note que acima dissémos que o objeto armazenará os coeficientes informados pelo usuário e
não que o objeto lerá esses coeficientes. Nas classes que fizemos antes, o objeto se encarregava
de ler os valores dos atributos que encapsula. Por que essa diferença de abordagem, agora?
Bem, há diferentes maneiras de se obter os valores dos atributos. Um programa console (modo
texto) poderá ler os dados diretamente do teclado, caso em que usamos input() para ler os
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 73

valores. Um programa em modo gráfico pode ter uma caixa de digitação de textos num formulário,
caso em que o valor já fica disponível para processamento quando digitado, e dai não poderemos
usar input(). Ou, então, os valores podem ser obtidos através de uma conexão de rede, usando
um objeto de conexão de dados ainda mais complexo. Além disso, os dados podem ser obtidos
por meio de coleta de dados através de sensores como Arduino e seus componentes, em
programas de automação de processos e Internet das Coisas.
Observe nas figuras abaixo um programa em modo console (à esquerda) e em modo de
formulário Windows ou modo gráfico (à direita):

Embora o resultado seja o mesmo para as duas aplicações, a interface com o usuário e o modo
de operação são bastante diferentes entre os dois.
O armazenamento dos dados e o cálculo da raiz pode ser feito por uma única classe, que pode
ser usada nos dois programas. No entanto, a maneira e obter os dados difere muito entre um
programa e outro. Portanto, a maneira de obter os dados não depende da classe que resolve o
problema e sim do tipo de aplicação que usa essa classe.
Essa situação ocorre na grande maioria das aplicações e soluções. Usaremos classes que
solucionam problemas, mas essas classes não serão responsáveis pela obtenção dos dados, pois
elas não têm como saber de que maneira os dados serão fornecidos. Quem sabe isso é a
aplicação, pois a interface com o usuário é criada de acordo com as características da aplicação.
Assim, a partir de agora nossos dados serão lidos pela aplicação e enviados à classe que
resolve o problema.
Para fazer esse envio dos dados lidos pelo programa (fora da classe) para os atributos internos da
classe, podemos usar dois conceitos: o construtor ou propriedades de atribuição. No momento,
vamos estudar o uso de construtor para enviar os dados para os atributos.

Método Construtor e passagem de parâmetros

Um construtor é o método executado quando um objeto é instanciado. Quando usamos o


comando

umaCalc = calc.Calculadora()

estamos chamando o método construtor da classe Calculadora. Na definição da classe, em


Python, o método inicializador __init__() é o construtor da classe.
Até o momento usamos esse tipo de comando para criar o objeto que contém os atributos e
métodos que resolvem o problema em questão.
Toda classe possui um construtor básico, chamado de construtor default ou implícito, que
cria um objeto na memória usando a classe como modelo, mas sem declarar nem preencher seus
atributos com quaisquer valores, mesmo os mais simples possíveis (zero, por exemplo). O método
inicializador default não tem como saber quais serão os atributos desejados para cada classe.
Assim, para resolver uma equação do primeiro grau, poderemos definir uma classe semelhante a:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 74

class Equacao1oGrau: Método construtor explícito

def __init__(self, coeficiente, termo):


self.a = coeficiente
self.b = termo

Na sintaxe da linguagem Python, o construtor sempre se chama __init__. Assim, quando o


programa principal instancia um objeto dessa classe, esse método é chamado, como vemos
abaixo:

... Comando que instancia (cria, constrói) o


objeto umTriangulo na memória.
umTriangulo = tri.Triangulo() Triangulo() chama o método __init__(),
... construtor da classe

No VSCode, crie e abra um nova pasta, chamada pyEquacao1, na pasta Cap04. Na janela
Explorer, adicione os arquivos main.py e equacao1.py ao projeto. Nesse último arquivo, digite o
código dessa classe como está codificado acima, incluindo seus atributos e o método construtor
explícito __init__().
Em nossa solução para a equação do 1o grau, o programa principal ficará responsável por
obter os valores do coeficiente linear e do termo independente, para que a classe Equacao1oGrau
não tenha de se responsabilizar por entrada e saída de dados e, para isso, o programa
principal poderá usar comandos como abaixo, para obter os dados necessários.
Observe que não estamos lendo esses dados na classe, e sim no programa principal. Digite o
código marcado no arquivo main.py:

import equacao1

print("\x1b[2J\x1b[1;1H") # Apaga a tela


print("Equação do Primeiro Grau\n")
coef = float(input("Digite o coeficiente linear da equação: "))
termo = float(input("Digite o termo independente da equação: "))

Assim, a interface com o usuário ficará sob a responsabilidade do programa principal, que fará a
obteção dos valores dos coeficientes e a exibição de mensagens e resultados para o usuário.
A classe Equacao1oGrau, que terá a responsabilidade pelos atributos e métodos que
solucionarão o problema da equação do 1º grau, mas não terá como sua obrigação saber de que
maneira será feita a obtenção dos dados, pois não sabe que tipo de interface com o usuário se
está usando (se mobile, console, GUI, Web ou outros tipos de interface).
Fixar um tipo único de interface na classe de solução do problema tornará mais difícil adaptar
essa classe para outros tipos de interface com o usuário. Assim, é melhor que a classe de
solução não tenha que lidar com a interface com o usuário. Assim, a classe Equacao1oGrau
poderá ser reaproveitada em aplicativos de diferentes tipos de interface, pois não está presa a
nenhum tipo.
Agora vamos instanciar, no módulo principal da solução, um objeto da classe Equacao1oGrau
para que ele receba os valores digitados e os use no cálculo da raiz da equação. Portanto, temos
de chamar o construtor dessa classe, logo após a leitura dos dados digitados. Mas não basta
chamá-lo através do construtor default, como no exemplo abaixo:

umaEquacao = equacao1.Equacao1oGrau()

O código acima funcionará, mas chamará o construtor default da classe Equacao1oGrau. Com
ele, os atributos a e b não serão declarados nem receberão os valores digitados. Nessa situação,
não teremos como calcular o valor da raiz.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 75

Para que os valores digitados sejam transferidos para a classe (que não os lerá do teclado, pois
isso não é sua obrigação), vamos usar o mecanismo de passagem de parâmetros, que já
iniciamos ao digitar o construtor. Observe o código abaixo, mais modularizado e mais legível:

import equacao1
1. Dados são lidos
print("\x1b[2J\x1b[1;1H") # Apaga a tela
print("Equação do Primeiro Grau\n")
coef = float(input("Digite o coeficiente linear da equação: "))
termo = float(input("Digite o termo independente da equação: "))

umaEquacao = equacao1.Equacao1oGrau(coef, termo)


2. Construtor com parâmetros
é chamado
Esses valores, colocados entre
parênteses, são conhecidos 3. Os valores dos argumen-
como argumentos da chamada tos de chamada são transfe-
ridos ao construtor da classe

4. Os valores dos argumentos


class Equacao1oGrau : são recebidos no construtor e
guardados nos parâmetros
def __init__(self, coeficiente, termo):

self.a = coeficiente 5. Os valores dos parâmetros são copia-


self.b = termo dos nos atributos, que são as variáveis
membro que guardarão esses valores
para serem processados pela classe.

Esse mecanismo faz com que os valores lidos sejam passados (via lista de parâmetros) para o
método construtor, que os usa para atribuir os valores lidos aos atributos da classe. Esses
atributos poderão ser usados, em seguida, para realizar cálculos como, por exemplo, o valor da
raiz da equação.
Tendo digitado esses códigos preliminares dos módulos equacao1 e main, vamos testar a
execução passo a passo, para visualizarmos e compreendermos na prática o mecanismo de
passagem de parâmetros.
Ajuste as janelas do editor de código do VSCode para que o arquivo main.py fique à esquerda do
arquivo equacao1.py. Para isso, clique no título da janela main.py com o botão direito do mouse e
selecione a opção Dividir à Direita.
Coloque um breakpoint no comando que faz a leitura da variável coef. Clique no botão de
depuração para iniciar a execução passo a passo. Pressione [F10] em sequência até ter
executado os comandos que exibem as mensagens solicitando e lendo as duas variáveis do
coeficiente linear e do termo independente de uma equação do 1º grau. Por exemplo, digite os
valores 10,7 e -30. As nossas janelas estarão como abaixo:

As variáveis coef e termo foram lidas, e seus valores estão prontos para serem usados.
Podemos visualizar o valor de uma variável ou objeto, apenas colocando o cursor sobre ela.
Vemos, portanto, que os valores solicitados foram lidos no módulo principal e armazenados nas
variáveis coef e termo. Elas não têm contato algum com a classe Equacao1oGrau.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 76

Observe que, nesse momento, o objeto umaEquacao não existe, pois ainda não foi instanciado, já
que não chamamos seu construtor.
Pressione [F11] para executar o próximo comando, em que a variável umaEquacao será
declarada e preenchida com a instância da classe Equacao1oGrau.
Observe que os valores de coef e termo serão repassados para o construtor __init__ dessa
classe, e que os parâmetros coeficiente e termo declarados na assinatura desse construtor
receberão os valores as variáveis que foram lidas há pouco.

Veja no código acima que aparecem, durante a depuração, os


valores dos parâmetros. Observe também na janela de variáveis
que os parâmetros coeficiente e termo receberam os mesmos
valores das variáveis coef e termo do módulo principal.
A variável self é o próprio objeto da classe Equacao1oGrau que
estamos usando. Observe na figura ao lado que seus atributos a
e b ainda não receberam nenhum valor.
Pressione [F11] para que os atributos a e b recebam os devidos valores, presentes nos
parâmetros.
Coloque o cursor sobre os parâmetros coeficiente e termo da lista de parâmetros do método
construtor, e observe que seus valores são, respectivamente, os mesmos das variáveis coef e
termo, do módulo principal do programa. Ou seja, sem que usássemos o comando de atribuição
=, os valores que digitamos e foram armazenados nessas variáveis foram atribuídos para esses
parâmetros:

Pressione [F11] e observe que o atributo a receberá o valor do parâmetro coeficiente e o atributo
b receberá o valor do parâmetro termo. a e b serão usados, como todo atributo de classe, para
armazenar os dados que serão processados pelos métodos dessa classe.
Pressione [F11] até que o fluxo de execução sai do
construtor e retorne ao módulo principal.
Pressione mais uma vez [F11] e observe que o fluxo está
no comando print(umaEquacao) do módulo principal e veja
que, na janela de variáveis, a variável umaEquacao
armazena uma instância (um exemplar) da classe
Equacao1oGrau, cujos atributos a e b valem 10.7 e -30.
Os valores digitados foram transferidos para um objeto da classe Equacao1oGrau sem que esta
classe precisasse ter métodos específicos para leitura de dados. Assim, esta classe não está
vinculada a este tipo de interface com o usuário e poderá, teoricamente, ser usada em aplicativos
de interface gráfica com o usuário (GUI), por exemplo.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 77

Em seguida, iremos discutir quais métodos a classe Equacao1oGrau precisará para resolver uma
equação do 1º grau, ou seja, calcular sua raiz (incógnita x), e como organizaremos esses
métodos.

Métodos como Funções que retornam um valor

Ainda não codificamos um método para o cálculo, nem declaramos um atributo para armazenar a
incógnita x. Isso foi feito de propósito, para introduzirmos o conceito de métodos como funções.
Como você sabe, x é calculado em função dos coeficientes a e b da equação do 1º grau. Isso
quer dizer que, sempre que o valor de a ou de b mudarem, o valor de x também mudará.
Mas, se declararmos um atributo x e se o programa o acessar antes de ter sido calculado
haverá, no mínimo, um erro no valor x exibido, pois este ainda não teria sido calculado em função
dos valores de a e de b. Portanto, ao invés de declararmos x como um atributo, usaremos uma
função com devolução para calcular seu valor sempre que necessário e devolvê-lo.
Uma função com devolução é um método que, ao ser chamado por um comando qualquer,
realiza operações e retorna um resultado. Uma função com devolução devolve um valor que
calculou.
A função calcula um resultado, de acordo com o seu objetivo e, após isso, o devolverá para o
comando que a chamou. Esse comando receberá o resultado da função e o programa poderá
usar esse resultado logo em seguida.
Uma função sem devolução (como a maioria das que fizemos até agora) não devolve nenhum
resultado, ela simplesmente executa comandos internos e o fluxo de execução volta para depois
do comando que chamou essa função. Os métodos que usamos até o momento e que não tinham
devolução são como o Somar() ou Dividir().
Usar uma função com devolução na classe Equacao1oGrau é uma maneira de evitar que o valor
de x seja acessado antes de ser calculado. O acesso ao valor de x será, simplesmente, a
chamada da própria função num comando, e ela calculará o valor de x e devolverá esse valor ao
comando que a chamou. Assim, o valor de x sempre será calculado ao ser referenciado pelo
programa, e não apenas acessado em um atributo que poderá nem ter sido usado.
Digitaremos esse método/função com devolução na classe Equacao1oGrau como faríamos com
qualquer outro método encapsulado nessa classe. A única diferença é que esse método devolve
um valor e, portanto, usa o comando return para devolver um resultado, como vemos abaixo:

class Equacao1oGrau :

def __init__(self, coeficiente : float, termo : float):


self.a = coeficiente
self.b = termo
self.houveErro = False Type Hinting (dica de Tipo)
Esclarece que o método é uma função
com devolução. É opcional.
def ValorDeX(self) -> float :
self.houveErro = False
if self.a != 0 : Essa função devolve um resultado de tipo
float (real).
return -self.b / self.a Ela realiza operações e calcula um re-
sultado que será retornado para o
else : comando que a chamou.

self.houveErro = True
return 0.0

Sabemos que nem sempre podemos calcular o resultado de uma equação do primeiro grau.
Como citamos anteriormente, o coeficiente a deve ser diferente de zero. Assim, é importante que
a função que calcula o valor de x leve isso em consideração.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 78

Portanto, no código acima foi declarado um atributo houveErro. Ele será um campo (atributo) da
classe Equacao1oGrau de tipo lógico, e indicará se foi possível calcular ou não a incógnita. Uma
variável de tipo lógico pode armazenar os valores False ou True, que significam, respectivamente,
Falso e Verdadeiro.

O programa que "dá vida" a essa classe e a usa para resolver o problema teria o conteúdo abaixo:

import equacao1

print("\x1b[2J\x1b[1;1H") # Apaga a tela


print("Equação do Primeiro Grau\n")
coef = float(input("Digite o coeficiente linear da equação: "))
termo = float(input("Digite o termo independente da equação: "))
umaEquacao = equacao1.Equacao1oGrau(coef, termo)
x = umaEquacao.ValorDeX()
if umaEquacao.houveErro :
print("Impossível calcular x, pois a= 0!")
else :
print(f"Equação {umaEquacao.a}x + {umaEquacao.b} = 0")
print(f"x = {x}")
resposta = input("Tecle [Enter] para terminar este programa.")

Temos que declarar as variáveis e objetos que usamos, para


que o computador saiba quanto espaço de sua memória umaEquacao
deverá reservar para executar o programa. Toda variável
ocupa espaço na memória, e o tamanho desse espaço a b houveErro ValorDeX()
depende do tipo da variável.
No exemplo acima, a variável x é double (real) e a variável
umaEquacao é um objeto do tipo Equacao1oGrau que, 10 3 falso -0.3
dentro de si, contém (encapsula) mais 3 variáveis, duas reais
e uma lógica (atributos a, b e houveErro).
Observe que tratamos os atributos de um objeto como variáveis, pois um objeto é uma variável
que, dentro de si, possui campos (atributos) que armazenam valores. Assim, quando o valor de
uma variável objeto muda, é porque valores de um ou mais atributos mudaram.
Os campos (ou atributos) são, portanto, variáveis internas a uma outra variável, o objeto, que
encapsula esses campos dentro de si.

Mais sobre Funções com devolução

Do ponto de vista da linguagem Python, todo método é uma função. Uma função é um bloco de
comandos identificado por um nome de método, e é chamado em algum ponto da execução do
programa, realiza suas operações, retorna ao ponto de chamada e devolve um valor que foi
calculado e que poderá ser usado nesse local de chamada, como se fosse uma variável.
No entanto, existem métodos que não precisam devolver um resultado ao local de chamada. Na
prática, é como se essas funções retornassem um valor vazio. Em algumas linguagens, esse tipo
de método é também conhecido como procedimento, pois apenas procedem à execução de
comandos e, ao terminarem, não retornam valores calculados em seu interior.
Em Python, procedimentos não precisam usar o comando return, pois não tem nada a devolver
para o local de sua chamada. Procedimentos são chamados em algum local do programa,
realizam suas tarefas e retornam ao ponto de chamada, sem devolverem nenhum valor que seja
usado nesse local de chamada.
Geralmente, métodos-procedimento são chamados como se fossem comandos isolados. Já
métodos que retornam algum valor (função com devolução) em geral são chamados dentro de
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 79

alguma expressão (aritmética, lógica, ou comando de atribuição) e o seu resultado retornado é


usado para que essa expressão seja finalizada e usada pelo comando que a abriga.
No exemplo abaixo, temos um método-função RaizCubica() que, teoricamente, calculará a raiz
cúbica do parâmetro valor, passado pelo programa na chamada dessa função dentro do cálculo
de uma expressão aritmética qualquer. Há também o método procedimento EsperarEnter(), ou
seja, que apenas realiza seus comandos internos e não retorna nenhum valor para ser usado no
local onde for chamado.

def EsperarEnter() :
espera = input("Tecle[Enter] para prosseguir")

def RaizCubica(valor) : A expressão onde esta função foi chamada


receberá este valor calculado e o usará para
valorDaRaizCubica = 0.0; atingir seu objetivo

// usa alguma sequência de operações aritméticas para calcular a raiz


cúbica do
// parâmetro valor, e armazena essa raiz na variável valorDaRaizCubica

return valorDaRaizCubica A função RaizCubica() está sendo chama-


da na expressão aritmética, que está
dentro de um comando de atribuição (=).
# módulo principal
O fluxo de execução salta para a
x = float(input(“Informe o valor de x:”) função, executa seus comandos e
retorna com um resultado, que
y = 2 * RaizCubica(x) + 37 será usado pela expressão para
3
terminar o cálculo de 2 √𝑥 + 37 e
atribuir seu resultado para y.
print(f"Resultado calculado = {y}")
EsperarEnter() Quando o comando de atribuição termina, o fluxo
print("Programa finalizado.Obrigado!") segue para print().
Chama o método EsperarEnter(), que é executado
e retorna após o local de sua chamada.
Como é um método-procedimento, nenhum valor é
devolvido e, assim, o comando que o chamou
termina e o fluxo segue.

4.5. Propriedades e Acessibilidade de Atributos


Em muitas linguagens de programação, como C#, Deplhi e Java, é possível impedir que atributos
de uma classe sejam acessados diretamente por outras classes ou módulos. Em outras palavras
esses atributos são chamados de privativos ou protegidos. Em C#, por exemplo, todo atributo de
uma classe é considerado privativo, e não pode ser acessado diretamente por uma outra classe, a
não ser que a programadora informe que esse atributo é público. Isso é feito colocando-se a
palavra public antes que o atributo seja declarado. Em Java, ocorre o contrário. Todo atributo é
público e pode ser acessado diretamente por outras classes, a não ser que seja declarado com as
palavras private ou protected. O mesmo raciocínio pode ser estendido para os métodos de uma
classe, nas duas linguagens.
Já em Python, não há como impedir que uma classe ou módulo acesse atributos ou métodos de
uma classe, pois Python considera que todos os membros de uma classe (atributos e métodos)
sempre serão públicos, e não há maneira de mudar essa acessibilidade nesse linguagem.
No entanto, nem sempre é aconselhável que um atributo seja totalmente acessível externamente,
sem controle, pois isso abre a possibilidade de que o valor armazenado nesse atributo seja
inválido.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 80

Muitas vezes, quem codifica uma classe não é o mesmo profissional que codifica um módulo
principal. Por mais que o desenvolvedor da classe seja cuidadoso, o fato de os atributos da classe
sempre serem públicos, permite que o desenvolvedor do módulo principal modifique o conteúdo
de um atributo importante antes de alguma operação crítica, sem que o usuário do programa
saiba que isso foi feito, como vemos abaixo:

Cria objeto usan-


print("Equação do Primeiro Grau\n") do os dados que
coef = float(input("Digite o coeficiente linear da equação: ")) o usário digitou
termo = float(input("Digite o termo independente da equação: "))
umaEquacao = equacao1.Equacao1oGrau(coef, termo)
umaEquacao.a = 0.0 O programa, sem que o usuário saiba,
muda o valor do atributo a, que poderá
x = umaEquacao.ValorDeX() ficar diferente do valor coef que o usuário
if umaEquacao.houveErro : digitou
print("Impossível calcular x, pois a= 0!")
else :
print(f"Equação {umaEquacao.a}x + {umaEquacao.b} = 0")
print(f"x = {x}")
O programa sempre exibirá a mensagem
de que é impossível calcular, mesmo que
o usuário tenha digitado coef diferente de
zero.

Se Python permitisse que indicássemos que os atributos são privativos ou protegidos, o


comando que diretamente atribui zero ao atributo a não seria aceito. Mas, como Pythn não
permite controlar a acessibilidade dos atributos e métodos de uma classe, esse tipo de erro é
possível, e temos que tomar muito cuidado com relação a situações como essa.
Uma maneira que os desenvolvedores Python usam para sinalizar que um atributo deve ser
considerado privativo é incluindo um caracter “_” (sublinhado, underline, undescore) antes do
nome do atributo. Isso realmente não impedirá que outro módulo mude o valor do atributo, mas
funciona como um alerta aos desenvolvedores que usam essa classe para que não mudem o
valor desse atributo diretamente, apenas com os métodos que a classe possui para isso (como o
construtor, por exemplo). Assim, podemos mudar nossa classe Equacao1oGrau para:

class Equacao1oGrau :

def __init__(self, coeficiente, termo):


self._a = coeficiente
self._b = termo
self._houveErro = False

def ValorDeX(self):
self._houveErro = False
if self._a != 0.0 :
return -self._b / self._a
else :
self._houveErro = True
return 0.0

A inclusão do “_” antes do nome dos atributos sinaliza que as classes e módulos que usarem a
classe Equacao1oGrau não devem mudar o valor desses atributos e, de preferência, nem mesmo
usarem esses atributos em seus códigos, pois são considerados de uso interno, privativo, da
classe.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 81

É como se colocássemos um alerta em um relógio: não mexa nas engrenagens ou circuitos dentro
deste relógio, mesmo sabendo que você tem acesso a eles, para que o funcionamento do relógio
seja o esperado.
Mesmo assim, o código de outros módulos ainda poderá modificar o valor desse atributo. Para
garantir que essas mudanças sejam controladas, podemos usar o conceito de Propriedade.
Propriedades atuam de forma que os outros módulos acessem os atributos de forma controlada.
Uma propriedade é um item da classe que declara um acessador para um atributo, que pode lê-lo
(para consulta apenas) e/ou alterá-lo (guardar um novo valor no atributo).
Em geral, as propriedades tem o mesmo nome que o atributo a ela associado. Em algumas
linguagens de programação, como C#, se sugere que o nome de uma propriedade tenha o
mesmo nome que o atributo, mas que inicie com a primeira letra em maiúsculo, enquanto se
recomenda que o nome de um atributo tenha letra inicial minúscula.
Já na linguagem Java não existe o conceito de propriedade. A mudança de valor e o acesso ao
valor de um atributo privativo devem ser feitos através de métodos acessadores, conhecidos por
getter (o que obtém o valor) e setter (o que muda o valor) de cada atributo privativo.
Em Python, criaremos propriedades de leitura e de modificação para cada atributo que precise
ser acessado por outro módulo, de forma que se possa obter (ler, pegar) o valor do atributo
associado e se possa mudá-lo (ajustar seu valor, sobrescrevendo o valor anterior e trocando-o por
outro). Essas propriedades terão o mesmo nome que seus atributos, mas sem o “_”.
No outro módulo que usar a nossa classe, usaremos os nomes do atributo sem o “_”. Por
exemplo:

if umaEquacao.houveErro : Chama o acessador getter para “pegar”


print("Impossível calcular x, pois a= 0!") o valor de cada atributo correspondente
else :
print(f"Equação {umaEquacao.a}x + {umaEquacao.b} = 0")

Para informar que estamos declarando propriedades, precisamos digitar o “decorador” @property
antes da declaração das propriedades. Em seguida, criamos uma função com o mesmo nome do
atributo cuja propriedades estamos criando. Esse método será o acessador de leitura (getter).
Logo em seguida, colocamos o decorador “@propriedade.setter”, para informar que estamos
codificando o acessador de modificação desse atributo:

class Equacao1oGrau :
def __init__(self,coefic:float,termo:float): Atributos com “_” para informar que
self._a = coeficiente precisam ser considerados privativos
self._b = termo
self._houveErro = False
Cria um método acessador getter,
@property com o mesmo nome do atributo
def a(self): correspondente, e que “pega” o valor
return self._a do atributo e o retorna para o local
onde esse método for chamado
@a.setter
def a(self, novoValor):
if novoValor != 0 : Cria um método acessador setter,
self._a = novoValor com o mesmo nome do atributo
correspondente, e um parâmetro com
else :
o novo valor desse atributo
print("Não há equação com a = 0!")

@property
def b(self):
return self._b
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 82

@b.setter
def b(self, novoValor):
self._b = novoValor
@property
def houveErro(self):
return self._houveErro
# não faremos o setter de _houveErro, para indicar
# que outros módulos não devem modificar esse atributo

No método construtor da classe, podemos atribuir valores para as propriedades e não para os
atributos pois, assim, qualquer lógica de validação que tivermos codificado no método setter será
executada. Observe abaixo como ficaria o construtor usando essa abordagem:
def __init__(self, coeficiente, termo):
self.a = coeficiente
self.b = termo
self._houveErro = False
Quando self.a (a sem o “_”) receber o valor de coeficiente, será
automaticamente chamado o método setter de _a, e coeficiente
será repassado para o parâmetro novoValor desse método.
É como se, implicitamente, o interpretador Python modificasse
esse comando para a(coeficiente), o que chamaria o método
abaixo:
@a.setter
def a(self, novoValor):
if novoValor != 0 :
self._a = novoValor
else :
print("Não há equação com a = 0!")

Experimente digitar o comando abaixo depois da instanciação de umaEquacao e antes da


atribuição da variável x:

umaEquacao.a = 0.0 # depois do teste, apague esse comando

umaEquacao, ao ser instanciada, fará com que os valores digitados pelo usuário sejam
armazenados nos atributos _a e _b.
Em seguida, o comando acima tentará atribuir 0 para o coeficiente linear (_a) o que geraria uma
equação inválida. Ao executar o acessador setter da propriedade a, é feito uma verificação se o
novo valor é diferente de zero.
Como, no caso acima, o novo valor é igual a
zero, o fluxo de execução vai para o else e exibe
uma mensagem dizendo “não há equação com
a = 0!“ e calcularia x para os valores
originalmente digitados.
Ao lado temos um exemplo dessa sequência de
acontecimentos, caso o usuário digite 4 e 9:
Retornaremos a esses conceitos futuramente
para novas aplicações e mais detalhes.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 83

4.6. Separação de Responsabilidades


Observe que nosso programa praticamente não faz nenhuma operação, a não ser usar os
recursos que o objeto umaEquacao lhe fornece.
Portanto, podemos ver o objeto como um fornecedor de serviços e o programa como a
aplicação que usa esses serviços. Os serviços são solicitados através das chamadas aos
métodos do objeto. A chamada dos métodos é também conhecida por envio de mensagem.
Em outras palavras, o programa envia uma mensagem ao objeto, solicitando a execução de um
serviço. O fluxo de execução "pula" para o início do método que implementa o serviço, executa-o
passo a passo e, ao final do método, retorna até o programa, no comando seguinte ao que enviou
a mensagem solicitando a execução do método.
Separar as ações do programa das ações do objeto permite que o código do objeto seja
reaproveitado em outros programas, que tenham aplicações diferentes para a equação de 1º grau.

4.7. Execução passo a passo


Vamos executar nosso programa passo a passo para acompanharmos o fluxo de execução e
visualizarmos o funcionamento dos parâmetros do construtor, da chamada à função ValorDeX() e
o acesso às propriedades houveErro, a e b.
Coloque um breakpoint no comando
que instancia umaEquacao.
Execute o programa em modo de
depuração e digite os valores
exibidos na figura ao lado (10 e -3):

Quando executamos uma leitura no teclado em modo console, o fluxo de execução para e
transfere o controle para o sistema operacional do computador. Nesse momento, o computador
fica esperando que o usuário digite um valor real, seguido por um [Enter], e após isso prossegue
normalmente a execução. Observe que um indicador de entrada aparecerá ao lado da mensagem
solicitando o dado.
Ele indica que o computador está esperando que o usuário forneça informações através do
teclado, digitando-as. Enquanto a operadora do programa não digitar os valores esperados, o
programa não prossegue sua execução, ou seja, o fluxo de execução continua na linha que
visitamos por último.
É importante você entender que apenas a tela console é visível por um usuário normal do
programa. Normalmente, o operador do programa (usuário) não vê os comandos internos do seu
programa, mas apenas o resultado da execução dos comandos. Isso significa que você, como
programadora ou programador, tem acesso aos comandos internos e pode executar seu
programa passo a passo, para procurar e corrigir erros, ao passo que o usuário não tem esse
privilégio (ou esse ônus, se seu programa tiver muitos erros para serem corrigidos).
Após a entrada dos dados, a tela ficará com a aparência abaixo, com uma tarja indicando o
comando que será executado. Observe os valores das variáveis coef e termo, colocando o cursor
sobre cada uma delas e clicando.
Pressione [F11] para executar o comando atual e verificar que o fluxo de execução saltará para o
construtor da classe.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 84

Observe os valores dos parâmetros coeficiente e termo, que serão os mesmos das variáveis coef
e termo, digitados no módulo
principal.
Como estamos usando
propriedades para controlar o
acesso aos atributos, o próximo
comando a ser executado
armazena o valor do parâmetro
coeficiente na propriedade “a”.
Isso fará com que seja
chamado o método setter de a
(@a.setter) e o parâmetro
novoValor desse método receba
o valor de coeficiente, como
veremos ao pressionar [F11]:
Pressione novamente [F11] e
veja que, como novoValor é
diferente de zero, a condição do if é verdadeira e, assim, o
fluxo de execução saltará para dentro do if, fazendo com
que o atributo _a receba, finalmente, o valor desejado:

Pressionando [F11], portanto, veremos o fluxo de execu-


ção retornar ao construtor e passar ao comando de atri-
buição de valor à propriedade “b”:
Pressionando [F11], o fluxo
de execução saltará para o
método setter de b, e o
parâmetro novoValor desse
método receberá o valor da
variável termo.
Pressionando [F11] de novo,
o atributo _b receberá,
finalmente, o valor desejado,
e o fluxo de execução
retornará para o final do
construtor. Observe o valor de “self”:
Pressione [F11] até que o fluxo retorne ao comando que atribui valor à variável x, no módulo
principal. Observe o valor de umaEquacao:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 85

Pressione [F11] novamente, para


chamar o método ValorDeX() do
objeto umaEquacao e armazenar
seu resultado na variável x. O fluxo
de execução saltará para esse
método e o executará conforme você
pressionar [F11] em sequência:
A condição do comando if dará True
e, assim, o comando subordinado ao
if será executado.
Esse comando calcula o resultado da
divisão de -b por a, e devolve esse
resultado para o local onde esse
método foi chamado (módulo
principal).

Observe que, no retorno do fluxo de execução ao módulo principal,


a variável x passará a armazenar o resultado retornado.

Podemos continuar pressionando [F11] para dar sequência à


execução do programa.
O comando if que verifica o valor de umaEquacao.houveErro
chamará o método getter do atributo _houveErro, método esse que
retornará o valor desse atributo que, neste momento, vale False.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 86

houveErro retornará False, e


esse será o valor avaliado no
if.

Como esse if dará falso, o fluxo seguirá para o else (pois não houve nenhum erro) e o resultado
calculado será exibido na tela. Observe que, quando o comando print escreve o valor de
umaEquacao.a e umaEquacao.b, o fluxo de execução salta para os acessadores getter dessas
propriedades.

Ao lado temos como a tela apareceria


ao usuário. Ela ainda não possui uma
apresentação adequada, e também
os números de resultado estão num
formato que poderia ser melhorado.
Futuramente aprenderemos como
melhorar esse formato, deixando a
tela mais agradável.
Salve seu projeto, pressionando as
teclas [Ctrl-K-S]. Dessa maneira, se
algo acontecer ao seu computador
(como perda de conexão à rede ou
queda de energia), seu programa
estará salvo com sua última versão.
Vamos discutir agora como melhorar um pouco a aparência do resultado e resolver o problema do
fechamento abrupto da janela console.
Para melhorar ainda mais a aparência e facilitar a leitura dos resultados pelo usuário (um ser
humano), vamos FORMATAR os valores reais exibidos. Formatar significa que diremos quantas
casas decimais e espaços serão escritas para cada valor. Isso se faz com uma especificação de
formato após cada item a exibir. Abaixo vemos como isso é feito:

else :
print(f"Equação {umaEquacao.a:5.1f}x + {umaEquacao.b:5.1f} = 0")
print(f"x = {x:8.2f}")
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 87

print exibirá os dados entre os parênteses e colocará o cursor na linha seguinte da tela, de modo
que uma linha será pulada antes de se exibir qualquer outra informação.
:5.1f é a formatação que citamos anteriormente. Indica que o valor a ser exibido utilizará
horizontalmente 5 posições na tela no total (incluindo o ponto e as casas decimais), e após o
ponto decimal haverá 1 casa decimal no máximo. É feito um arredondamento caso existam mais
dígitos após essa casa decimal.
:8.2f informa que o valor de x (entre chaves) será exibido usando 8 posições na tela, sendo que
após o ponto haverá 2 casas decimais. Ou seja, dessas 8 posições, uma ficará para o ponto
decimal e duas para as casas decimais. Antes do ponto decimal a parte inteira do valor poderá
ocupar até 5 posições.
O resultado para os valores 3 e 2 ficaria
como vemos ao lado:
A letra “f” no formato indica que o dado a
ser exibido é um float. Se você desejar
formatar um dado inteiro, use a letra “d” (de
decimal) para isso. Exemplo:
print(f"Total: {quantosAlunos:4d}")

4.8. Expressões Aritméticas


Na classe Equacao1oGrau, estude o comando return -self._b / self._a .
Ele indica que a função valorDeX() devolve (retorna) o resultado da expressão menos b
dividido por a para ser usado no local onde o objeto umaEquacao chamou essa função no
programa principal. Assim, a função valorDeX() resultará na divisão real de -b por a.
Como já vimos várias vezes, para modificarmos valores de variáveis e de atributos, usamos o
comando de atribuição =. Ele armazena na variável à sua esquerda o resultado da expressão à
sua direita. O formato geral do comando de atribuição é:

Variável = Expressão

Expressão é uma sequência de operadores e operandos que produz um resultado


Um operador é um símbolo que define uma operação.
Um operando é um valor sobre o qual a operação atua.

Atenção: nem sempre as expressões em um programa serão aritméticas. No entanto, neste


início de nosso estudo, usaremos bastante esse tipo de expressão e aprenderemos outros.
Os operadores aritméticos que já usamos nesta fase inicial do nosso estudo são os seguintes:
+ : adição
- : subtração
* : multiplicação
/ : divisão real (com casas decimais após a vírgula)
// : divisão inteira (sem casas decimais)
% : resto de divisão inteira
Esses operadores possuem prioridades iguais às da Matemática. Ou seja, *, / e // têm precedência
sobre + e -. Quando dois operadores com mesma precedência aparecem em sequência na
expressão (exemplo: 4 - 2 + 3), a prioridade é sempre dos operadores à esquerda (no exemplo,
faz a subtração antes da soma). Para se alterar a prioridade de operadores nas operações
aritméticas, usam-se parênteses.
Nas linguagens de programação e nos algoritmos não se usam colchetes e chaves para sucessi-
vos agrupamentos de operações, e sim mais parênteses. Vejamos exemplos mais complexos:
( 3 + 5 ) * ( 4 * ( 10 -7 ) ) / 2 Opera-se primeiramente o que estiver nos parênteses mais internos
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 88

(3+5)*(4*3)/2 Operam-se os dois parênteses que restaram


8 * 12 / 2 Só há * e / - , então opera-se da esquerda para a direita
96 / 2
48 Resultado final

Um outro exemplo:
6 * ( 9 + 3 * 2 ) / ( 2 * 4 - 11 ) Operam-se as multiplicações nos parênteses
6 * ( 9 + 6 ) / ( 8 - 11 ) Operam-se os parênteses
6 * 15 / -3 Só há * e / - opera-se da esquerda para a direita
90 / -3 Observe o sinal negativo antes do 3
-30 Resultado final

Outra informação importante é sobre a divisão. Uma expressão é descrita sempre em uma linha,
de maneira que, em um programa, não podemos escrever divisões com o dividendo numa linha e
o divisor na linha seguinte. O comando de atribuição abaixo, portanto, está escrito de forma errada
para um algoritmo, embora pareça correto na notação matemática:
-b
X=
a
O correto é escrever-se: x = -b / a numa única linha, usando o operador de divisão real /.
Devido a essa regra, deve-se tomar muito cuidado se o divisor é uma subexpressão. Por
exemplo, analise a fórmula matemática abaixo:

X’ = -b ± √Δ
2a

Usamos parênteses para mudar as prioridades dos operadores, e a forma correta passa a ser

x1 = ( -b - math.sqrt( delta ) ) / (2*a)

ao invés de simplesmente escrever x1 = -b - math.sqrt( delta ) / 2*a .


math.sqrt( valor ) é uma função definida no módulo math, ou seja, é uma operação que o
computador já conhece e que, nesse caso, usa para calcular a raiz quadrada (square root) de um
valor. No exemplo acima, a palavra delta representa o valor cuja raiz quadrada será calculada.
Note que foram colocados parênteses tanto no numerador quanto no denominador, para
determinar a ordem correta das operações em cada parte da divisão, e que usamos a palavra
delta escrita no alfabeto latino, e não a letra grega .

4.9. Variáveis Lógicas


O tipo bool representa valores lógicos True e False. Uma variável que receba um desses
valores será, portanto, declarada com o tipo bool. Uma variável lógica pode assumir, a cada
momento, um dos valores True ou False, o que nos permite criar comandos que usem
expressões lógicas.
Uma expressão lógica é uma expressão que envolve variáveis lógicas e operadores lógicos e/ou
relações condicionais. Expressões lógicas formam as condições usadas nos comandos if e
while. Exemplo:

Usando variáveis lógicas e if Usando expressões lógicas


x = False x = False
░ ░
if a > b : x = a > b
x = True ou
else : x = not a <= b
x = False
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 89

As expressões do quadro à direita têm o mesmo resultado que o comando if-else do quadro à
esquerda. Resumindo, os comandos abaixo têm os seguintes significados:

a. if not x : <comando-1> será executado se (not X) é verdadeiro, ou seja,


<comando-1> se x for igual a false, <comando-1> será executado.

b. x = <expressão lógica> x recebe o valor, true ou false, da <expressão


lógica>.
4.10. Operadores Lógicos e Tabela Verdade
São as palavras reservadas que identificam os conectivos que efetuam operações lógicas, como
NÃO, E, OU, OU Exclusivo. Esses operadores são usados para unir diferentes proposições
lógicas. Uma proposição lógica é uma pergunta com resultado verdadeiro (True) ou falso (False).

Assista esse vídeo explicativo sobre a Tabela Verdade e seu uso


As proposições podem ser simples ou compostas, e são usadas para criarmos as condições dos
comandos if e while.
Abaixo temos a Tabela Verdade, que estabelece os resultados esperados quando se usam os
conectivos lógicos (operadores lógicos) para se trabalhar com proposições compostas.

a. Não cond Não cond if not i <= 2 :


True False print(“Deu erro!”)
else :
False True print(“Valor correto!”)

b. E cond1 cond2 cond1 E cond2 def Ordenar() :


False False False if a >= b and b >= c:
False True False return f”{a} {b} {c}”
True False False else :
return “sem ordem!"
True True True

c. Ou cond1 cond2 cond1 Ou cond2 def MMC() :


False False False while True :
False True True … cálculos
True False True if a==1 or b==1:
return mmc
True True True

d. Ou Exclusivo cond1 cond2 cond1 Ou Exclusivo cond2 Não há um operador


False False False específico em Python
False True True Alternativa:
True False True
True True False return cond1 != cond2
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 90

Teorema de De Morgan: (Not A) and (Not B) ➔ not (A or B)


A B not A not B not A and not B A or B not (A or B)
F F V V V F V
F V V F F V F
V F F V F V F
V V F F F V F
Leia o texto indicado aqui, para conhecer mais sobre esses conceitos.

4.11. Condições Compostas


Uma condição, usada no comando de decisão if ou no comando de repetição while, é uma
proposição ou expressão lógica, cujo resultado é um valor verdadeiro, indicando que a
condição ocorre, ou falso, indicando que a condição não ocorre no momento. Esses são os dois
únicos resultados que uma condição, por ser uma expressão lógica, pode ter.
No entanto, uma condição pode ser formada por várias sub-condições. O resultado de cada sub-
condição compõe o resultado final da condição, que é determinado pela Tabela Verdade.
Geralmente, condições compostas são formadas por várias sub-condições que são agrupadas
através de um conectivo lógico ou operador lógico. Esses operadores implementam as lógicas
de conexão e, ou e não. Na linguagem Python, essas lógicas são representadas pelos
operadores and (e), or (ou) e not (não). São usados para unir duas ou mais sub-condições que
precisam ser satisfeitas para que a condição geral represente o raciocínio desejado para a
decisão. Por exemplo:

if a >= b and b <= c :


print(f"{a}, {b}, {c}")
else :
if a <= c or b >= a :
print("Imprevisto")
else :
print("Improvável")

Note que, para o resultado de uma condição composta que usa and (e) ser verdadeiro, todas as
sub-condições precisam ser verdadeiras simultaneamente. Basta que uma sub-condição seja
falsa para que o resultado final da condição seja falso também.
No caso de or (ou), basta uma sub-condição verdadeira para que o resultado final da condição
composta seja verdadeiro.
O operador lógico not (não) inverte o resultado da sub-condição: alterna verdadeiro com falso.
A tabela abaixo demonstra os possíveis resultados de condições compostas (SC = sub-condição):

E, And Ou, Or Não, Not


SC 1 SC 2 SC 1 and SC 2 SC 1 or SC 2 not SC 1
F F F F V
F V F V V
V F F V F
V V V V F

Para o mesmo trecho de código, teríamos os resultados abaixo para valores de a, b e c:

A B C a >=b b<=c a>=b e b<=c a<=c b>=a a<=c ou b>=a Exibição


12 5 8 V V V -o- -o- -o- 12, 5, 8
5 10 6 F F F V V V Imprevisto
6 5 3 V F F F F F Improvável
8 9 9 F V F V V V Imprevisto
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 91

Note que, quando usamos condições compostas, os conectivos lógicos ficam entre as sub-
condições. Pode-se fazer composições de duas ou mais sub-condições.

4.12. Diagramas de Execução (Teste de Mesa)


Cada variável ocupa um espaço, ou posição, da memória. Isto nos permite simular o comporta-
mento do fluxo de execução do programa e como cada passo executado age sobre a memória.
Podemos fazer essa simulação no caderno, usando um diagrama de execução, que é um mapa
da memória, onde colocamos os nomes das variáveis e os valores que elas assumem no
decorrer da execução do programa.
Por exemplo, suponha que temos os valores reais 7.5 e -1.2. Se simulássemos a execução do
programa apEquação1oGrau, o diagrama de execução seria semelhante à tabela abaixo:

7.5 -1.2 (Valores Lidos)


a : 7.5
b : -1.2
x : 0.16
7.5x + -1.2 = 0
x = 0.16

Quando o objeto umaEquacao é instanciado, são declarados os atributos a e b, reservando o


espaço de memória para cada atributo. Esse espaço, no diagrama acima, é representado pelo
nome de cada variável, seguido de dois pontos ( : ).
Em seguida, são lidos os valores dos coeficientes e armazenados em a (7.5) e b (-1.2). No passo
seguinte, chama-se a função que calcula x, onde é testado se a != 0. Como esta condição é
verdadeira (já que a vale 7.5), o fluxo da execução calcula a incógnita e a retorna. Dessa forma, x
recebe o valor retornado -b/a, que é 0.16. Então exibe-se a equação (7.5x + -1.2 = 0) e o valor de
x (x = 0.16). A execução do programa então para.
O programa seguinte não tem nenhum objetivo além de exemplificar nossa discussão sobre
diagramas de execução. Vamos usar um diagrama tabular, onde colunas representam variáveis e
linhas representam os comandos que as modificam. Use seu caderno, não digite o programa no
computador, mas siga-o passo a passo com a apostila e anote os valores no diagrama:

Linha a c b Entrada Saída

2
3

6
8

9
11

Para os valores de entrada 1.4 e 2, o diagrama de execução seria o seguinte:


COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 92

Linha a c b Entrada Saída


2 (1.4) ? ? 1.4
3 1.4 ? (2) 2
4 1.4 2.71828 2
6
8 21.4 2.71828 2
9 21.4 24.11828 2
11 21.4 26.11828 2
12 21.4 26.11828 2 21.40 2 26.1183

Os números entre parênteses indicam que esse valor foi digitado e lido, e a variável dessa coluna
o recebeu. Um segundo teste, com os valores de entrada -8.5 e -1, vem a seguir:

Linha a c b Entrada Saída

2 (-8.5) ? ? -8.5
3 -8.5 ? (-1) -1
4 -8.5 2.71828 -1
6 -1.5734 2.71828 -1
8

9 -1.5734 1.14488 -1
11 -1.5734 0.14488 -1
12 -1.5734 0.14488 -1 -1.57 -1 0.1449

Caso executássemos para um terceiro par de valores de entrada, como -200.5 e 7, teríamos:

Linha a c b Entrada Saída

2 (-200.5) ? ? -200.5
3 -200.5 ? (7) 7
4 -200.5 2.71828 7
6
8 -130.5 2.71828 7
9 -130.5 -127.78172 7
11

12 -130.5 -127.78172 7 -1.57 7 -127.7817

Agora crie esse projeto no PyCharm, edirte sua configuração de execução, digite o código e teste
sua execução, passo a passo; verifique como as variáveis assumem os valores acima.
Muitas vezes, o diagrama de execução é uma ferramenta de análise do funcionamento do
algoritmo mais adequada do que a execução “passo a passo” através de um computador, pois o
uso do papel e lápis, nesse e em vários casos, “liberta” o cérebro do programador do caminho
especificado pelo computador, permitindo maior percepção de problemas lógicos e maior
raciocínio, mais imaginação. Assim, é uma ferramenta complementar à Depuração do Ambiente
de Desenvolvimento.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 93

4.13. Funções Aritméticas de Python


A maioria das linguagens de programação possui funções pré-definidas, que são operações que o
programador pode usar para efetuar cálculos e, com isso, facilitam a vida do programador. Estas
funções realizam os cálculos aritméticos, trigonométricos e de manipulação e conversão de dados
mais comuns; assim, o programador não tem que reinventar a roda a cada programa que faz. A
este grupo de funções se dá o nome de biblioteca de funções.
Em Python, essas funções estão definidas como métodos do módulo math. Assim, você pode
declarar import math no início do programa e usar essas funções como abaixo:

raizQuadrada = sqrt(numero)
velocidadeEscape = sqrt(2 * massa *G / Raio)
aceleracaoGravidade = massa * G / pow(Raio, 2)
aElevadoAaB = pow(a, b)
Abaixo temos algumas funções úteis:
fabs(expressão) – Retorna o valor absoluto de uma expressão do tipo inteiro ou real.
Equivale a | expressão |.
acos( expressão) – Retorna o ângulo (em radianos) cujo co-seno é dado por expressão.
asin( expressão) – Retorna o ângulo (em radianos) cujo seno é dado por expressão.
atan( expressão) – Retorna o ângulo (em radianos) cuja tangente é dada por expressão.
cos( expressão) – Retorna o co-seno do ângulo (em radianos) dado por expressão.
pow( base, expoente) – Retorna o valor de base elevado a expoente, sendo ambos
expressões do tipo real.
trunc(expressão) – Retorna a parte inteira do valor real representado por expressão.
log10( expressão) – Retorna o logaritmo decimal (base 10) do valor dado por expressão.
log2( expressao) – Retorna o logaritmo na base 2 do valor representado por expressão.
pi – Retorna o valor 3.141592.
radians(graus) - Converte um ângulo medido em graus sua medida em radianos.
sqrt( expressão) – Retorna a raiz quadrada do valor representado por expressão.
sin( expressão) – Retorna o seno do ângulo (em radianos) dado por expressão.
tan( expressão) – Retorna a tangente do ângulo (em radianos) dado por expressão.
As expressões colocadas entre parênteses nas descrições acima são chamadas de parâmetros.
Parâmetros são usados para o cálculo do resultado da função. Por exemplo, sqrt(expressão)
recebe o valor expressão e calcula e retorna a sua raiz quadrada. Parâmetros fornecem os
valores que serão usados como entrada para a função. A função recebe o valor do parâmetro e o
usa para calcular seu resultado. Por exemplo, abs(-10) resulta em 10. -10 é o parâmetro
(expressão fornecida como entrada para a função) e 10 é o resultado da função.
Uma função pode ser usada em qualquer lugar onde uma variável pode ser usada, a não ser,
naturalmente, no “lado esquerdo de =” em um comando de atribuição – uma função produz (diz-se
no linguajar dos programadores retorna) um valor, e não o recebe.
Algumas linguagens não possuem todas as funções acima representadas, e podem ter outras
funções que não estão listadas acima. Por exemplo, Pascal não traz pronta uma função que cal-
cula a potência de um valor elevado a outro, mas pode-se criar uma função assim nessa lin-
guagem, usando as funções de logaritmo e exponencial neperianos que essa linguagem fornece.
No caso de valores inteiros para x e y, pode-se usar um processo repetitivo com produtório, que é
um conceito matemático que ainda aprenderemos e usaremos em programação
Algumas linguagens de programação possuem um símbolo específico para a operação aritmética
de exponenciação, ou seja, um valor x elevado a um valor y (por exemplo: ^, **).
Material adicional: https://www.w3schools.com/python/module_math.asp
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 94

Exercícios

4.3. Suponha que o programa abaixo tem 4.4. Faça o diagrama de execução do
como entrada o número do seu RA. programa abaixo, com os valores a = 3 e
Faça o diagrama de execução do b = 4:
mesmo, mostrando também o aspecto
dos dados de saída.

4.5. Faça o teste de mesa do programa abaixo, tendo como dados de entrada os valores:
5 10 -1 4 -4 3 0 5 8 (digitados em sequência)

Todos os exercícios abaixo envolvem a modelagem de classes que representem a realidade do


problema apresentado e contenham os métodos adequados para solucioná-lo usando a
linguagem Python e o VSCode, descreva a classe que armazena os dados necessários e que
também contenha métodos que realizam os cálculos solicitados e propriedades que permitam ao
programa consultar os atributos e os resultados desses cálculos.
Crie as classes que solucionam os problemas abordados nos exercícios, usando construtor com
parâmetros para receber os valores digitados pela aplicação que usar tais classes.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 95

As classes também deverão encapsular propriedades com acessador get para que o programa
possa acessar os resultados calculados. Em seguida, dando continuidade ao programa que
começou a ser codificado nos exercícios 4.1 e 4.2, adicione novas opções ao seletor de opções,
uma para cada exercício abaixo e implemente o tratamento dessas opções, instanciando um
objeto da classe que resolve cada problema específico e chamando seus métodos, a partir de
uma função do programa principal que é chamado quando a opção correspondente ao exercício
for escolhida no seletor de opções do programa. No programa da Calculadora temos um exemplo
desse seletor, clique aqui para rever.
Cada uma dessas funções do programa principal usará as classes descritas, lerá os dados,
instanciará objetos dessas classes passando os dados lidos para o construtor da classe, chamará
os métodos e propriedades da classe que calcularão e exibirão os resultados.
4.6. Escreva uma classe que armazene 3 valores reais, que representam o comprimento de 3
segmentos de reta. Nessa classe, codifique um método-função de tipo lógico ( public
bool FormamTriangulo() ) que verifique se os valores digitados formam um triângulo, e um
método void que determina (guarda num atributo string) qual tipo de triângulo formam
(“equilátero”, “isósceles”, “escaleno”, “retângulo”), que será acessado por propriedade get.
4.7. Escreva uma classe que armazene 3 valores reais, que são os coeficientes de uma
equação do 2º Grau, e codifique um método que calcule as raízes da mesma. Caso o
coeficiente quadrático seja nulo, calcule a raiz da equação do 1º Grau resultante, se for
possível, e a deixe disponível por uma propriedade com acessador get, também.
4.8. Um sistema de equações lineares da forma ax + by = c
dx + ey = f
ce − bf af − cd
pode ser resolvido utilizando-se as fórmulas: x= e y=
ae − bd ae − bd

Escreva uma classe que armazene o conjunto de coeficientes (a, b, c, d, e, f) e um método


que calcule uma solução, ou seja, x e y. Existem casos em que esse algoritmo não
funciona?
4.9. Escreva uma classe que armazene 3 valores inteiros e tenha um método-função que
determine sua disposição em ordem crescente e os retorne como uma string, nessa
ordem.
Os exercícios abaixo são baseados na apostila de algorítmos que acompanha o software VisuAlg,
de autoria do Prof. Bráulio de Paula Machado - 04 Mar 2004. Seus enunciados foram adaptados
para a criação de classes, de acordo com o modelo de objetos
4.10. Escreva uma classe que armazene o nome de um aluno e as notas de suas três provas.
Essa classe deve conter também um método que calcule a média
harmônica das provas e retorne o valor calculado para o programa. 3
m=
1 1 1
A média harmônica de três valores a, b e c é dada pela fórmula ao lado. O + +
programa lerá os dados e os repassará para o construtor criar uma a b c
instância dessa classe, para então chamar o método-função desse objeto
que calcula em retorna a média harmônica e a exibirá.
4.11. Escreva uma classe que armazene o valor do raio de um círculo e calcule sua
circunferência, sua área e o volume da esfera com esse raio. Para isso, além da variável
que armazena o raio, a classe deverá ter métodos para calcular e retornar os três valores
esperados e, também, uma propriedade que permita ao programa consultar o raio
armazenado.
4.12. Escreva uma classe que armazene três números reais, os leia e forneça a sua soma e a
sua média aritmética. Escreva também o programa que usa essa classe, instancia um
objeto nela baseado, armazene os valores, calcule a soma e a média, e exiba os
resultados, através da chamada dos métodos da classe que fazem essas operaçoes.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 96

4.13. Escreva uma classe que armazene 2 números, calcule e retorne o quociente e o resto da
divisão inteira do primeiro número pelo segundo, através de métodos que realizem essas
operações.
4.14. Escreva uma classe que armazene as 4 notas bimestrais de um aluno e forneça sua média
anual sabendo que a nota do 1º bimestre tem peso 2, a do 2º bimestre tem peso 3, a do 3º
bimestre tem peso 5 e a nota do 4º bimestre tem peso 6.
4.15. Codifique uma classe que armazene a área de um quadrado e calcule o lado de um
quadrado com metade da área lida.
4.16. Codifique uma classe que armazena um número inteiro que representa um valor em reais
e retorne uma string com uma mensagem que explicite o número mínimo necessário de
notas de 100, 50, 10, 5, 2 e 1 reais para decompor este valor.
4.17. Codifique uma classe que receba como entrada a hora de início (hora, minuto e segundo)
de um evento e a hora de término do mesmo. Um método-função deverá retornar a
duração do evento em segundos.
4.18. O serviço de cronometragem de uma maratona conta o tempo de cada atleta em horas,
com precisão de milésimos. Isto é, um tempo de 2,742 horas significa 2 horas e 742
milésimos de hora. Codifique uma classe que armazene o tempo de um atleta e retorne
uma string contendo o mesmo tempo em horas, minutos e segundos.
4.19. Codifique uma classe que armazene o nome, o dia, mês e ano do nascimento de uma
pessoa, a data atual e calcule o número total de dias de vida da pessoa. Considere os
meses com 30 dias. Um método-função de tipo string deverá retornar uma única cadeia
com o nome da pessoa, sua data de nascimento e o número de dias vividos.
4.20. Codifique uma classe que receba como entrada o número de dias que uma pessoa viveu e
forneça como saída uma string com o número de anos, meses e dias que esta mesma
pessoa viveu. Considere os meses com trinta dias.
4.21. Codifique uma classe que armazene a duração de um evento em dias, horas, minutos e
segundos; um método-função deverá retornar o valor correspondente em segundos.
4.22. Codifique uma classe que armazene uma quantidade de segundos e tenha um método-
função que retorne o valor correspondente em horas, minutos e segundos.
4.23. Faça uma classe que armazene: nome de um funcionário, seu número de registro, o
número de horas trabalhadas no mês, o número de horas extras trabalhadas no mês, o
valor da hora normal, o valor da hora extra. A classe deverá ter dois métodos-função que
retornem, respectivamente, o salário bruto e o salário líquido do funcionário, dados por:
salário bruto = (horas normais x valor da hora normal) + (horas extras x valor da hora
extra);
salário líquido = salário bruto – 15% IRPF – 17% INSS
4.24. Codifique uma classe que armazene o valor da altura, largura e comprimento de um
paralelepípedo e tenha um método-função que retorne o valor do comprimento da diagonal
do sólido.
4.25. Codifique uma classe que armazene o raio externo, o raio interno e o comprimento de um
tubo metálico e forneça métodos que retornem: a área da superfície externa do tubo, a
área da superfície interna do tubo e o volume de metal que constitui o tubo.
Área de um círculo de raio r : π r2 Comprimento de uma circunferência de raio r : 2πr
4.26. Codifique uma classe que receba como entrada os lados de um triângulo e tenha um
método que retorne a área desse triângulo. Seja um triângulo com lados a, b, c:
Semiperímetro (p): p = (a +b +c)/2
Área do triângulo (Fórmula de Herão): S = sqrt(p (p-a)(p-b) (p-c))
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 97

A linguagem Python foi anunciada em 1989 por Guido van


5. Rossum, do Instituto de Pesquisa Nacional para Matemática e
Ciência da Computação da Holanda.
Linguagem de É uma linguagem de programação orientada a objetos de
Programação propósito geral, permitindo desenvolver aplicativos em várias
plataformas como console, web e aplicativos de interface gráfica
Python com o usuário.
É uma linguagem de programação pensada para aprendizado de
programação por iniciantes, devido à sua capacidade de prototipagem, facilidade de aprendizado
e de utilização. Atualmente (07/06/2024) sua versão mais atual é a 3.12.8, e pode ser acessada
no link abaixo:
https://www.python.org/downloads/

Atualmente é bastante usada em conjunto com módulos e pacotes que permitem desenvolver
aplicações para inúmeras áreas. Tem sido bastante utilizada por cientistas de dados e para
Inteligência Artificial, além de ferramente importante para aprendizado de conceitos fundamentais
de programação de computadores.

5.1 Elementos Básicos da Linguagem

Um programa escrito em Python deve seguir certas regras de estruturação de seus elementos, de
acordo com os critérios que explicaremos a seguir. Esses critérios formam as regras sintáticas
da linguagem, e devem ser seguidos à risca, para evitar erros de compilação, ou seja, evitar que a
tradução da linguagem de programação para a linguagem de máquina seja feita com problemas.

5.1.1. Forma Geral de uma Classe e de um Programa Principal

class <identificador da classe> :


[declaração dos atributos da classe – método __init__(self)]
[declaração e implementação de métodos e propriedades da classe]

# módulo principal – sugestão de estrutura


import <nome de módulo / pacote / classe>
...
def principal():
# comandos da função principal()

if __name__ == ‘__main__’ :
principal()

Classes devem, preferencialmente, ser digitadas em arquivos próprios e separados. O nome do


arquivo pode ser igual ao identificador usado para nomear a classe.

O nome da classe deve identificar para que ela serve. Por exemplo:

class EquacaoSegundoGrau :

O identificador da classe, no exemplo acima, é formado pelas palavras EquacaoSegundoGrau.


Como vimos anteriormente, quando estudamos declaração de variáveis, um identificador é um
conjunto de letras, dígitos e o caracter “_”, iniciado por uma letra, formando nomes de variáveis,
constantes, comandos e programas.
Em Python, toda linha declaração de classe deve terminar com “:” .
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 98

Para Python um pacote (ou biblioteca), é um conjunto de classes (atributos e métodos) pré-
definidos, que podemos usar em nossos programas quando necessário.

5.1.2. Símbolos da Linguagem

Os símbolos válidos na linguagem Python são aqueles com que se pode escrever comandos,
números, identificadores e mensagens. São os seguintes:
Letras: ‘A’ a ‘Z’, ‘a’ a ‘z’, ‘_’
Dígitos: ‘0’ a ‘9’
Símbolos Especiais: + - * / = ^ < > ( ) [ ] { } . , ; : $ " # ! | @

Os demais símbolos representados no teclado não têm uso na linguagem, podendo, contudo, ser
usados em cadeias de caracteres.

5.1.3. Palavras Reservadas

São identificados pré-definidos e reservados que possuem significado especial para o compilador.
Não podem ser usadas como identificadores em seus programas. Por exemplo, If e wHIle são
identificadores válidos, mas if e while não são, por serem palavras reservadas.
As palavras abaixo são reservadas, ou seja, não podem ser usadas como identificadores; só
podem ser usadas para o objetivo definido para elas na linguagem.
and as assert break
class continue def del
elif else except False
finally for from global
if import in is
lambda None nonlocal not
or pass raise return
True try while with
yield

5.1.4. Informações Gerais sobre padrão de escrita

O interpretador Python diferencia letras maiúsculas e minúsculas, de modo que mesmas palavras
escritas com diferentes letras maiúsculas e minúsculas são consideradas dferentes.
Existem recomendações sobre como definir nomes de identificadores, mas não são obrigatórias.
Em geral, temos as seguintes recomendações (algumas das quais não usamos anteriormente):
Nomes de classes:
- usar CamelCase: primeira letra de cada palavra em maiúsculo, sem separar palavras
com “_”
Equacao1oGrau Calculadora
Nomes de variáveis e parâmetros:
Usar letras minúsculas, separando as palavras com “_”
qtos_funcionarios total_salarios contador1
Nomes de métodos:
Usar letras minúsculas, separando as palavras com “_”
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 99

ler_dados() somar(valor_a_somar) calcular_derivada()


Linhas em Branco:
São importantes para separar funções e definições de classes (duas linhas), bem como
definições de métodos (uma linha)
Espaço em Branco:
Deve ser usado para separar operadores aritméticos, lógicos, relacionais e atribuição.

if contador == 10 :
print 4 * 2 / 0.9

Evitar espaço entre nome de função/método e o “(“ na definição e chamada. Colocar um


espaço após “,” na declaração e passagem de parâmetros:

def metodo(quantos_dados, saldo_atual, nome) :


...
metodo(4, -2.85, “Maria”)
Indentação
Recomenda-se que a indentação de blocos de comandos internos a outro seja feita com
quatro espaços por nível. Alguns editores de código Python (VSCode, PyCharm)
automatizam os recuos, usando, em geral, a tecla [Tab] para efetuar a tabulação que gera
o recuo.
Correto:

def valor1(self):
try:
self.c = input('Primeiro Valor: ')
c = self.c
return c
except:
print 'Inválido!'
self.valor1()

Incorreto:

def valor1(self):
try:
self.c = input('Primeiro Valor: ')
c = self.c
return c
except:
print 'Inválido!'
self.valor1()

5.1.5. Declaração de Variáveis e Tipos de Dados

Toda variável é um identificador; para dar-se um nome a uma variável deve-se seguir as regras
dos identificadores.
Para se declarar variáveis em Python, damos nomes a elas e usamos o comando de atribuição de
valor. O tipo do valor atribuído determinará o tipo dessa variável, ou seja, qual a natureza dos
valores que ela armazenará:
<variável> = <valor/expressão>

a = 42.8
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 100

b = int(input(“Digite o valor desejado:”))


resposta = input(“Deseja parar (S/N)?”) Classe é um tipo, pois
umaEquacao = EquacaoSegundoGrau() modela variáveis

Os tipos de variáveis correspondem ao tipo do valor que cada variável armazena. Em


computação, todas as informações são representadas por uma sequência de bits. Por exemplo,
32 bits em sequência podem ser considerados um valor inteiro. 16 bits são usados para
representar cada caractere individual de uma string.
Para não termos de tratar de quantidade de bits em nossos programas, as linguagens de
programação geralmente usam o conceito de tipos para representar esses agrupamentos. Os
tipos são abstrações que nos permitem tratar um agrupamento de bits de forma específica.
Assim, os principais tipos pré-definidos e mais básicos da linguagem Python são:

Nome do Tipo Natureza da informação armazenada


int valores inteiros
float valores reais (ponto flutuante)
bool valores lógicos
string cadeia de caracteres
complex representam números complexos (raiz quadrada de negativo)

Esses tipos são associados a classes já pré-definidas,


que implementam seus atributos (valores armazenados)
e comportamentos (forma de funcionamento) de cada
tipo.
Podemos usar a janela de console do PyCharm para
ver o tipo de dados, como no exemplo ao lado.
A função type() também pode ser usada em código
programado em classes ou módulos.

5.1.6. Ponto-e-vírgula e comandos

Na linguagem Python, para colocar mais de um comando por linha, termine cada comando com
um “;” e, depois, escreva o próximo comando. O último comando não deve terminar com “;”.
O uso de “;” não é recomendado, no entanto. Sempre procure colocar cada comando em uma
única linha e apenas um comando por linha.

5.1.7. Comando de atribuição de valor a variáveis

O símbolo = é usado para atribuir valores a variáveis em comandos de atribuição de valor.


Exemplos:
x = 40
delta = b * b – 4 * a * c
Deve-se levar em conta se há compatibilidade entre o tipo da
variável receptora e o tipo da expressão atribuída a ela. Por
exemplo, uma variável real pode receber o resultado de uma
expressão inteira, mas o contrário mudará o tipo da variável
receptora, de float para int.
Igualmente, inteiros ou reais, se receberem o valor de uma string,
terão seus tipos alterados.
Uma variável string só pode receber, em atribuições, cadeias de
caracteres delimitados por “ ou ‘. Nunca poderá receber um valor
numérico, pois seu tipo deixará de ser string e passará a ser float ou int, dependendo do valor que
foi atribuído.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 101

5.1.8. Comentários

Comentários são textos escritos no meio de um programa que servem para documentá-lo, ou
seja, prover explicações sobre seu funcionamento a quem ler o texto. São, portanto, ignorados.
Um comentário de linha é definido pelo símbolo #. O que for escrito após # é considerado
comenteário, mesmo que o texto corresponda a um comando válido. Exemplo:

class EquacaoSegundoGrau: # Esta classe calcula uma equacao do segundo grau


def __init__(self):
self._a = 0.0 # coeficiente quadrático
self._b = 0.0 # coeficiente linear

Há uma maneira de fazer uma espécie de comentário de múltiplas linhas. Como python ignorará
cadeias de caracteres não atribuídas a uma variável, você pode adicionar uma string de várias
linhas usando aspas triplas no início e no final, e colocar seu comentário dentro delas. Por
exemplo:

“””
Este é um comentário de várias linhas,
Colocado em uma string.
Pode ser usado para escrevermos comentários mais longos
““”
if __name__ == “__main__” :
inicio()

5.1.9. Operadores aritméticos, relacionais e lógicos

Operadores são símbolos que servem para efetuar operações em expressões aritméticas ou
lógicas. Na linguagem Python os operadores são os seguintes:

Operador Significado Exemplo


Aritméticos
+ Adição de valores numéricos C=A +B
- Subtração C=A–B
* Multiplicação C=A*B
/ Divisão real D = E / F (resultado real)
// Divisão inteira D = E // F (resutaldo inteiro)
% Resto de divisão inteira R=A%B
Atribuição
+= Adição embutida A += B é o mesmo que A = A + B
-= Subtração embutida A -= B é o mesmo que A = A – B
*= Produto embutido A *= B é o mesmo que A = A * B
/= Divisão real embutida A /= B é o mesmo que A = A / B
//= Divisão inteira embutida A //= B é o mesmo que A = A // B
%= Resto embutido A %=B é o mesmo que A = A % B
Relacionais
> Maior que if ( Primeiro_Valor > Outro_Valor ) ...
>= Menor ou igual if ( quantidadeDeLivros >= estoqueBasico ) ...
< Menor que if ( salarioFuncionario < 2000.00 ) ...
<= Menor ou igual if ( valor1 <= valor2 ) ...
== Igual if ( a == b ) ...
!= Diferente while ( A != B ) ...
Lógicos
and E lógico if b > a and x == y : ...
or OU lógico (ou exclusivo ➔ ^) if codigo != valorAnterior or a == b : ...
not NÃO lógico if not umaEquacao.houveErro : ...
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 102

5.1.10 Objetos

Quando instanciamos uma classe, armazenamos o local da memória onde o objeto foi criado e o
colocamos em uma variável. Essa variável possuirá um tipo padrão chamado Object.
A declaração de um objeto é feita abaixo:

<nomeDaVariávelObjeto> = Classe(parâmetros do construtor)

Ao instanciar, chamamos o método construtor da classe, como vemos abaixo:

umaCalc = Calculadora(valor1, valor2)

O tipo da variável objeto será definido, implicitamente, a partir do nome do construtor, que é igual
ao nome da classe. Quando chamamos o construtor da classe, como feito acima, Python chama o
método __init__() definido no código dessa classe.
Você pode codificar um método __str__() em uma classe. Esse método, quando codificado,
retorna uma string que define como os dados da classe devem ser exibidos quando escrevemos
um objeto na tela, usando print().
No programa da equação do primeiro grau, podemos exibir o objeto umaEquacao, sem termos
definido o método __str__() na classe Equacao1oGrau. Codificamos um print(umaEquacao) logo
após instanciar esse objeto. O resultado seria o seguinte, para uma equação com valores 4 e -2:

Agora, se codificarmos __str()__ na


classe Equacao1oGrau, com o código
abaixo, o resultado seria diferente:

Podemos remover o

primeiro print do else, já


que, ao codificarmos um método __str__() nessa classe, delegamos a ela a responsabilidade de
definir como seus dados devem ser exibidos, e já chamamos a sua escrita logo antes de calcular
o valor de x.

Ao lado, temos a execução com valores 0 e 2:


Estudaremos os mecanismos de objetos com maior
detalhamento no futuro.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 103

5.2. Comandos de Desvio Condicional de Fluxo


Estudamos anteriormente que os comandos de desvio de fluxo permitem que a execução do
programa não seja obrigada a seguir o fluxo sequencial natural (padrão) dos comandos. Um
desvio de fluxo permite desviar a execução do comando atual para outro comando que não aquele
imediatamente subsequente. Há dois tipos de desvio de fluxo em Python: condicional e
incondicional.
Os comandos de desvio condicional são o if e o match. O comando de desvio incondicional é o
goto. Este comando não será estudado neste curso, devido ao fato de que, embora seja um
comando válido na linguagem, foi criado em 2004 como fruto de uma brincadeira e seu uso
desenfreado conduz a “vícios” de lógica que tornam a programação complicada, confusa,
ineficiente e desestruturada, além de diminuir a facilidade de leitura, compreensão e manutenção
do programa por outra pessoa que não aquela que o escreveu. goto diminui a qualidade dos
programas.
A seguir, veremos outros detalhes do comando de decisão if e, conheceremos o comando match.

5.2.1. Comando if
O comando if é construído de forma que um comando subordinado ao if depende de uma
condição ser satisfeita para ser executado (ou seja, para que o fluxo de execução passe por ele).
Apenas quando essa condição especificada se verificar (ou seja, for verdadeira) ocorrerá a
execução do comando que depende desta condição. A forma geral do comando if é:

if <condição> : Na descrição da sintaxe de um comando,


itens entre [ e ] indicam uma cláusula
<comando 1>
opcional do comando, ou seja, na forma
[ else : geral ao lado, a parte do else é não-
<comando 2> ] obrigatória.
Assim, se <condição> for verdadeira então <comando 1> será executado e, caso haja a parte com
else, <comando 2> será ignorado. Se <condição> for falsa, <comando 1> será ignorado e, caso
se haja a parte com else, <comando 2> será executado.
Nos dois casos, em seguida o fluxo de execução retorna ao comando seguinte ao If. Por exemplo:
int a = int(input(“Primeiro valor:”))
int b = int(input(“Segundo valor :”))
if a > b :
print(f”{a} > {b}”) # Comando 1
else :
print(f”{a} <= {b}”) # Comando 2

Se, por exemplo, a variável a vale 4 e a variável b vale 5, a condição a > b é falsa, e assim
<comando 1> é ignorado: o fluxo de execução executará o <comando 2>, escrevendo 4 <= 5.
Os comandos 1 ou 2 de um if podem ser outro if. Neste caso, cada else, em geral, se associa a
cada if na ordem inversa ao seu aparecimento, como se fossem parênteses aninhados, ou seja, o
último else corresponde ao primeiro if, o penúltimo else corresponde ao segundo if. Exemplo:
if <condição 1> :
if <condição 2> :
<comando 1.1>
else :
if <condição 3> : <comando 1>
<comando 1.2.1>
else : <comando 1.2>
<comando 1.2.2>

else :
<comando 2>
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 104

<comandos seguintes>

Estude o funcionamento da estrutura de comandos acima:


a) <comando 1> está subordinado ao if-else com a <condição 1>. <comando 1> é outro
comando if-else, que testa uma <condição 2>.
b) Caso <condição 2> seja verdadeira, é executado <comando 1.1>, e o fluxo pula o else
e o quadro verde, e segue para os comandos seguintes, fora de toda essa estrutura.
c) Se <condição 2> é falsa, o fluxo segue para o else // 1 e chega a um novo if-else.
Esse if-else forma o <comando 1.2>, e testa <condição 3>.
d) Se <condição 3> for verdadeira, o fluxo segue para <comando 1.2.1> , e depois pula
o else e segue para os comandos seguintes, fora de toda essa estrutura.
e) Se <condição 3> for falsa, o fluxo vai para o else, executa <comando 1.2.2> e segue
para os comandos seguintes, fora de toda essa estrutura.
f) Caso, no primeiro if, <condição 1> seja falsa, o fluxo pula < comando 1> (quadro azul)
e vai para o else, executa <comando 2> e, após isso, o fluxo segue adiante para os
comandos seguintes.

Caso algum if não tenha a parte com else, poderiamm ocorrer ambiguidades, como no exemplo
abaixo. Mas, emPython, a indentação resolve essa situação. Para que o segundo if tivesse um
else, esse else deveria estar no mesmo nível de indentação que o segundo if.

if <condição 1> :
if <condição 2> :
<comando 1.2>
else:
<comando 3>;

<Condição> deve ser uma expressão lógica, ou seja, deve ser uma expressão que produza um
resultado verdadeiro ou falso.

5.2.2. Comando match


O comando match seleciona um comando para ser executado dentre uma lista de comandos, de
acordo com o conteúdo de uma variável ou expressão, chamada expressão de correspondência.
Sua forma geral é a seguinte
match <expressão de correspondência> :
case <const 1> :
<comandos 1>
case <const 2> :
<comandos 2>
……
case _: opcional
<comandos n>

Caso a <expressão de correspondência> seja igual à constante <const 1>, <comando 1> será
selecionado e executado, e os demais <comandos> serão ignorados. O mesmo se dá com
<comando 2> se a <expressão de correspondência> for igual a <const 2>, e assim por diante. Se
a variável não for igual a nenhuma das constantes, então <comando n> será executado se a
opção com _ tiver sido especificada. <comando> pode ser um bloco de comandos indentados.
Exemplo:

match valor : # notas de Cruzados


case 1 | 2 | 3 | 4 | 5 :
print("Não havia nota com este valor")
case 10 :
print("Rui Barbosa")
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 105

case 50 :
print("Oswaldo Cruz")
case 100 :
print("Juscelino Kubitschek")
case 500 :
print("Villa Lobos")
case 1000 :
print("Machado de Assis")
case 5000 | 10000 :
print("Nunca tive em mãos")
case _ :
print("Não sei qual Nota é!")

A mesma estrutura, escrita com If-elif, seria:

if valor >= 1 and valor <= 5 :


print("Não há Nota com este valor")
elif valor == 10 :
print("Rui Barbosa")
elif valor == 50 :
print("Oswaldo Cruz")
elif valor == 100 :
print("Juscelino Kubitscheck")
elif valor == 500 :
print("Villa Lobos")
elif valor == 1000 :
print("Machado de Assis")
elif valor == 5000 or valor == 10000 :
print("Nunca tive em mãos")
else :
print(“Não sei qual Nota é!")

Em nosso programa pyCalc, podemos substituir a sequência de if-else aninhados por um


comando match, como vemos abaixo:

def Realizar(operacao) : def Realizar(operacao) :


... ...
if operacao == "+": match operacao:
umaCalc.Somar() case "+":
elif operacao == "-": umaCalc.Somar()
umaCalc.Subtrair() case "-":
elif operacao == "/": umaCalc.Subtrair()
umaCalc.Dividir() case "/":
elif operacao == "*": umaCalc.Dividir()
umaCalc.Multiplicar() case "*":
umaCalc.ExibirResultado() umaCalc.Multiplicar()
else: umaCalc.ExibirResultado()
print("Operação imprevista.") else:
print("Operação imprevista.")
Podemos observar que o comando match nos ajuda a escrever menos e torna o código mais
legível.

Modifique o programa pyCalc para que use match e crie um método __str__() na classe
Calculadora para que o programa exiba os dados originais. Teste esse programa passo a passo
para ver o funcionamento do comando match.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 106

5.3. Comandos de Repetição de Fluxo de Execução


São os comandos repetitivos que, na maioria das vezes, permite que um programa realize
operações mais complexas.
Em Python há dois comandos repetitivos, que permitem controlar a repetição de trechos de
código, enquanto for necessário ou enquanto não se tenha atingido a condição para parar essas
repetições.
Cada uma das instâncias da repetição, ou seja, cada repetição individual é também conhecida por
iteração, que é um termo bastante usado em Matemática para representar a repetição de ações.

5.3.1. Repetição com teste antes da execução

O comando while permite repetir um ou mais comandos mediante o teste de uma condição lógica.
Enquanto essa condição resultar em verdadeiro (true), os comandos subordinados ao while são
repetidos.
O teste da condição é feito antes de cada repetição, inclusive antes da primeira repetição.
Cabe aos comandos subordinados ao while fazer com que a condição fique falsa em algum
momento. Caso contrário, ocorrerá loop infinito e a repetição nunca terminará. Em outras
palavras, o controle das repetições não está apenas no próprio comando while e sim, também,
depende dos comandos que ele repete para que a condição de repetição fique falsa em algum
momento, e a repetição pare para que o fluxo prossiga para executar os comandos seguintes ao
while.
A forma geral do comando while é:

while <condição> :
<comandos_subordinados> # executados enquanto a condição for verdadeira

Como já vimos, esse comando nos permite repetir ações enquanto uma condição lógica resultar
em True (verdadeiro). Por exemplo:

print("Este programa informa se números digitados são pares")


resposta = "n"
Trecho de código que
while resposta == "n" :class será repetido enquanto a
valor = int(input("Digite um valor inteiro:")) condição do while for
if valor % 2 == 0 : true.
print(f"{valor} é par!") Quando o usuário digitar
else : um caracter diferente de
print($=f"{valor} é ímpar!") n como resposta a esta
pergunta, a condição
resposta = input("\nDeseja terminar (s/n)?") ficará False e o while
parará.
A cada repetição, a condição tem que se encaminhar para um resultado falso. Caso contrário, a
repetição entrará em loop infinito.
Acesse aqui para ver outro exemplo de while, no site de documentação da Microsoft e poder
editar, testar e visualizar o resultado da execução desse exemplo. Acessando aqui, você verá a
descrição do comando while nesse site de documentação.

Exercícios
5.1. Escreva um programa exiba na tela um seletor de opções, com as seguintes operações:
1 – Equação do 2º grau
2 – Ordenar 3 números inteiros
3 – Leia os coeficientes a1, b1, a2 e b2 de duas equações de reta (y = ax + b) e verifique
se as duas retas se interceptam e em qual ponto, informando os resultados.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 107

4 - Solicite ao usuário o seu peso (em kg) e sua altura (em metros). Em seguida, calcule
o IMC (Índice de Massa Corporal), que é dado pela divisão do peso pelo quadrado da
altura. Após exibir o IMC, o programa deve informar a pessoa sobre a interpretação do
valor calculado, dada pela tabela abaixo:

IMC Interpretação
IMC <=18,5 Abaixo do peso normal
18,5 < IMC <= 25 Peso normal
25 < IMC <= 30 Acima do peso normal
IMC > 30 Obesidade
Cada operação deve ser implementada num método separado.

5.3.2. Comando for - repetição controlada

O comando for permite repetir trechos de código de forma controlada por uma variável que
poderá ser modificada, de acordo com as necessidades do processo repetido. Esse comando
pode ser usado para iterar um bloco de comandos contando as vezes que repete o bloco. Nesse
caso, for possui 4 informações importantes:
1. Valor inicial: podemos informar o valor inicial da variável de contagem;
2. Valor final: enquanto a variável de contagem for menor que o valor final, a repetição
continuará executando;
3. Incremento: informa de quanto em quanto a variável de contagem será incrementada
após cada repetição;
4. Comandos repetidos: os comandos subordinados ao for são repetidos enquanto a
contagem não chegar ao seu final.
Cada uma das três primeiras partes do for é definida na função range():

for variável de contagem in range(valor inicial, valor final, incremento) :


<comandos repetidos> # executados enquanto contagem < valor final

Existe uma outra maneira de usar o comando for, para percurso em listas e vetores, que
aprenderemos posteriormente.

Exemplos:

1. Cálculo de fatorial

Fatorial de um número é o resultado de uma sequência de produtos dos inteiros de 1 até o


número. Por exemplo, fatorial do número 5 = 1 x 2 x 3 x 4 x 5 = 120. Abaixo temos uma função
fatorial() que solicita ao usuário o número cujo fatorial se deseja calcular, lê esse número e
calcula seu fatorial, através de um comando for que repete tantas vezes quanto o número
digitado:

def fatorial():
n = int(input("Digite o valor cujo fatorial deseja calcular:"))
fator = 1
for vez in range(1, n+1, 1) : # vez variará de 1 a n, contando de 1 em 1
proximo = fator * vez
fator = proximo
print(f"O fatorial de {n} é {fator}")
fatorial()
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 108

Após se digitar e ler o valor cujo fatorial deve ser calculado, o comando for passa a ser
executado. Será repetido tantas vezes quanto for o valor lido em n e, a cada repetição, a variável
fator será multiplicada cumulativamente pelo valor da variável vez.
Por exemplo, se N é igual a 7, fator será multiplicada pelos valores 1, 2, 3, 4, 5, 6 e 7 e receberá,
a cada repetição, respectivamente, os valores 1, 2, 6, 24, 120, 720 e 5040, que é o fatorial de 7.
Em seguida, o valor do fatorial é exibido. Estudaremos sobre fatorial e outros métodos numéricos
logo mais.

2. Exibir a tabela ASCII (ver capítulo 9 para informações sobre tabela ASCII)

A tabela ASCII (American Standard Code for Information Interchange) é o conjunto de caracteres
que um computador utiliza.
Ela contém 256 caracteres básicos e cada caractere possui um código, de 0 a 255.

import os

def tabelaASCII() :
os.system('cls') or None
print("\n ", end="")
for codigo in range(0, 16, 1) : # Faz cabeçalho superior
print(f"{codigo:3d}", end=" ")
for codigo in range(32, 255) : Note como não se colocou a
if codigo % 16 == 0 : terceira parte no for abaixo. O
print(f"\n{(codigo):3d} ", end="") incremento da variável de controle
print(f" {chr(codigo)}", end="") fica automático, de 1 em 1.
tecla: str = input("\n\nPressione [Enter]")

if __name__ == '__main__':
tabelaASCII()

Na tabela abaixo, foram exibidos os caracteres de 32 a 255, pois os anteriores não são visíveis na
tela e são usados para atividades internas de um computador. Somando o código da esquerda
com o código superior em cada coluna, temos o código do caractere correspondente à linha e à
coluna. Assim, o código da letra A é 65, e da letra a é 97.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 109

Num print, end=”” indica que não se deve escrever um caractere de final de linha após o que o
print escreve. Assim, não ocorre mudança de linha quando usamos essa declaração.
Crie uma aplicação Python e digite o programa acima, para testá-lo passo a passo e ver o
resultado.

3. Calcular Juros Compostos:

Uma pessoa investe R$1000.00 em uma aplicação que rende juros de 5% ao ano. Assumindo que
todos os juros são deixados no depósito, calcule e exiba a quantia de dinheiro na aplicação ao
final de cada ano, pelos próximos 10 anos. Para determinar essas quantias, use a fórmula:

a = p(1 + r ) n
onde p é a quantia original investida (ou seja, o principal)
r é a taxa anual de juros
n é o número de anos
a é a quantia depositada ao final do n-ésimo ano

import locale

def juros():
locale.setlocale(locale.LC_ALL,'pt_BR.UTF-8')
quantia = 0.0
principal = 1000.0
taxa = 0.05
print("Ano\tQuantidade Depositada")
for ano in range(1, 11):
quantia = principal*pow(1.0 + taxa, ano)
print(f"{ano:2d}\t{locale.currency(quantia, grouping=True)}")

if __name__ == '__main__':
juros()

Não se esqueça de colocar os caracteres \t onde indicados


no comando print().
Ao ser executado, esse programa resulta na tela da figura
ao lado.
Note que usamos o método pow() para calcular potências.
Seu primeiro argumento é a base e o segundo argumento
é o expoente. Portanto, no comando acima calculamos
(1+taxa)ano.
Dentro do for, calculamos a nova quantia a partir da
aplicação dos juros compostos e escrevemos cada linha de
resultados.
Usamos o módulo locale, de localização, para informar que
estamos no Brasil com o comando abaixo:
locale.setlocale(locale.LC_ALL,'pt_BR.UTF-8')
No momento de escrever o valor atual a quantia, usamos o método currency desse módulo para
informar o valor a ser escrito (quantia, primeiro argumento do método, e que desejamos que os
valores sejam agrupados por milhares, com o segundo argumento. Como foi dito que estamos no
Brasil, usou-se “.” Na separação dos milhares e o símbolo da moeda nacional (R$), devido ao uso
das configurações de localização do módulo locale.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 110

Exercícios
Baseados na apostila de C# da Caelum
Referência: https://www.caelum.com.br/apostila-csharp-orientacao-objetos/

5.2. Qual é o valor exibido no seguinte código:

total = 2
for i in range(5) :
total = total * 2
print(f"O total é: {total}")
a) 256
b) 64
c) 128
d) 512

5.3. Faça um programa em Python que imprima todos os múltiplos de N, entre um valor inicial e
um valor final.
Para saber se um número é múltiplo de N, você pode fazer if (numero % n == 0).
Os valores inteiros N, valor inicial e valor final devem ser lidos pelo teclado.
5.4. Escreva um programa em Python que some todos os números de 1 a 100, pulando aqueles
que forem múltiplos de 3. Qual o resultado?
5.5. Escreva um programa em Python que exiba todos os números que são divisíveis por 3 ou por
4 entre 0 e 30.
5.6. Faça um programa em Python que exiba os fatoriais dos inteiros de 0 a 12.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 111

5.4. Erros de Programação Frequentes e Como Evitá-los


Erros na prova A - 2007
14.
Modularização/Coesão/Sequ
10. Interface com o usuário ência; 2,8%
(E/S); 12,6%
1. Sintaxe; 18,3%

9. Ocorrências de Loops;
1,0%

8. Cálculos, Somas,
Produtos; 9,4%

2. Tipos de Variáveis; 11,8%

7. Blocos Início-Fim; 3,7%

6. Condições de Desvios;
8,9%

5. Condições de Repetições;
2,4% 3. Inicialização de Variáveis;
26,6%

4. Contagem em Repetições;
2,6%

Em primeiro lugar, é importante conhecer as regras sintáticas da linguagem de programação que


se está usando.
As fontes mais comuns de erros são:
• esquecimento de iniciar variáveis antes de repetição (d3)
• esquecimento de incrementar variáveis de contagem, gerando loop (d4 e d9)
• esquecimento de indentação (d7) – pode gerar loop, caso ocorra logo após um while, pois
haveria apenas um comando subordinado ao while. Se esse comando subordinado não levar
ao fim da repetição, ocorre loop (d9).
• Esquecer de exibir (escrever) os resultados
Além de esquecimento, muitas vezes esses erros ocorrem por falta de atenção a certos detalhes
simples. Sempre antes de uma repetição, por exemplo, deve-se inicializar todas as variáveis que
serão usadas dentro dela. |
Com relação aos erros de sintaxe (d1) os mais comuns foram:
• Falta de aspas delimitando cadeias de caracteres.
• Falta da indentação adequada de comandos
Resumindo

Não esqueça de:


1. Colocar os dois-pontos (:) após comandos de declaração, condição de if e while, e também
depois de else;
2. Todo valor constante atribuído a uma cadeia de caracteres ou comparado com uma, deve ser
colocado entre apóstrofes ou aspas
3. Toda repetição feita com while deve ter um comando interno que encaminhe o fluxo de execu-
ção para a finalização da repetição; geralmente, esse comando interno é, na maioria das
vezes, uma contagem ou uma leitura de valores de arquivo
4. Confira as indentações, verificando com muito cuidado os comandos while e for.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 112

6.1. Variáveis Compostas de Tipo Homogêneo


6.
Strings É possível, na maioria das linguagens de programação, declarar e usar
variáveis que se compõem de vários elementos em sequência, cada um
armazenando um dado, geralmente com todos eles tendo o mesmo tipo de
informação. Assim, uma única variável pode conter vários valores e cada um
pode ser acessado individualmente, por uma operação chamada indexação.
Um exemplo de variáveis como essa são as strings, que já usamos. São consideradas variáveis
compostas porque há vários valores armazenados na mesma variável, e homogêneas porque têm
o mesmo tipo de dados, já que cada elemento individual de uma string é um caracter.
Um objeto instanciado a partir do modelo de uma classe pode ser considerado uma variável
composta, pois pode agrupar vários atributos. Mas não são homogêneas, porque cada atributo
pode ter um tipo de dado diferente dos demais.
Neste capítulo estudaremos as strings com maior profundidade.

6.2. Cadeias de Caracteres


Uma string ou cadeia de caracteres é uma variável formada por uma sequência de vários
caracteres, que formam, em conjunto, uma informação. Ao invés de se declarar várias variáveis
para armazenar cada caracter individualmente, usamos uma única variável string que armazena
todos os caracteres que constam da informação e podemos trabalhar com eles individual ou
coletivamente. São exemplos de strings:
"String" "Teste de CADEIA; ! #$ = abc "
" " ‘ ‘ "123"
"0" ‘0’ ‘ Pedro Gonçalves ’

Os exemplos acima referem-se a strings constantes, ou seja, que são fixas, incluídas no texto do
programa fonte, como por exemplo, num método print() ou em comandos de atribuição de valor.
Para declarar variáveis desse tipo atribuímos uma sequência de caracteres delimitada por “ ou ‘:

cadeia = "TESTE 1"


nome = 'Albertina Martins'

Uma variável string é definida da maneira mostrada acima. Cada caracter ocupará, na memória,
dois bytes e serão armazenados em sequência, como vemos no modelo de memória do abaixo:

0 1 2 3 4 5 6
cadeia T E S T E 1

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
nome A l b e r t i n a M a r t i n s

Vemos que espaços em branco ocupam posições na memória e que as sequências de caracteres
que atribuímos a uma variável são desmembradas em caracteres individuais e cada um deles é
armazenado em uma posição específica da variável composta.
Cada posição é identificada por um índice, que
é o número inteiro que vemos acima de cada
caracter. Esse índice sempre começa a ser
contado a partir de zero.
Ao lado vemos a exibição da primeira e
segunda letras da string nome, acessadas
através dos índices 0 e 1, respectivamente,
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 113

entre [ e ]:
De certa maneira, uma string é como uma rua com casas todas do mesmo tipo, cada casa tendo
seu próprio número e um morador individualmente diferente dos demais. Quando precisamos
conversar com um dos moradores, basta saber o número da casa em que ele mora para nos
dirigirmos até ela e acessar seu morador, mesmo que as casas tenham o mesmo formato, o
mesmo tipo:

O tamanho máximo permitido de uma string em Python dependerá da capacidade de memória e


do tipo de processador do seu computador. Em uma instalação de 64 bits e 64 Gb de memória
RAm, strings com até 63 gigabytes (63 bilhões de caracteres) podem ser usadas, mas isso
degradará o desempenho do computador. Em geral, strings com 2 a 3 gigabytes atendem a
grande maioria das necessidades e podem ser acessíveis em computadores com processador de
32 bits e 4 gibabytes de memória RAM.
O que se pode então fazer com uma variável desse tipo? Podemos armazenar nomes de
pessoas, endereços, palavras, em resumo, quaisquer dados alfanuméricos, ou seja, cadeias com
letras, algarismos (dígitos) e caracteres especiais. Podemos processar esses caracteres,
aplicando algum algoritmo sobre eles e chegar a resultados e soluções de certos problemas. Por
exemplo, podemos classificar os caracteres, encontrar palavras, trocar caracteres, criptografar a
string, dentre outros.

6.3. Operações básicas com Strings


a. Len() - comprimento de string
Para sabermos quantos caracteres uma string contém, usamos a função len() aplicada na string.
len é uma abreviação da palavra length que significa comprimento, em inglês. Por exemplo:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
nome A l b e r t i n a M a r t i n s

Observe que, no modelo de memória da string nome, os índices variam de 0 a 16, e que
len(nome) vale 17. Isso ocorre porque, como os índices começam a ser contados a partir de zero,
o último índice é igual ao comprimento da string menos um.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 114

Assim, caso tentemos acessar o


caracter na posição que corresponde
a len(string), teremos um erro de
indexação inválida, como podemos
ver na figura ao lado:
Nesse código, o primeiro comando
tenta acessar a posição quantos da
string nome. A variável quantos vale
17 e, dessa forma, o comando tenta
armazenar na variável
ultimoCaracter o valor de nome[17],
índice inexistente nessa string.
Já o código ao lado está correto, pois
quantos-1 vale 16 e, assim, estamos
acessando um índice que existe na
string nome:
Esse índice contém a letra “s”, que foi
exibida pelo comando print().
O conteúdo de uma cadeia de caracteres não pode ser alterado depois que o objeto é criado,
embora os comandos acima pareçam permitir isso. No entanto, sempre que você concatena
(junta) uma string a outra, ou atribui uma nova sequência de caracteres a uma mesma variável
string, o gerenciador de memória do sistema operacional do seu computador na verdade cria um
novo objeto de cadeia de caracteres para manter a nova sequência de caracteres e esse novo
objeto é atribuído à variável. O espaço de memória usado pela cadeia de caracteres anterior é
então liberado para futuro reaproveitamente pelo gerenciador de memória do Sistema Operacional
do computador.
Podemos ver esse processo acontecendo com os comandos ao
lado. Mostramos o resultado da chamada à função id(), que
retorna o endereço de memória em que uma variável está
localizada. No caso, como usamos nome como parâmetro de id(),
essa função retorna o endereço de memória onde a variável
nome se localiza.
Observe como o resultado de id(nome) muda a cada vez que
atribuímos um novo valor à string nome, mostrando que uma
nova variável é instanciada a cada mudança em nome:

Pelo fato de strings serem


imutáveis em Python, nessa
linguagem não podemos
modificar um único caracter
através de atribuição de um
novo caracter a uma
posição indexada da string,
como vemos ao lado:

b. Atribuição

cadeia1 = cadeia2 # cadeia2 é outra string, anteriormente atribuída

O comando acima atribui à string cadeia1 uma cópia do conteúdo da string cadeia2.

caracterDigitado = 'A'
cadeia = caracterDigitado
outroCaracter = cadeia[0]
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 115

As variáveis caracterDigitado, cadeia e outroCaracter são strings.

c. Leitura pelo teclado

cadeia = input(“Digite uma frase”)


O comando anterior lerá do teclado tantos caracteres quantos forem digitados até se encontrar um
fim de linha (Enter). Esses caracteres formarão uma string e serão armazenados na variável
cadeia, que deverá, portanto, será considerada como sendo do tipo primitivo str de Python.

d. Escrita no Console

print(cadeia)

Exibe na tela todos os caracteres armazenados na string cadeia.

e. Concatenação

cadeia3 = cadeia1 + cadeia2

Concatenar significa juntar duas strings, gerando uma terceira string contendo as cadeias
presentes nas duas cadeias originais.
Para isso, se usa o operador + que, aplicado a strings, produz a concatenação das mesmas na
ordem em que forem especificadas, ou seja, gera uma só string formada pelas cadeias
concatenadas.
Lembre-se que isso destrói o que já poderia estar armazenado em cadeia3 e uma nova string é
criada na memória com o conteúdo de cadeia1 seguido do conteúdo de cadeia2.
O processo de destruir (liberar para reaproveitamento do gerenciador de memória) e instanciar
uma nova string toma tempo do processador e acaba deixando a memória com trechos
fragmentados.
O trecho abaixo cria duas strings e as concatena, gerando uma terceira string que é passada
como parâmetro para o método print_hi que, por sua vez, exibe uma mensagem na tela contendo
a string:

def print_hi(name):
print(f'Hi, {name}')

if __name__ == '__main__':
nome= "Luis"
sobrenome = "Camargo"
nomeCompleto = nome+" de "+sobrenome
print_hi(nomeCompleto)

Quando a string resultante é a mesma que está sendo concatenada, podemos usar o operador +=,
como vemos no código abaixo:

nomeCompleto += " de Barros"


print_hi(nomeCompleto)

f. Limpar uma string

Para limpar uma variável string, ou seja, fazer com que ela não contenha nenhum caracter, basta
atribuir-se-lhe uma cadeia nula (vazia) com o seguinte comando:

cadeia = "" # sem espaço entre as aspas


COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 116

g. Comparação de strings

A comparação de strings é usada para definir se duas strings são iguais, diferentes ou a ordem
em que se encontram, levando em conta a disposição dos caracteres no conjunto de letras,
dígitos e caracteres especiais em uso pelo computador (tabela ASCII, por exemplo).
Para comparar strings, usamos os operadores relacionais, como ==, !=, >,<, >=, <= e in. Abaixo
temos um exemplo:

nome= "Luis"
sobrenome = "Camargo"
nomeCompleto = nome+" de "+sobrenome

if nome >= sobrenome:


print(f"{nome} vem depois de {sobrenome}, alfabeticamente")
else:
print("alfabeticamente, {sobrenome} vem depois de {nome}")

letra = input("Digite uma letra qualquer: ")


if letra in "AEIOUaeiou": # in verifica se um string pertence a outra
print(f"{letra} é vogal")
else:
print(f"{letra} não é vogal.")

h. Método __str__() de uma classe

Todas as classes em Python possuem esse método. Isso ocorre devido ao mecanismo de
herança. Esse mecanismo faz com que todos as classes que criarmos ou usarmos sejam criadas
a partir de uma classe base (ou ancestral), chamada Object, e que sempre servirá de modelo
para todas as outras classes que forem codificadas. Esse uso de um modelo ancestral é chamado
de herança. Portanto, todo objeto possui um método __str__() que procura transformar o conteúdo
do objeto em uma string, para poder ser exibido na tela ou atribuído a uma string, como fizemos
acima.
Quando se exibe um objeto de
uma classe que não teve esse
método codificado explicitamen-
te, é usado o método padrão da
classe ancestral Object.
Nesse caso, é exibido o no-
me da classe do objeto e
seu endereço na memória.
Vemos isso na figura ao
lado:

Para que o conteúdo dos atributos do objeto Equacao1oGrau seja exibido, teríamos de substituir
o método padrão __str__() (herdado da classe base Object) por um outro que retorne uma string
com o valor dos atributos dessas classes. Essa substituição se chama tecnicamente de
sobreposição (override). Por exemplo:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 117

class Equacao1oGrau :
def __init__(self, coeficiente, termo):
self._a = coeficiente
self._b = termo
self._houveErro = False
def __str__(self):
return f"Equação {self._a:5.1f}x + {self._b:5.1f} = 0"

O resultado dessa codificação é visto ao lado:

6.4. Percurso e processamento de strings

Como já estudamos, podemos acessar cada caracter individual de uma string por meio de índices
e um índice é uma expressão (ou variável) inteira cujo resultado (ou conteúdo) indica o número da
posição desejada.
O índice da primeira das posições individuais dos caracteres de uma string é sempre 0. Assim,
uma string com 7 caracteres terá índices de 0 a 6.
Se ind é uma variável inteira usada como índice, temos que o trecho de comandos abaixo exibirá
na tela os caracteres ‘S’ e ‘3’ e pula de linha, ou seja, a saída será S3.

0 1 2 3 4 5 6
cadeia T E S T E 3

cadeia = "TESTE 3"


ind = 2
print( cadeia[ind] + cadeia[6] ) → S3

Várias operações computacionais consistem em processar os caracteres de uma string, um a um,


aplicando algum algoritmo de tratamento a cada um deles. Em geral, esse processamento se
inicia no primeiro caracter, ou seja, no início da string, e continua até o último índice usado, ou
seja, no final da string. Esse processamento é repetitivo, de forma que o mesmo tratamento é
aplicado a cada caracter, geralmente, e é conhecido como percurso da string.
Como se trata de um processo repetitivo, o percurso é realizado, geralmente, com um comando
while e índices que mudam de valor a cada repetição, de maneira que uma nova posição da string
é acessada (através do índice) e processada. No caso do comando abaixo, a string cadeia será
escrita ao contrário:

indice = len(cadeia) - 1
while indice >= 0:
print(cadeia[indice], end="")
indice = indice – 1

A saída será 3 ETSET. Como se viu, um índice de string deve ser um valor inteiro entre colchetes,
[ e ]. O índice não pode ser maior que o tamanho máximo da string, definido durante a atribuição
de conteúdo à mesma.
Assim, pode-se acessar a string como um todo, ou cada um dos seus caracteres aleatoriamente.
Para isso, basta usar índices e fazer o conteúdo dos mesmos referenciar as posições desejadas.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 118

Podemos processar uma string caracter a caracter para classificar cada caracter. O código abaixo
faz isso. Note o uso de funções isalpha() e isdigit():

frase = input("Digite uma frase: ")


indice = 0
while indice < len(frase):
if frase[indice].isalpha() : # é uma letra
if frase[indice] in "AEIOUaeiou" :
print(f"{frase[indice]}: vogal")
else:
print(f"{frase[indice]}: consoante")
elif frase[indice].isdigit() :
print(f"{frase[indice]}: dígito")
else:
print(f"{frase[indice]}: caracter especial")
indice = indice + 1

Somamos 1 ao valor do índice ao final do bloco de comandos


do while. Somar 1 a uma variável é uma forma de contagem, e
é também conhecida por incremento. Ele permite variar o índice, o que muda a posição da
string que está sendo processada a cada passo do percurso, ou seja, a cada repetição do while.
Sem isso, o while entraria em loop.
Abaixo fazemos a simulação do funcionamento desse código, para estudarmos como o comando
de incremento nos leva a uma nova posição da string, a cada repetição do while:

frase T e m o s 1 0 c a r n e i r o s
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

indice frase[indice] Resultado indice = indice + 1


0 “T” consoante 1
1 “e” vogal 2
2 “m” consoante 3
3 “o” vogal 4
4 “s” consoante 5
5 ““ especial 6
6 “1” dígito 7
7 “0” dígito 8
8 ““ especial 9
9 “c” consoante 10
10 “a” vogal 11
11 “r” consoante 12
12 “n” consoante 13
13 “e” vogal 14
14 “i” vogal 15
15 “r” consoante 16
16 “o” vogal 17
17 “s” consoante 18
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 119

Outra possibilidade de percorrer e processar uma string é por meio do comando for:

frase = input("Digite outra frase: ")


for caracter in frase:
if caracter.isalpha() : # é uma letra
if caracter in "AEIOUaeiou" :
print(f"{caracter}: vogal")
else:
print(f"{caracter}: consoante")
elif caracter.isdigit() :
print(f"{caracter}: dígito")
else:
print(f"{caracter}: caracter especial")

A cada repetição do for, o próximo símbolo da string é


acessado e atribuído à variável caracter. Em seguida,
tratamos esse caracter e o processo se repete, continuando
até que todos os símbolos tenham sido acessados, na
sequência em que estão dispostos na string.

Particionamento de strings

Uma operação bastante útil que pode ser feita durante o processamento de uma string, é copiar
uma parte da mesma para tratamento e análise. Essa cópia, em Python, é chamada de
particionamento ou fatiamento de string, e é operada com dois valores inteiros entre [ e ]. Um
particionamento define uma posição (índice) inicial e uma posição final que delimita os caracteres
que serão copiados:

string[inicio:fim] → copia o trecho da string desde o índice “inicio” até antes do índice “fim”

O trecho de código a seguir percorre uma string contendo um nome completo de uma pessoa e
descobre a primeira ocorrência do caractere de espaço nessa string. Em seguida, faz o fatiamento
da string desde o primeiro caractere até o caraftere antes desse espaço em branco. Assim, gera
uma cópia do trecho da string que contém a primeira palavra do nome de uma pessoa e a
escreve.

nomeCompleto = input("Digite seu nome completo: ")


if ' ' in nomeCompleto: # há pelo menos um espaço em branco
indice = 0
while nomeCompleto[indice] != ' ':
indice = indice + 1
prenome = nomeCompleto[0:indice]
print(f"{prenome} é a primeira palavra desse nome.")
else:
print("Não foi encontrado um espaço separando palavras")

A seguir vemos também a execução passo a passo e os valores das variáveis a cada repetição do
while e variação do índice:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 120
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 121

Observe o uso da variável índice no fatiamento da string. Como começamos a contar de 0, o valor
de índice informa quantos caracteres já percorremos. Portanto, no comando abaixo, a expressão
0:indice significa fatie desde a posição 0 até a posição anterior a “indice” e armazene na variável
prenome:
prenome = nomeCompleto[0:indice]

Posição inicial A partir da posição inicial, quantos


do fatiamento caracteres serão copiados em
prenome

Podemos usar parcialmente os delimitadores de fatiamento. Por exemplo:

nomeCompleto[:indice] → inicia fatiamento na posição 0 e copia até índice-1


nomeCompleto[indice:] → inicia fatiamento na posição índice e copia até final da string

Se inicio e fim tem o mesmo valor, o resultado do fatiamento será uma string vazia (“”), ou seja,
com tamanho zero.

Indexação invertida

Python possui uma característica bastante peculiar dentre as linguagens de programação, que
podemos chamar de indexação invertida. Se você indexar uma string com um índice negativo, o
acesso será feito a partir da última posição da string. Assim, se você indexar uma string com -1,
estará acessando o último caracter. -2 acessa o penúltimo, -3 acessa o antepenúltimo e assim
sucessivamente. Por exemplo:

frase = input("Digite outra frase: ")


indice = -1
while indice >= -len(frase) :
print(frase[indice], end=””)
indice = indice - 1
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 122

6.5. Métodos de Strings


Em Python, strings são consideradas instâncias de objetos da classe String. Dessa maneira,
possuem diversos métodos, alguns dos quais estudaremos a seguir.

find()- retorna índice de um texto dentro de uma string

Esse método devolve como resultado a posição inicial onde um texto ocorre dentro de uma string.
Essa posição inicial é baseada em 0, como ocorre em toda string. Caso não encontre a subcadeia
dentro da cadeia que chama o método, retorna o valor -1. Passamos o texto procurado como
parâmetro, como vemos no exemplo abaixo:

Opcionalmente, find() pode ter um segundo argumento, que informa a posição da string onde deve
iniciar a busca pela cadeia procurada. Por exemplo:

strip() – remove espaços iniciais e finais de uma string

Esse método remove espaços em branco, tabulações e caracteres de nova linha, que possam
existir no início e no final de uma string. Preserva os espaços internos. Por exemplo:

lstrip() – remove somente os espaços iniciais de uma string

Esse método remove os espaços, tabulações e caracteres de nova linha à esquerda do primeiro
caracter diferente de espaço da string. Preserva os espaços internos. Por exemplo:

rstrip() – remove somente os espaços finais de uma string

Esse método remove os espaços, tabulações e caracteres de nova linha à direita do último
caracter diferente de espaço da string. Preserva os espaços internos. Por exemplo:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 123

É possível remover os espaços iniciais e finais de uma string através do uso em sequência dos
métodos lstrip() e rstrip(), de forma que o resultado do primeiro método é usado para remover
espaços com o segundo método:

No exemplo abaixo, substituímos a palavra Maria por Marcela:

replace() – substitui uma subcadeia de uma string por outra subcadeia

O método replace() recebe como parâmetro dois valores: o primeiro é a cadeia que se deseja
procurar e substituir na string que o chama, e o segundo é a cadeia substituta. Todas as
ocorrências da primeira cadeia serão substituídas pela segunda cadeia. Por exemplo:

Nesse exemplo, os espaços duplos internos foram substituídos por um único espaço. Isso ocorre
progressivamente, de forma que, se houver três espaços em seguida, uma dupla será substituída
por um espaço e sobrarão dois espaços. Esses, em seguida, serão novamente substituídos por
um único espaço.

ljust() – completa string com um caracter à direita até chegar ao tamanho indicado

Esse método recebe dois argumentos: o primeiro é um tamanho máximo da string resultante, e o
segundo é um caracter qualquer. O método retorna uma nova string, preenchida à direita com o
caracter fornecido, até que o tamanho da string atinja o tamanho máximo fornecido. Por exemplo:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 124

rjust() – completa string com um caracter à esquerda até chegar ao tamanho indicado

Esse método recebe dois argumentos: o primeiro é um tamanho máximo da string resultante, e o
segundo é um caracter qualquer. O método retorna uma nova string, preenchida à esquerda com
o caracter fornecido, até que o tamanho da string atinja o tamanho máximo fornecido. Por
exemplo:

lower() – converte todas as letras maiúsculas da string em minúsculas

Esse método substituirá as letras maiúsculas de uma string pelas letras minúsculas
correspondentes. Letras minúsculas, dígitos e símbolos especiais não são afetados. Por exemplo:

upper() – converte todas as letras minúsculas da string em maiúsculas

Esse método substituirá as letras minúsculas de uma string pelas letras maiúsculas
correspondentes. Letras maiúsculas, dígitos e símbolos especiais não são afetados. Por exemplo:

swapcase() – inverte letras maiúsculas para minúsculas e vice-versa

Esse método substituirá as letras maiúsculas de uma string pelas letras minúsculas
correspondentes e as minúsculas pelas maiúsculas correspondentes. Dígitos e símbolos especiais
não são afetados. Por exemplo:

split() – separar uma string em subcadeias baseada num caracter de separação

Esse método “quebra” uma string em diversas strings, com base em um caractere de separação
que é fornecido para o método através de um argumento. Sempre que o caracter de separação
aparecer na string, os caracteres anteriores são separados e colocados em uma lista de strings
(estudaremos mais sobre listas posteriormente). Por exempl9o:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 125

Outros métodos para estudo:


Acesse os links https://note.nkmk.me/en/python-split-rsplit-splitlines-re/ e https://docs.python.org/pt-
br/3/library/stdtypes.html#string-methods e estude os métodos descritos com maior detalhamento,
além de outros que possa considerar interessantes.
Inserir uma string dentro de outra
Não há um método para isso, mas podemos usar o fatiamento de string junto com a concatenação
para essa tarefa. No código abaixo inserimos uma string a partir da posição 6 da string original:
cadeia = "Maria tinha um carneirinho"
cadeia = cadeia[:6]+"Amélia "+cadeia[6:]
print(cadeia) # escreve: "Maria Amélia tinha um carneirinho"

6.6. Formatação de Strings


Clique aqui papara conhecer o método format() da classe string, e maneiras de formatar dados.

Exercícios
Crie um novo projeto Python, com módulo principal main.py, e codifique o que se pede a seguir:

6.1. Codifique uma classe Cadeia, que terá um atributo texto que armazenará uma string
passada por parâmetro ao seu construtor e implementará os métodos descritos a seguir:
inverterEmMaisculo() – retorna uma string com o conteúdo do atributo texto, escrito ao
contrário e com todas as letras em maiúsculo. Não alterar o conteúdo do atributo.
palindromo() – retorna True se o conteúdo do atributo texto é espelhado, ou seja, lido da
esquerda para a direita reproduz o mesmo conteúdo que lido da direita para a esquerda.
Retorna False caso contrário.
tirarImpares() – retorna uma string contendo as palavras do atributo texto, menos as que
tenham tamanho ímpar. Use o caracter “ “ (espaço) para separar as palavras de texto com o
método split(). Não alterar o conteúdo do atributo texto.
tirarVogais() – retorna uma string com o conteúdo do atributo texto, menos as vogais, tanto
maiúsculas quanto minúsculas que estejam nesse atributo. Não alterar o conteúdo do
atributo.
6.2. No módulo principal, importe a classe Cadeia e codifique um seletor de opções repetitivo
que implemente as seguintes operações, usando objetos da classe Cadeia:
0. Terminar a execução do programa
1. Ler um texto qualquer e mostra-lo na tela ao contrário e em maiúsculo
2. Ler um texto qualquer e informar se é ou não palíndromo
3. Ler um texto qualquer e mostra-lo na tela sem as palavras de tamanho ímpar
4. Ler um texto qualquer e mostra-lo na tela sem as vogais
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 126

7.1. Contagem e o Cálculo do Sucessor


7.
Uma das operações mais realizadas por um computador é a
Acumulação de contagem. Usa-se contagem em várias ocasiões, como
Valores calcular o número de elementos de um conjunto de dados,
contar a ocorrência de valores de um mesmo tipo, censo
populacional, dentre outros exemplos que vemos no dia a dia.
Literalmente, a contagem consiste em “contar”. Geralmente conta-se de um em um, como
fazemos com os dedos ao aprender a contar. Quando você conta com sua mão, o valor atual da
contagem é, geralmente, um a mais do que o valor anterior, correto? É da mesma maneira que o
computador conta. A sua mão, de certa maneira, funciona como uma memória que guarda o valor
atual da contagem e permite guardar o próximo valor da contagem. A contagem acontece numa
sequência repetitiva, ou seja, você vai contando até que atinge um determinado valor final.
No entanto, um computador não possui mãos para fazer contagens. Ele sabe realizar apenas
operações aritméticas básicas e possui uma memória. É com essas características que vamos
desenvolver um algoritmo de contagem.
A contagem é baseada na busca do sucessor de um número inteiro. O sucessor de um número
inteiro é dado pela adição de uma unidade a esse número, numa operação chamada
incremento.
Se você incrementar um número inteiro i sucessivas vezes, gerará a sequência de números
inteiros i, i+1, i+2, i+3, ..., i+n, onde n é a quantidade de números gerados na sequência.
A variável i na sequência acima é chamada de contador. O valor inicial de um contador pode ser
qualquer valor inteiro, mas, em geral, se usam os valores 0 ou 1, dependendo do que se deseja
contar.
Por exemplo, se o contador i tem valor inicial 0, o sucessor de i será 0 + 1 = 1. O sucessor de 1
é 1 +1 = 2. O sucessor de 2 é 2 +1 = 3.
Podemos sistematizar a geração dos valores da sequência de inteiros da seguinte maneira:

Valor inicial i=0


Na tabela ao lado, vemos a geração da sequência de
i Sucessor Novo i valores i, onde i começa com 0 e vai sendo modificado a
0 i+1=0+1 i=1 cada nova iteração, através do incremento de uma
1 i+1=1+1 i=2 unidade ao seu valor.
2 i+1=2+1 i=3
3 i+1=3+1 i=4 Note que que o novo valor de i é, a cada iteração, o seu
... i + 1 ... ... sucessor, ou seja, i passa a valer o resultado de i + 1.
n-1 (n-1) + 1 i=n Em programação, podemos usar o comando i = i +1 para
n i+1=n+1 i=n+1 realizar o cálculo do sucessor do valor i.

Como você pode deduzir, a operação de contagem é uma operação realizada dentro de um
processo iterativo, ou seja, dentro de uma repetição de operações.
Podemos, portanto, usar contagem para várias aplicações como, por exemplo, contar a
quantidade de ocorrências de uma letra específica em um arquivo texto ou escrever na tela do
computador os valores inteiros de 1 a 10, um a um, como desenvolveremos a seguir.
Em primeiro lugar, lembre-se de que toda contagem tem um início. Vamos supor que o valor
inicial da nossa contagem é zero, como ocorre quando vamos começar a contar com as mãos e
ainda não temos nenhum dedo utilizado.
Como anteriormente comparamos a mão a uma memória, iremos, em nosso algoritmo, utilizar
uma variável, que fica na memória, para representar a mão e guardar os valores da contagem.
Abaixo temos um primeiro esboço do algoritmo de contagem:

def contar():
contador = 0 # variável que armazenará o valor atual da contagem
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 127

... # comandos seguintes a serem codificados aqui

if __name__ == '__main__':
contar()

No método contar() declaramos a variável contador para que ela simule a mão. Atribuímos 0 a
ela, para que ela seja iniciada com um valor que não “polua” as contagens que virão em seguida.
De certa forma, é como sua mão no início da contagem, quando nenhum dos dedos foi utilizado.
Vamos supor que nosso objetivo é contar de 1 a 10, e escrever esses valores na tela do computa-
dor. Vamos ter que gerar cada um dos números inteiros nesse intervalo e, para isso, devemos
pensar numa maneira de como fazer a variável contador passar a valer 1. Observe que ela já
está valendo zero. Lembre-se, agora, da contagem manual: para contar o próximo valor, você
utiliza sempre um dedo a mais do que tinha usado anteriormente, certo? É como se a sua mão
passasse a ter um dedo a mais do que tinha antes.
Como a variável contador faz o papel da mão nesta discussão, devemos encontrar uma maneira
de fazer com que contador fique com (receba) uma unidade a mais do que tinha antes, para
gerar cada novo número da contagem. Isso é realizado pelo comando abaixo:

contador = contador + 1

Imagine que contador valha 0 inicialmente. No comando acima, a variável contador receberá o
valor da expressão aritmética que fica à direita do comando =.
Essa expressão tem por resultado o valor atual de contador, somado com 1. Portanto, se
contador vale 0, contador + 1 vale 0 + 1, que resulta em 1. Seria algo como na figura abaixo:

contador = 0; contador 0
...

contador = contador + 1 contador +1


0

contador 1

Portanto, quando o comando contador = contador + 1 é executado, o valor atual de contador é


recuperado na memória, soma-se um a ele e o resultado final é guardado na própria variável
contador que, na figura acima, de 0 passou a valer 1.
Chamaremos essa operação de incremento ou contagem. A expressão + 1 é o passo, que
indica que se deve somar 1 para se chegar ao novo valor do contador.
Se o comando de contagem for executado várias vezes, de maneira repetitiva, a cada repetição o
valor de contador irá sendo incrementado em um, ou seja, passará a valer 2, depois 3, depois 4,
e assim sucessivamente, até que se chegue ao último valor desejado para a contagem. Para fazer
essa repetição, precisaremos de um comando de repetição while, como vemos abaixo:

def contar():
contador = 0
while contador < 10 :
contador = contador + 1
 # aqui entram os comandos que usam o valor gerado na contagem

if __name__ == '__main__':
contar()
Em algumas situações, o passo pode ser diferente de 1 como, por exemplo, incrementar a soma
de 2 em 2 unidades e, nesse caso, o comando de contagem seria “contador = contador + 2”.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 128

Como nossa intenção é exibir os valores entre 1 e 10, no lugar onde vemos o símbolo ,
devemos colocar o comando que exibe os valores. contador contém o valor recém-gerado da
contagem e, portanto, deve ser exibido. O programa, em sua versão final, seria algo como abaixo:

Entre no VSCode, crie um novo projeto na


pasta \Tecnico\1oSemestre\TecPro\cap07\,
chame-o de pyContagem1 e digite o
programa ao lado.
Coloque um breakpoint na chamada à
função contar().
Em seguida, execute esse programa passo a
passo pressionando o botão de depuração e
a tecla [F11] para o fluxo de execução
avançar pelos comandos.
Na janela Debugger adicione uma inspeção
(botão +) e inclua a variável contador
Arrume a disposição e as dimensões das janelas para deixar visível a janela console.

Execute passo a passo o programa, usando a tecla [F11] e atente para o fluxo de execução
repetitivo, bem como para as várias mudanças de valor da variável contador. Note também como
a tela vai apresentando os valores exibidos pelo programa. Abaixo vemos o aspecto da depuração
logo após o fluxo de execução ter acabado de passar pela chamada do método contar() e nele
entrar:

valor da
variável
exibição
na tela

A variável contador receberá 0 quando [F11] for pressionada e o fluxo de execução se dirigirá
para o comando while.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 129

Pressionando [F11] novamente, a condição “contador < 10” do comando de decisão if será
avaliada. Como contador vale 0, e 0 é menor que 10, essa condição resultará em True e o fluxo
de execução se dirige para os comandos subordinados ao while, como vemos abaixo:

Pressionamos [F11] e o comando de contagem será executado, e contador passará a valer 1


(0+1 = 1). Observe isso acontecendo na Inspeção. Em seguida, mais um [F11] e o valor de
contador será exibido na tela. Veja o resultado desses comandos na figura abaixo:

Um novo [F11] executará o comando print, o valor de contador será exibido na tela e o fluxo
retorna ao comando while:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 130

Pressionando [F11] novamente, a condição “contador < 10” do comando de decisão if será
avaliada. Como contador vale 1, e 1 é menor que 10, essa condição resultará em True e o fluxo
de execução se dirige para os comandos subordinados ao while, como vemos abaixo:

Ao realizar o comando seguinte, contador ficará com 2, como vemos abaixo:

A tarja de fluxo agora informa que o próximo comando é o print. Assim, ao pressionarmos [F11],
faremos com que esse comando seja executado, o valor de contador é buscado na memória, é
exibido na tela e o fluxo de execução retorna ao while, como observamos a seguir:

Realizando várias vezes esses passos, vemos como o fluxo de execução repetitivo se comporta, e
como a variável contador, em cada repetição, se aproxima mais e mais do limite de contagem.
Assim, contador controla o número de repetições, determinado também pela condição do while.
A figura a seguir nos mostra o resultado após repetirmos várias vezes e o contador valer 9, no
momento em que o fluxo de execução retornou para o comando while.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 131

Pressione [11] novamente, para que a condição contador < 10 seja verificada e, como ela resulta
em True, o fluxo de execução entre nos comandos subordinados ao while.

O valor do contador será exibido na tela quando você pressionar [F11], e o fluxo de execução
retornará para o while, mesmo que contador já valha 10. É importante entender que o fato de
contador já valer 10 não termina a repetição automaticamente; pelo contrário, o fluxo ainda
retornará ao while e mais uma vez a condição será testada.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 132

Depois disso, o fluxo sairá da função contar(), já que não há nenhum comando após o while e
seus comandos internos. O fluxo retornará para depois da chamada, feita na parte principal do
programa, e este será terminado, já que não há nenhum comando após essa chamada.
Execute novamente o programa, passo a passo, até que o contador chegue em 10, e verifique
como o programa termina, nesse momento. Note que, no momento em que contador é
incrementado e passa a valer 10, o fluxo de execução ainda está dentro do while e continua
dentro dele, não terminando simplesmente porque contador passou a valer 10. Lembre-se de que
você, nesse momento, sabe que contador vale 10, mas o computador ainda não sabe, mesmo
esse valor estando armazenado na sua memória. Ele saberá que isso ocorre quando o fluxo de
execução retornar ao while mas, para isso, ainda é necessário que o fluxo passe ANTES pelo
print(), para só então retornar ao while.
Podemos substituir o comando contador = contador + 1 pelo comando abaixo:
contador += 1
O operador += também pode ser aplicado a strings, realizando concatenação da string com o que
está sendo adicionado. Por exemplo:

string poema = “Maria tinha“ poema: Maria


tinha
poema: Maria tinha um carneirinho

poema += “ um carneirinho”
print(poema) → será exibido na tela o texto Maria tinha um carneirinho

Exercícios

Os exercícios abaixo devem ser implementados como opções de um único programa em Python,
chamadas a partir de um seletor de opções, que deve ser implementado usado o comando match.
Cada exercício deve ser codificado em uma função separada, chamada pelo item do match que
corresponda ao número da opção do exercício. Não use comando for, pois estamos focados em
aprender o conceito de contagem.
7.1. Use o método estudado de contagem para gerar os números inteiros de 1 a 100 e os exiba
na tela, um a um.
7.2. Pergunte ao usuário e leia quantos números serão exibidos; em seguida, gere todos os
números inteiros entre 1 e a quantidade digitada pelo usuário e exiba cada um deles.
7.3. Solicite ao usuário quantos números inteiros ele digitará; em seguida, leia esses valores
um a um e, ao final deles, informe a quantidade de pares, de ímpares e de negativos.
7.4. Solicite ao usuário quantos números inteiros ele digitará e leia o valor dessa quantidade;
em seguida, leia tantos valores inteiros quanto a quantidade lida acima e, após lê-los
todos, exiba o maior deles e o menor deles. Aceite somente valores no intervalo [-100000,
100000].
7.5. Solicite ao usuário informar quantos números inteiros serão processados e leia essa
quantidade. Em seguida, gere esses números, de 1 até a quantidade fornecida, exiba-os
um a um, um por linha e, ao lado de cada número, exiba também o valor das funções de
seno, cosseno, raiz quadrada e o número atual elevado ao número seguinte.
7.6. Solicite ao usuário informar quantos números inteiros serão processados. Em seguida,
gere esses números, de 1 até a quantidade fornecida, exiba-os um a um, um por linha. Ao
lado de cada número exiba também a contagem regressiva, ou seja, do último número até
o valor 1, usando, para calcular esse valor, um contador inverso, cujo valor inicial é o
último número e que, a cada passo, é decrementado em 1.
7.7. Solicite ao usuário que digite um número inteiro, chamado divisor e um outro número
inteiro, chamado de valorFinal. Faça a contagem de 0 até valorFinal, e exiba na tela
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 133

apenas os múltiplos do divisor. Você não deve fazer cálculos de resto nem multiplicações,
somente somas. Por exemplo, para divisor igual a 3 e valorFinal igual a 30:
0... 3... 6... 9... 12... 15... 18... 21... 24... 27... 30... FIM!
7.8. Crie um contador de tipo real que permita contagem usando valores reais. O programa
deverá ler os valores inicial, final e o passo (reais) e depois realizar uma contagem
exibindo os valores na tela. Por exemplo:
>> Contagem – Início ate Final <<
Entre com o valor inicial: 0.5
Entre com o valor final: 5.0
Passo do Ajuste: 0.5
Contagem resultante: 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0
7.9. Usando duas contagens (uma externa e outra interna), exiba toda a tabuada de 1 a 10 na
tela (1x1, 1x2, ... até 10x10). Observe que não devemos pedir o valor cuja tabuada se
deseja, e sim exibir 10 linhas, sendo cada linha a tabuada do contador externo. Resultado
esperado:

7.10. Crie um programa que execute no terminal e use três contadores para exibir na tela um
relógio, contando separadamente as horas, minutos e segundos, até que o usuário feche o
programa. Use o método sleep(1) do módulo time para que o programa fique suspenso
por 1 segundo entre cada contagem e exibição. Efetue uma repetição para horas, dentro
dela uma repetição para minutos e, dentro desta, uma terceira repetição para contar
segundos.

7.2. Acumulação de Valores

7.2.1. Somatória

Em algumas situações, é necessário que valores lidos em sequência sejam acumulados,


somando-os uns aos outros. Como exemplo disso, existe a situação de se exibir o total de salários
pagos numa empresa, para que a mesma possa depositar o valor total no banco pelo qual efetua
os pagamentos. Assim, suponha que um arquivo contém os seguintes dados de funcionários, que
serão lidos por um algoritmo:
MATRÍCULA DE FUNCIONÁRIO, NOME, SALÁRIO

Suponha que existem vários funcionários na empresa e, portanto, no arquivo existirão dados refe-
rentes a cada um deles, um funcionário por linha do arquivo. Para se somar os salários de todos
eles, usaremos variáveis que terão a função de TOTALIZADORES.
Totalizador é uma variável que será usada para acumular valores lidos ou calculados
em sequência, de forma que ao final do processo, ela contenha a soma de todos os
valores. Esta soma em sequência é chamada de Somatória.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 134

O valor inicial de um totalizador deve ser ZERO pois, quando o processo se inicia, nada ainda foi
somado. Após o totalizador ser zerado, pode-se então começar o processo de somas sucessivas.
O esquema abaixo demonstra como fazê-lo.
Suponha que totalSalarios é a variável totalizadora, e a parte inicial dos dados são os seguintes:
07508Joao 5000.00
02503Maria 10050.00
01078Ana 3450.00
08881Marcio 2500.01
21083Solange 1500.00
18094Everton 6208.00
0.00
1. Zerar o Totalizador totalSalarios

2. Ler o Primeiro Registro e separar seus campos. matricula nome salario


Os dados ficarão na memória, nas variáveis ao lado 07508 Joao 5000.00
3. Somar o salario lido com a variável totalSalarios. O valor resultante deve ser armazenado na
própria variável totalSalarios, que assim passa a acumular seu conteúdo anterior (0.00) com o
salário lido (5000.00), como indicado abaixo: 5000.00
totalSalarios
4. Ler o Próximo Registro e separar seus campos. matricula nome salario
Os dados ficarão na memória, nas variáveis ao lado 02503 Maria 10050.00
5. Somar o salario lido com a variável totalSalarios. O valor resultante deve ser armazenado na
própria variável totalSalarios, que assim passa a acumular seu conteúdo anterior (5000.00)
com o salário lido (10050.00), como indicado abaixo:
totalSalarios 15050.00

6. Ler o Próximo Registro. matricula nome salario


Os dados ficarão na memória, nas variáveis ao lado 01078 Ana 3450.00
7. Somar o salario lido com a variável totalSalarios. O valor resultante deve ser armazenado na
própria variável totalSalarios, que assim passa a acumular seu conteúdo anterior (15050.00)
com o salário lido (3450.00), como indicado abaixo:
totalSalarios 18500.00

8. Temos que repetir os dois passos acima para o próximo registro, e assim por diante, até que
terminem os dados do arquivo. Os passos 2 e 3, 4 e 5, 6 e 7 e o 8 são o mesmo conjunto de
instruções, repetido para cada registro lido. O acúmulo na variável totalSalarios nada mais é
do que a seguinte instrução de algoritmo:
totalSalarios = totalSalarios + salario
que faz com que o valor anterior de totalSalarios seja somando ao valor de salario que
acabou de ser lido, e o resultado armazenado na própria variável totalSalarios.

Um programa para se ler um arquivo texto de funcionários da empresa, totalizar seus salários e
informar a média salarial, é mostrado a seguir.
Estudaremos mais detalhes sobre arquivos no próximo capítulo. Observe apenas que abrimos o
arquivo salários.txt usando a função open() do Python e lemos linha a linha, até que as linhas
acabem (quando tentarmos ler e a linha ficar vazia, o arquivo acabou de ser lido). A cada linha
lida, separamos os dados de cada campo usando fatiamento de strings.
Agora, vamos focars no estudo da somatória. Segue o código do programa. Digite-o e o use para
testar, passo a passo, o funcionamento da somatória a partir dos dados lidos do arquivo.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 135

Vemos o resultado desse programa, executado com os


dados apresentados no início da nossa discussão.

Concluindo a ideia de somatória, vemos que ela tem um


comportamento semelhante ao de um contador.
No entanto, um contador é, em geral, incrementado
sempre pelo mesmo valor (o passo), enquanto uma
somatória pode ser incrementada por valores diferentes,
em cada vez que ocorre a soma de uma nova parcela.
Isso se torna óbvio quando lembramos de que os salários
dos funcionários de uma empresa são, geralmente,
diferentes entre si, de forma que a soma dos mesmos
não é composta por valores idênticos, como ocorre
com o incremento de um contador.
Imagine que um usuário tivesse de, realmente, digitar inúmeras vezes esse conjunto de dados de
um funcionário. Seria uma tarefa demorada, bastante cansativa e propensa a erros humanos de
digitação. Por esses motivos, o uso de arquivos com os dados pré-digitados é bastante
importante.
Leitura adicional sobre somatória na Matemática: https://algol.dev/somatorios-o-que-sao/

7.2.2. Produtório

Além do acúmulo de valores por soma, algumas aplicações necessitam que se acumule produtos
entre valores. Assim, introduziremos o conceito de Produtório:

Produtório é um acúmulo de produtos entre valores sucessivamente lidos ou calculados no


decorrer de uma repetição de ações de um algoritmo. Para ser feito, é necessária uma variável
totalizadora. Essa variável, a cada repetição dos comandos que calculam ou obtém os valores que
serão acumulados, deverá armazenar o resultado da multiplicação do seu conteúdo atual pelo
novo valor que entrará na totalização.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 136

O comando que faz isto é o seguinte:

totalDeProdutos = totalDeProdutos * novoValor

onde totalDeProdutos representa a variável usada para produtório e novoValor é o valor que foi
lido ou calculado nesta repetição do algoritmo.

Pergunta: qual deve ser o valor inicial de uma variável totalizadora de um produtório, antes de se
iniciar as repetições? A resposta é simples. Da mesma maneira que fizemos na somatória, usa-se
o elemento neutro da operação. Para a operação de produtos, o elemento neutro vale 1. Se
inicializar o totalizador de produtos com 0, ao invés de 1, todos os resultados serão nulos.
Inicializando o totalizador de produtos com o valor 1, a primeira multiplicação do produtório pelo
valor novo fará com que este seja armazenado no totalizador. Na segunda multiplicação, será feito
o produto do primeiro valor novo com o segundo, e esse resultado será armazenado no
totalizador. Isso continuará até que os dados terminem.

7.3. Uma classe de Somatória


Uma somatória, como dissemos acima, é bastante parecida com um contador. No entanto, uma
somatória não deveria se importar com a contagem de vezes em que os valores são somados,
nem com a obtenção desses valores. Podemos criar uma classe para tratar desses e outros
aspectos.
Os valores que são acumulados em sequência na somatória poderão ser obtidos de várias fontes
como, por exemplo, a digitação pelo usuário do programa ou a leitura a partir de um arquivo
gravado em disco. Portanto, fazer com que a classe de somatória se responsabilize pela obtenção
dos valores a serem somados seria impraticável, pois teríamos de prever todas as possíveis
maneiras de os valores serem obtidos.
Assim, os atributos de nossa classe de somatória se resumirão à variável que acumula os valores
somados e a uma variável que armazena quantos valores foram somados. Esse valor é
importante para o caso de desejarmos calcular a média aritmética dos valores que foram
acumulados.
Cada valor a ser somado será fornecido através de um parâmetro. O programa de aplicação, que
usa a classe de somatória, chamará o método que faz o acúmulo dos valores, passando como
argumento cada um dos valores a serem somados. A aplicação é que determina quando um valor
foi obtido e quando deve ser acumulado na somatória.
Assim, a responsabilidade pela obtenção dos valores a serem somados é da aplicação e,
portanto, a classe de somatória não deverá se preocupar com leituras de dados do teclado ou de
arquivos. Cada aplicação obtém os valores da forma que lhe é adequada, seja pela digitação no
teclado, pela leitura de dados em arquivo em disco ou pela recepção de algum outro dispositivo de
entrada.
A Matemática representa as operações de somatória e de média aritmética, com as fórmulas
abaixo:

n n é o número de valores que serão somados entre si


soma =  vi i é um contador que inicia-se em 1 e é incrementado até ultrapassar o valor n.
i =1  (Sigma) é a letra grega maiúscula que representa a operação de somatória
vi é cada um dos valores que serão somados.
Assim, a fórmula acima pode ser expandida para:

soma = 0 + v1 + v2 + v3 + ... + vn−1 + vn 0 é o elemento neutro da soma, valor inicial da somatória


v1 é o primeiro valor a ser somado ( quando i = 1 )
v2 é o segundo valor a ser somado ( quando i = 2 )
v3 é o terceiro valor a ser somado ( quando i = 3 )
vn-1 é o penúltimo valor somado ( quando i = n-1 )
vn é o último (n-ésimo) valor somado ( quando i = n )
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 137

A média aritmética é dada pela fórmula abaixo:


n

v i
media = i =1
, ou seja, é a somatória dos n valores dividida pela quantidade n de valores.
n
Assim, podemos imaginar a classe Somatoria com o modelo abaixo:

Somatoria
_soma : real
_quantosValoresSomados : inteiro

construtor __init__()
método somar(valorASerSomado : real)
função mediaAritmetica() : real
propriedade valor : real
propriedade quantos : inteiro

Declaramos os atributos _soma e _quantosValoresSomados usando o “_” como prefixo para


indicar a qualquer programador que use essa classe, que evite usar diretamente esses atributos.
Ao invés disso, o programador deverá usar propriedades que permitam o acesso de leitura (get) e
armazenamento (set) nesses atributos.
Por uma questão de segurança de informações, o programa de aplicação não deveria poder
acessar diretamente os atributos da classe Somatoria e mudar seus valores. Nem sempre é
aconselhável que o usuário tenha acesso aos detalhes dos dados, que precisam ser protegidos
para, justamente, serem evitados acessos de aplicações ou usuários maliciosos a esses dados.
Imagine um relógio de ponteiros. Se você pudesse acessar e mudar as
posições das engrenagens diretamente, estaria aberta a possibilidade
de alterar totalmente o funcionamento do relógio. Esse é um dos motivos
para que as engrenagens sejam protegidas, de forma que o usuário do
relógio pode apenas ler os ponteiros, que estão na parte externa, e cujos
valores dependem das engrenagens internas.
Se você precisar ajustar o horário do relógio, isso trará mudanças nas
engrenagens internas, que representam os valores referentes ao tempo
marcado. Para fazer esses ajustes, você também não tem acesso direto
a essas engrenagens, mas sim usa mecanismos que permitem ao
usuário fornecer valores dentro dos limites esperados para horários.
Nesse caso, usamos botões ou outros mecanismos que levam os valores
desejdos pelo usuário para dentro do relógio. Em Python, podemos usar
propriedades e parâmetros tanto para ler os valores sem alterá-los
quanto para ajustar esses valores de forma controlada e segura.

Acesso protegido aos valores de atributos

Implementaremos o acesso aos valores internos da Somatoria, usaremos uma propriedade de tipo
real, que lerá o valor atual do atributo _soma. Uma propriedade permite acessar o valor de um
atributo protegido para evitar que o programa que usa a classe acesse e altere diretamente o valor
desse atributo, o que poderia levar a erros de programação ou mesmo uso da classe para
objetivos escusos ou não previsto pelos seus desenvolvedores, como citamos acima.

@property property para acessador get permite obter (ler) o valor de


def valor(self): um atributo protegido (com _). Assim, a aplicação evita o
return self._soma acesso direto ao atributo e nao poderá mudar seu valor.
Mudanças de valor serão feitas apenas pelos métodos
@property internos da classe.
Com o uso de property, podemos tratar o valor do atributo
antes de retorná-lo à aplicação.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 138

def quantos(self):
return self._quantosValoresSomados

Em nosso programa, usaremos a propriedade valor para acessar o conteúdo do atributo _soma.
Como ela apenas obtém (get) _soma, não permite alterá-lo. Dessa forma, _soma ficará protegido
de alterações indevidas feitas no programa de aplicação, desde que o programador siga a boa
prática de não usar, na aplicação que desenvolve, atributos precedidos por “_”.
Algumas linguagens de programação não possuem estruturas para declaração de propriedades e,
assim, tem que usar funções para obter (getters) e alterar (setters) valores de atributos protegidos.
É o caso de Java. Já Python, C# e Delphi possuem o recurso de propriedades.
A vantagem de usar uma propriedade ou uma função para devolver o valor de um atributo de uma
classe é que, assim, podemos protegê-lo de acesso direto pelos comandos da aplicação. Em
Python, todo o atributos de uma classe é público, ou seja, pode ser acessado por qualquer
programa ou classe que use a classe onde esse atributo foi declarado.
No entanto, nem sempre podemos garantir que um programa irá usar um atributo de uma classe
sem danificar seu valor.
Em outras palavras, nada impede que um programa tenha um comando como o abaixo:

umaSomatoria = Somatoria()
...
umaSomatoria._soma = 4945

Talvez esse comando poderia até mesmo ter alguma utilidade, mas certamente não é uma das
características de um objeto de Somatória receber esse valor específico. Assim, o melhor seria
evitar o acesso direto a esse atributo, pois ele é realmente o cerne desse objeto, e sua
manipulação direta por um programa causará mais erros do que acertos. É por isso que esse e os
outros atributos foram declarados com “_”, para indicar que devem ser considerados protegidos e
não acessados diretamente.
Os métodos __init__(), somar() e mediaAritmetica() são públicos. De outra forma, um programa
de aplicação não poderia chamá-los quando deles necessitasse.
No entanto, para infomar o desenvolvedor que não é permitido alterar o conteúdo dos atributos
_soma e _quantosValoresSomados diretamente, não codificamos o acessador set para esses
atributos (@soma.setter e @quantos.setter).

No VSCode, crie e abra uma nova pasta, chamada pySomatoria, dentro da pasta
\Tecnico\1oSemestre\TecPro\cap08.
Nessa pasta, crie o arquivo soma.py e nele digite o código da classe Somatoria, como veremos a
seguir. Lembre-se de digitar a inicial desse nome em maiúscula (S) para seguir o padrão
estabelecido pela linguagem Python.

class Somatoria(): Quando formos iniciar a somatória,


def __init__(self): usaremos o construtor, que
self._soma = 0 simplesmente zera os atributos
self._quantosValoresSomados = 0
Atributos foram protegidos (“_”)
@property para que a aplicação evite acessá-
def valor(self): los diretamente, sem controle
return self._soma
Essas propriedades foram
@property declaradas para que a aplicação
possa obter (get) o valor presente
def quantos(self):
nos atributos mas não alterá-los
return self._quantosValoresSomados (não há set)

O método Somar() recebe como parâmetro um valor real, que será acumulado na somatória, e
incrementa o número de valores que foram somados até o momento:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 139

def somar(self, valorASomar):


self._soma = self._soma + valorASomar
self._quantosValoresSomados = self._quantosValoresSomados + 1

Observe que podemos usar os operadores especiais embutidos de Python para realizar as
operações acima. Portanto, o método Somar() poderia ser escrito como abaixo:

def somar(self, valorASomar):


self._soma += valorASomar
self._quantosValoresSomados += 1

O método função mediaAritmetica() vêm abaixo. Note como testamos o valor do divisor antes de
calcular a divisão que gera a média aritmética, para evitar divisão por zero:

def mediaAritmetica(self):
if self._quantosValoresSomados > 0 :
return self._soma / self._quantosValoresSomados

raise ZeroDivisionError("Não houve valores somados.")

Acima, quando não conseguimos calcular a média, dispararemos uma exceção, informando ao
programa que houve uma tentativa de divisão por zero.
Como o programa irá discernir que a divisão não foi feita? Simplesmente disparar a exceção
avisará o programa que uma situação anômala ocorreu. Como esse erro é uma exceção à regra,
neste momento apenas “torceremos” para que não ocorra e aprenderemos a tratar essa exceção
logo mais.
Vamos discutir uma aplicação que use essa classe. Abra a guia com o arquivo main.py. Suponha
que o usuário deverá digitar uma quantidade pré-conhecida de valores reais, a partir de uma lista
de valores, para que seja calculada sua soma e sua média aritmética. Desenvolveremos esse
programa passo a passo e descobriremos onde a contagem e a somatória se incluem na solução.
Em primeiro lugar, o usuário saberá de antemão a quantidade de valores reais que serão
processados. Portanto, essa quantidade deverá ser informada ao programa. Isso nos leva à
necessidade de uma variável que armazene a quantidade de valores que serão processados, já
que não vamos querer um programa que funcione apenas para um número fixo de vezes.
Portanto, vamos declarar a variável inteira quantosValores, cuja função é informar ao programa
quandos valores serão digitados e processados. O valor dessa variável será lida dentro de um
while, que será repetido enquanto o valor dessa variável não for adequado. Isso é feito pelos
comandos abaixo:

def processar():
quantosValores = 1
while quantosValores < 0:
quantosValores = int(input("Informe quantos valores serão digitados:"))
if quantosValores < 0:
print("Quantidade inválida! Digite novamente.")
...

if __name__ == '__main__':
processar()

Acima, além da solicitação ao usuário e da leitura da quantidade de valores reais que ele digitará
em seguida, aproveitamos para verificar se a quantidade informada é adequada. Se o usuário
digitar um valor negativo ou nulo, emitimos uma mensagem na tela informando-o que a
quantidade digitada é inválida, e repetimos a leitura. Caso isso não ocorra (a condição do if é
falsa), o fluxo de execução retornará ao while e sairá desse comando, se dirigindo para “...”, onde
a leitura e processamento dos valores digitados serão realizados.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 140

É justamente esse processamento que precisamos discutir agora. Observe que, para o
computador ter acesso aos valores reais que serão digitados, é preciso que eles sejam digitados
um a um, dentro de uma repetição, uma vez que o programa não sabe a quantidade de valores
que serão realmente digitados, mesmo que o usuário saiba. Cada input() lê apenas um valor, em
geral.
No entanto, quantosValores é uma variável inteira que guarda a quantidade de valores reais que
serão digitados. Portanto, se quantosValores vale 87, teremos que ler 87 valores reais a serem
digitados. Se ele vale apenas 9, teremos de ler 9 valores reais que o usuário digitará.
Portanto, o programa precisa se adaptar ao valor armazenado em quantosValores. Um contador
deverá variar entre 1 e quantosValores pois, quando temos de repetir uma ação por um número
conhecido de vezes, usamos uma variável de contagem para fazer as repetições na quantidade
desejada, controlando assim os momentos de digitação dos valores que serão processados.
No arquivo main.py, digite os comandos mostrados no quadro abaixo, respeitando as indentações:

def processar():

def lerEProcessarValores():
pass

def exibirResultados()
pass

def pedirQuantidade()
quantosValores = -1
while quantosValores < 0:
quantosValores = int(input("Informe quantos valores digitará:"))
if quantosValores < 0:
print("Quantidade inválida! Digite novamente.")

pedirQuantidade()
lerEProcessarValores()
exibirResultados()

if __name__ == '__main__':
processar()

O que fizemos acima foi separar três funcionalidades distintas do programa, colocando-as em
funções específicas. Assim, o código referente a solicitação da quantidade de valores a processar
ficará separado do código da leitura e processamento dos valores a serem digitados e também do
código referente à exibição dos resultados na tela.
Para o usuário que opera o programa, isso não será visível, já que ele não vê o código fonte. No
entanto, para o desenvolvedor do programa e para um eventual profissional que venha a corrigir
esse programa no futuro, a sepação dos assuntos em funções e métodos específicos torna o
código todo mais legível e mais fácil de manter. É como se cada função fosse um capítulo de um
livro de Matemática: ao invés de misturarmos assuntos, como equação do 2o grau ensinada junto
com cálculo de frações. Separamos cada assunto em um capítulo específico.
Observe que as funções pedirQuantidade(), lerEProcessarValores() e exbirResultados() estão
declaradas dentro (aninhadas, indentadas) da função processar(). Isso significa que são funções
locais à função processar(), e só podem ser chamadas dentro dessa, pois não são acessíveis
externamente, por estarem aninhadas a ela.
Na função específica para efetuar a leitura dos valores e o seu processamento, teremos de
codificar o mecanismo de contagem, que fará a repetição da leitura de um valor real tantas
vezes quanto for o valor armazenado na variável quantosValores.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 141

Dentro dessa função faremos a leitura de cada um dos valores que o usuário deseja digitar. Note
que, como não sabemos quantos valores realmente deverão ser lidos (isso apenas será
conhecido quando o programa for executado), não temos como declarar uma variável avulsa
(individual) específica para cada valor que será lido, nem podemos usar algo como declarar
alguns e, em seguida, colocar reticências ( ... ), pois embora saibamos em nossa mente como
gostaríamos que isso funcionasse, o computador não sabe e não criará variáveis normais
avulsas, sob demanda, sem que você as declare explicitamente e em quantidade conhecida.
Portanto, vamos declarar uma única variável para receber cada valor lido e colocar a leitura dentro
de uma repetição. A cada vez que o while for repetido, leremos um novo valor, como vemos
abaixo:

def processar():

def lerEProcessarValores():
contador = 1
while contador <= quantosValores :
umValorLido = float(input(f"Digite o {contador}o. valor real: "))
# processar o valor lido, ou seja, acumulá-lo na somatória
contador += 1 # gerar o sucessor do contador
... # calcular a média aritmética

É claro que, a cada nova repetição, o valor que estava presente na variável umValorLido será
substituído por outro. Para que os valores anteriores não se percam, precisamos acumulá-los
numa somatória pois, obviamente, o objetivo desse programa é calcular a soma e a média dos
valores reais que o usuário digitar.
Portanto, uma operação de somatória deve ser realizada para acumularmos cada valor digitado
junto aos demais e, para fazer essa operação, declararemos um objeto da classe Somatoria e o
instanciaremos, usando-o no acúmulo dos valores digitados:

from soma import Somatoria


global minhaSoma, quantosValores, mediaDosValores
minhaSoma = Somatoria()
quantosValores = 0
mediaDosValores = 0.0
def processar():
def lerEProcessarValores():
contador = 1
while contador <= quantosValores :
umValorLido = float(input(f"Digite o {contador}o. valor real: "))
# processar o valor lido, ou seja, acumulá-lo na somatória
minhaSoma.somar(umValorLido)
contador += 1 # gerar o sucessor do contador

mediaDosValores = minhaSoma.mediaAritmetica() # calcular a média


def exibirResultados():
pass

A cada repetição, o objeto minhaSoma usará seu método somar() para acumular o valor recém-
digitado pelo usuário (na variável umValorLido). Assim, mesmo que a variável umValorLido seja
modificada a cada repetição, o valor nela presente vai ficando acumulado (somado) aos que já
foram digitados e acumulados anteriormente. O objeto minhaSoma, após o final da repetição, tem
o método função mediaAritmetica() invocado para calcular a média aritmética.
Declaramos as variáveis umValorLido e contador dentro do método LerEProcessarValores(),
mas não declaramos minhaSoma nem mediaDosValores dentro desse método. Isso ocorre
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 142

porque as duas primeiras variáveis não serão usadas fora desse método, enquanto
minhaSoma e mediaDosValores precisarão ser usadas fora para que seus valores sejam
exibidos.
Assim, controlamos o alcance de uma variável. Sempre que uma variável tem alcance pelo
programa todo, ela é chamada de variável global. Ela pode levar valores errados de um ponto do
programa a outros. Portanto, sempre que usar métodos, é bom declarar internamente as variáveis
cujos valores não precisarão ser usados fora desse método. Essas são chamadas variáveis
locais.
Sendo assim, precisamos declarar minhaSoma como global:
from soma import Somatoria
global minhaSoma, mediaDosValores
quantosValores = -1
minhaSoma = Somatoria()
mediaDosValores = 0.0
def processar():
def lerEProcessarValores():
contador = 1
while contador <= quantosValores :
umValorLido = float(input(f"Digite o {contador}o. valor real: "))
minhaSoma.somar(umValorLido) # acumular o valor lido na somatória
contador += 1 # gerar o sucessor do contad
mediaDosValores = minhaSoma.mediaAritmetica() # calcular a média
def exibirResultados():
print(f"Foram somandos {quantosValores} valores.")
prinf(f"A soma dos valores é {minhaSoma.valor:7.2f}")
print(f"A média aritmética dos valores é {mediaDosValores:7.2f}")
def pedirQuantidade():
global quantosValores # para acessar a variável declarada no início
while quantosValores < 0:
quantosValores = int(input("Informe quantos valores digitará:"))
if quantosValores < 0:
print("Quantidade inválida! Digite novamente.")
pedirQuantidade()
lerEProcessarValores()
exibirResultados()
if __name__ == '__main__': # programa principal
processar()

Tratamento de Exceções

Observe o método lerEProcessarValores() acima. Logo depois de calcularmos a somatória, tendo


saído do while, fazemos o cálculo da média aritmética. No entanto, poderá ocorrer de o usuário ter
digitado zero valores (pois o valor 0 como quantidade de valores a processar está sendo aceito).
Caso não houvesse valores digitados, haveria um erro no cálculo da média. Isso ocorreria se
quantosValores fosse igual a zero.
Vamos mudar a função lerEProcessarValores() para que ela, ao invés de simplesmente calcular a
média aritmética através da chamada ao método mediaAritmetica(), capture a exceção que é
disparada nesse método quando o divisor (quantosValoresSomados) vale zero. Assim,
poderemos avisar o programa que ocorreu um erro e este deverá decidir o que fazer com essa
situação.
Observe o método MediaAritmetica:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 143
Se o if der True (divisor maior que zero),
podemos fazer a divisão. O fluxo de execução
pula o comando do if e faz o cálculo da média. O
resultado desse cálculo é retornado para o
def mediaAritmetica(self): comando que chamou este método-função.
if self._quantosValoresSomados > 0 :
return self._soma / self._quantosValoresSomados

raise ZeroDivisionError("Não houve valores somados.")


Se o divisor valer zero, o método levanta (raise)
uma exceção. Isso faz o fluxo de execução sair
desta função e retornar para o ponto em que a
função foi chamada, sem que um resultado seja
retornado. O comando de chamada é abortado.

Bem, o método-função acima dispara/levanta uma exceção. Uma exceção é um objeto da classe
Exception que, ao ser instanciado, recebe uma mensagem de erro que poderá ser exibida pelo
programa quando este tratar a exceção.
Tratar essa exceção significa que o local de chamada da função acima deverá esperar que um
erro possa ocorrer no cálculo da média e tomar atitudes caso isso ocorra, ou seja, capture a
possível exceção e faça com que o programa não seja, simplesmente, abortado pela ocorrência
de um erro de divisão por zero.
Para tratar uma exceção, o programa precisa antes capturar essa exceção. Isso é feito pelo uso
do comando try-except, como vemos no código do programa, abaixo modificado:
Se não houver erro, os comandos abaixo
deste são executados e se pula o catch. Ai o
fluxo vai para o final do programa.
def exibirResultados():
print(f"Foram somandos {quantosValores} valores.")
print(f"A soma dos valores é {minhaSoma.valor:7.2f}")
try:
mediaDosValores = minhaSoma.mediaAritmetica() # calcular a média
print(f"A média aritmética dos valores é {mediaDosValores:7.2f}")
except Exception as erro:
print(f"Ocorreu algo errado: {erro}")
Se houver erro em mediaAritmetica(), a exceção é disparada e o
fluxo pula ao except, executando seus comandos sem abortar o
programa.

Você pode, agora, remover a declaração d mediaDosValores como global, e não mais calcular
essa variável na função lerEProcessarValores().
Poderemos usar essa classe Somatoria em muitas aplicações úteis. O interessante aqui é que,
atualmente, aplicações usam muito classes e, portanto, já temos prontas classes que serão úteis
futuramente, sem que precisemos recriá-las a cada nova aplicação.
Claro que poderíamos optar por colocar diretamente no programa os comandos de somatória,
cálculo de média aritmética e outros que ainda acoplaremos a essa classe.
Mas colocá-los numa classe nos permite agrupar todas essas funcionalidades num único lugar,
numa única classe, especializada nas tarefas relativas à operação de Somatória. Nessa classe
podemos adicionar novas funções como, por exemplo, informar qual o maior e qual o menor dos
valores digitados, média ponderada, média harmônica, dentre outras várias tarefas que se
realizam com somatória.
Observe que, por serem armazenadas em arquivos diferentes do programa de aplicação, as
classes que criamos podem ser acopladas a outros programas facilmente e, assim, permitirem a
criação de novas aplicações sem que se perca tempo planejando as funcionalidades de
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 144

somatória; isso nos permite focar mais rapidamente na real aplicação que o programa procura
implementar.
Outro exemplo de somatória ocorre em jogos digitais. Por exemplo, no Resident Evil 5, a
personagem principal precisa acumular dinheiro para poder comprar as armas, munições e até
mesmo roupas. Conforme Chris ou Sheva encontram certas peças no jogo, seus bolsos
acumulam os valores dessas peças. Note que cada um deles tem seu próprio bolso, de forma
que, para cada um, existe uma somatória própria. Isso indica, também, que cada um dos
personagens possui um objeto próprio no jogo, com seus atributos e comportamentos separados.

Execução do programa com objeto Somatoria

Vamos executar o programa anterior passo a passo e discutir os detalhes do mecanismo de


criação e acesso aos objetos e sua interação com o restante do programa.
Na função lerEProcessarValores(), marque um
breakpoint na linha d o comando minhaSoma =
Somatoria(). O círculo vermelho que aparece é o
breakpoint marcado:
Inicie a Depuraçção do Arquivo Python (main.py)
para executar o programa em modo de depuração,
passo a passo. A execução seguirá o fluxo até o
breakpoint. Pressione [F11].

O fluxo de execução passará para o


construtor da classe Somatoria.
Pressione [F11] para que os atributos
_soma e _quantosValoresSomados
sejam declarados e passem a valer
0, até que retorne ao breakpoint.

Pressione [F11] até o fluxo chegar a processar().

Na janela Debugger, adicione


uma inspeção para a variável
minhaSoma:

Pressione [F11] novamente e o fluxo irá para o início


do programa, no if que chama a função processar().
Pressione [F11] para executar o if e [F11] novamen-
te para chamar a função processar().

Observe que, pressionando [F11] em seguida


algumas vezes, o fluxo de execução passará por
cada um dos métodos internos a processar antes
de se dirigir para a chamada a esses métodos (no
comando pedirQuantidade()).
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 145

Isso ocorre porque


o interpretador
Python precisa
conhecer as
assinaturas (nomes
e parâmetros) de
cada método
interno para avaliar
sua correção
sintática e, assim,
continuar a
execução.
Pressione [F11]
para o fluxo de
execução se dirigir
ao início da função
pedirQuantidade().
Crie uma inspeção
para a variável
quantosValores e
pressione [F11]:

Pressione [F10] até que o fluxo chegue ao comando que lê a variável quantosValores. Não
pressione [F11] para não ter que entrar dentro dos módulos de leitura de teclado do Python, que
não nos interessam agora. Na janela Console, digite 5 como resposta à pergunta sobre
quantidade de valores e [Enter], para o fluxo seguir de volta ao while:

Aproveite para criar a inspeções para contador, umValorLido


e mediaDosValores. Observe que, na janela Debugger,
minhaSoma tem uma seta do lado esquerdo. Clique nessa
seta e expandirá o objeto, mostrando os atributos que estão
nele encapsulados e seus valores atuais. Faça o mesmo
para o item Protected Attributes e veja os atributos
protegidos com “_”.
Esse momento da depuração é mostrado na figura abaixo,
onde vemos as janelas Console, a de Inspeção e a de
Código:
Pressione [F11] seguidamente para que o fluxo vá até o
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 146

primeiro comando da função lerEProcessarValores(), no comando que inicializa a variável


contador. Pressione [F11] e observe que contador ficará valendo 1:

Observe que o fluxo chegou ao comando while. Na janela Debugger, veja que o valor de contador
mudou para de 0 para 1.
Coloque o cursor sobre a condição do while, e note que o depurador a avalia e mostra ser true, ou
seja, neste momento contador é menor ou igual a 5 e, por esse motivo, o fluxo de execução
entrará dentro do while quando pressionarmos [F11]:

Pressione [F10] seguidamente até que seja pedida a digitação do 1º valor real. Digite o valor 7 e
[Enter]. A variável umValorLido será preenchida com 7.0 e o fluxo irá para o comando que chama
o método somar().
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 147

Pressione novamente [F11] para observar o valor 7.0 da variável umValorLido ser passado como
argumento para o método somar() e ser recebido pelo parâmetro valorASomar desse método:

Observe o valor do parâmetro valorASomar, que recebeu o valor 7 que foi passado como
argumento. Ao pressionar novamente [F11] duas vezes, o conteúdo de valorASomar será
acumulado no atributo _soma e o atributo _quantosValoresSomados será incrementado (contou
mais um valor):

Observe que as propriedades quantos e valor


nos permitem ler os valores dos atributos
_soma e _quantosValoresSomados de
minhaSoma. Tais atributos foram mudados e
representam o valor atual da somatória.
Esses são valores parciais, pois somados
apenas o primeiro dos valores digitados. Ainda
resta digitar mais quatro valores.

Pressione [F] para que a variável contador seja incrementada.

Observe o retorno do fluxo de execução para o while e coloque o cursor sobre a condição, que
continua valendo true, pois contador (que vale 2) é menor ou igual a quantosValores (que vale 5):
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 148

Continue pressionando [F10] seguidamente para que o fluxo entre no while e os comandos que
pedem a digitação do segundo valor real sejam executados. Digite 45, agora. A figura abaixo
ilustra esse momento da execução:

Pressione [F11] para que o método somar() seja chamado. Observe o valor (45) do argumento
passado na chamada e do parâmetro recebido ao entrarmos nesse método:

Pressione [F11] para executar os comandos que acumulam o valorASomar na _soma e


incrementar _quantosValoresSomados. Em seguida, pressione [F11] para retornarmos ao local de
chamada desse método. Observe na visualização que minhaSoma passou a ter o valor 52
acumulado e a contagem de 2 valores somados:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 149

Pressione [F11] para incrementar o contador e para que o fluxo retorne ao while. A condição
continua true, de forma que, ao pressionarmos [F10] novamente, iremos ler o terceiro valor:

Pressione [F10] para executar o comando de leitura do terceiro valor (digite -5) e [F11] para
chamarmos o método somar() e ver a passagem do valor digitado como argumento, e seu
recebimento pelo parâmetro valorASomar. Depois, pressione [F11] continuamente para fazer o
acúmulo deste valor na somatória, até o incremento do contador, e o retorno do fluxo de execução
ao while. A figura abaixo mostra isso:

Continue pressionando [F10] continuamente, para digitar os dois próximos valores: 10 e 4. Após a
digitação desse último valor, observe que, quando o fluxo de execução retorna ao while, contador
valerá 6 e, por isso, a condição do while dará false, pois 5 não é menor ou igual a 6:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 150

Ao pressionar [F10] novamente, o fluxo sairá do while e sairá de lerEProcessarValores(),


retornando na função processar(), no comando que chamou lerEProcessarValores(). Pressione
[F11] novamente para o fluxo chegar à chamada do método exibirResultados(). Pressione [F11]
para entrarmos nesse método:

Na figura anterior, veja que o objeto minhaSoma contém o valor da somatória (61.0), e informa
também quantos valores foram somados, no total de 5 valores.
Na tela Console ao lado, vemos os
valores que foram digitados, um a cada
ciclo de repetição do while.
No método ExibirResultado(), vamos
calcular a média aritmética e exibir seu
valor.
Pressione [F11] para que o programa
chame esse método e fluxo de execução
salte para lá.
Pressione [F11] continuamente até o fluxo de execução chear à chamada da função
mediaAritmetica():
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 151

Pressione [F11] novamente para ver como o fluxo funciona dentro desse método-função:

Execute passo a passo. Veja que o comando raise é pulado (pois a quantidade de valores
somados é maior que zero). Ao retornar da chamada dessa função, o fluxo pulará a cláusula
except do comando try-except (tratamento de exceções) e irá para o final desse método.
Pressione [F11] continuamente até terminar o programa.
A execução desse programa na janela
Terminal é mostrada ao lado.
Num programa executado por um usuário, e
não por um programador ou programadora
como você, apenas a tela ao lado seria
vista. O usuário do programa não vê os
comandos que você programou, nem
executa o programa passo a passo, como
fizemos agora.
Some os valores digitados e calcule sua
média aritmética, comparando esses
valores com os resultados apresentados pelo programa executado.
Para testar o funcionamento do raise no disparo de exceções e do try-except na captura de
exceções, execute passo a passo, informando que serão digitados zero (0) valores

7.4. Uma Classe de Produtório


Um objeto que sirva para encapsular os atributos e comportamentos de um produtório terá a
variável totalizadora e um contador para a quantidade de valores que estão sendo multiplicados.
Os métodos que podemos incluir nessa classe são o construtor, o método que acumula os
produtos e o que informa o valor atual da sequência de produtos. Outro método interessante é o
da média geométrica. A média geométrica é a raiz n-ésima do produto de N valores, dada pela
fórmula abaixo:

n n é o número de valores que serão multiplicados entre si


m=n  vi i é um contador que se inicia em 1 e é incrementado até ultrapassar o valor n.
i =1  (Pi) é a letra grega maiúscula que representa a operação de produtório
vi é cada um dos valores que serão multiplicados.
Assim, a fórmula acima pode ser expandida para:

m = n 1  v1  v 2  v3  ...  v n −1  v n
1 é o elemento neutro da multiplicação, o valor inicial do produtório
v1 é o primeiro valor a ser multiplicado ( quando i = 1 )
v2 é o segundo valor a ser multiplicado ( quando i = 2 )
v3 é o terceiro valor a ser multiplicado ( quando i = 3 )
vn-1 é o penúltimo valor multiplicado ( quando i = n-1 )
vn é o último (n-ésimo) valor multiplicado ( quando i = n )
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 152

Nossa classe Produtorio inicial teria o diagra-


ma de classe ao lado, portanto: Produtorio
_produto : real
_quantosValoresMultiplicados : inteiro
Crie o código da classe Produtorio e
implemente os métodos e propriedade acima, construtor __init__()
para que a classe seja usada nos próximos método multiplicar(valorASerMultiplicado : real)
exercícios. função mediaGeometrica : real
propriedade valor : real
propriedade quantos: inteiro
Exercícios
Os exercícios abaixo devem ser implementados como opções de um único programa, chamadas a
partir de um seletor de opções. Cada exercício deve ser codificado em um método separado e não
dentro do seletor de opções, sendo chamado por meio do comando match.
7.11. Leia uma série de números inteiros e, usando um objeto da classe Somatoria, os some.
Leia valores um a um, repetindo a leitura até que o usuário digite um valor zero, que não
deve ser somado. Exiba a quantidade, soma e média aritmética dos valores digitados
antes desse zero.
7.12. Leia a informação sobre quantos números inteiros serão somados, após solicitar ao
usuário que informe isso. Em seguida, usando uma variável de contagem, gere cada um
desses números de 1 até a quantidade fornecida, exiba-os um a um e, usando um objeto
da classe Somatoria, calcule sua soma. Ao final da contagem, exiba a soma e a média
aritmética dos valores gerados e exibidos.
7.13. Leia a informação sobre quantos números inteiros serão multiplicados, após solicitar ao
usuário que informe isso. Use uma variável de contagem para gerar os inteiros entre 1 e
essa quantidade. Use um objeto da classe Produtorio para calcular o produto entre todos
esses valores gerados. Ao final da geração dos valores, exiba o resultado do produto entre
eles e sua média geométrica.
7.14. Solicite ao usuário uma quantidade de números a serem digitados; em seguida, leia esses
valores e, ao final, informe quantos estão nos intervalos (-infinito, 0), [0, 25], [26,50],
[51,75], [76,100] e (100, +infinito), e a média aritmética dos valores em cada intervalo. Use
objetos da classe Somatoria para calcular a quantidade de valores e sua média aritmética
em cada intervalo.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 153

8. 8.1. Introdução
Arquivos de Quando desenvolvemos aplicativos que tratam grandes
quantidades de dados, em geral precisaremos de maneiras de
Dados armazenar essa massa de dados em dispositivos em que os
dados não sejam perdidos, pois o custo de digitá-los
novamente poderá ser proibitivo. Arquivos permanecem com
seus dados quando um programa deixa de ser executado,
permitindo que os dados se tornem persistentes e acessíveis
sempre que necessário e evitando que precisem ser
novamente digitados.
Em geral, uma relação extensa de dados pode ser armazenada em arquivos gravados em disco,
para serem recuperados sempre que for necessário processá-los. Processar dados envolve ler os
dados, aplicar algoritmos sobre eles e obter-se resultados, como todos os programas que fizemos.
No entanto, neste momento falamos sobre uma quantidade de dados tal que não seja viável
digitá-los toda vez que precisem ser processados, ou porque são muitos dados, ou porque
precisam ser processados várias vezes, ou devido a ambas situações.
Há vários tipos de maneiras de gravar os dados nos arquivos. Podemos, por exemplo, gravar os
dados como textos formados pelos caracteres que usamos no dia a dia ou diretamente pela sua
representação interna em binário.
Qualquer que seja a forma como os dados estejam gravados em um arquivo, para o computador
todo arquivo é um fluxo de bytes, ou seja, uma sequência de dígitos binários, mesmo os que
foram gravados em formato de texto. Esse fluxo de bytes pode ser acessado como se fosse um
vídeo em streaming na Internet, bloco a bloco de dados, às vezes de tamanhos diferentes,
processados em sequência, num fluxo contínuo.
Esse tipo de processamento não estruturado nos obrigaria a processar (ler ou escrever) dados do
arquivo byte a byte, analisar segundo algum algoritmo e transformar essa sequência em
informações estruturadas que possamos compreender, e isso torna o processamento dos dados
bastante complexo.
Portanto, pode ser mais fácil gravar os dados em formato de texto e recuperá-los dessa maneira,
separando as diversas informações gravadas de forma a dar sentido a elas. Os arquivos de texto
podem estar organizados de diversas formas, também. Temos bastante liberdade na maneira de
organizá-los. Por exemplo, cada conjunto de informações que representam uma mesma entidade
pode ficar numa única linha do arquivo. Podemos chamar essa linha de registro, pois um registro
é um agrupamento de campos (de dados específicos) que se referem a uma mesma entidade.
Dentro dessa linha, as diversas informações podem estar dispostas cada uma com lugar e
tamanho fixos, sem variações. Ou cada informação pode estar separada uma da outra por
vírgulas (formato CSV – Comma Separated Values) ou, ainda, o formato JSON (JavaScript Object
Notation), que lembra a declaração de atributos de uma classe. Vemos as três situações abaixo:
João 51500.00 2 1 0.00 João,51500.00,2,1,0.00
Maria 27250.00 0 0 15000.00 Maria,27250.00,0,0,15000.00
Ana 15832.50 10 0 200.28 Ana,15832.50,10,0,200.28
Paula 10721.05 15 1 1800.00 Paula,10721.05,15,1,1800.00
Ricardo 2500.00 0 0 0.00 Ricardo,2500.00,0,0,0.00
Arquivo texto com dados dispostos em tamanho fixo Arquivo com valores separados por vírgula

[
{"Nome":"João","Salario":51500.00,"Faltas":2,"Filhos":1,"Imposto":0.00},
{"Nome":"Maria","Salario":27250.00,"Faltas":0,"Filhos":0,"Imposto":15000.00},
{"Nome":"Ana","Salario":15832.50,"Faltas":10,"Filhos":0,"Imposto":200.28},
{"Nome":"Paula","Salario":10721.05,"Faltas":15,"Filhos":1,"Imposto":1800.00},
{"Nome":"Ricardo","Salario":2500.00,"Faltas":0,"Filhos":0,"Imposto":0.00}
]
Arquivo texto com dados em formato JSON
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 154

Há vantagens e desvantagens em qualquer um dos formatos acima, mas todos usam caracteres,
que é uma maneira bastante comum de representar informações e facilita muito a troca de
informações entre aplicativos.
O arquivo de simples texto ou arquivo ASCII (que é uma abreviação para “American Standard
Code for Information Interchange'') é aquele que contém apenas caracteres que representam
letras, números e outros símbolos legíveis, segundo um padrão que é de fato universalmente
aceito.
Arquivos deste tipo podem ser lidos e manipulados diretamente pelo usuário através do uso de
editores ou paginadores de uso geral, pois a forma binária do seu conteúdo corresponde a um
padrão aberto e bem conhecido de todos.
Ou seja, os arquivos ASCII são aqueles diretamente legíveis pelos usuários, através da utilização
de programas de uso geral, sem a necessidade de aplicativos específicos ou proprietários. Uma
das características importantes dos sistemas livres e abertos é que todos os seus arquivos de
configuração sejam arquivos ASCII, facilmente legíveis ou copiáveis por qualquer um.
Fonte: http://latt.if.usp.br/fma215/apostilas/aula-08/node3.html

8.2. Tabela ASCII e Caracteres Representáveis

A tabela ASCII (American Standard Code for Information Interchange) indica os códigos que
representam cada caracter para o computador. Por exemplo, a letra 'A' é representada pelo valor
decimal 65, o dígito '0' é representado por 48, '1' por 49, '2' por 50, e assim por diante. Cabe aqui
diferenciar dígitos (ou algarismo) e número: um dígito é um caracter entre '0' e '9', ao passo que
um número é um valor numérico que pode valer entre 0 e 9 ou qualquer outro valor. A diferença é
que o computador armazena dígitos como caracteres da tabela ASCII, usando o código do
caracter para isso. Valores numéricos são armazenados em binário puro, que é, como vimos,
aquela linguagem de impulsos elétricos baixos e altos (0 e 1).

c = ‘0’ → armazena como 48 em binário → |00110000| → 1 byte (8 bits)


i = 48 → armazena como 48 em binário →
|00000000|00000000|00000000|00110000| → 4 bytes (32 bits)

Em outras palavras, quando armazenamos um caracter numa variável o que o computador faz
nada mais é do que armazenar o código ASCII do mesmo numa posição da memória identificada
pela variável usada. Vimos que um caracter ocupa um byte na memória. Um byte (8 bits) pode
armazenar 256 configurações diferentes de 0s e 1s.
Por isso, a tabela ASCII é formada por 256 caracteres diferentes. Cada caracter tem um código
próprio, de 0 a 255, e cada código corresponde a um, e apenas um, caracter da tabela. Abaixo
mostramos a tabela ASCII.
Para entender a tabela abaixo, basta somar o valor de código da linha com o da coluna, para
obter o caracter correspondente. Assim, o caracter 'n' tem como código o valor 96 + 14 = 110. O
caracter '5' tem como código o valor 48 + 5 = 53.
Note que existem códigos diferentes para letras maiúsculas e minúsculas. Existe aí uma regra:
dada a letra maiúscula, o código da letra minúscula correspondente é de 32 a mais que o código
da letra maiúscula. Portanto, se sabemos que o código de 'A' é 65, então o código de 'a' será de
65 + 32, que vale 97. Observe que esta regra não vale para as letras acentuadas.
Na tabela abaixo, os 128 primeiros caracteres são os padronizados. Os demais 128 são usados
para desenhos de linhas na impressora e tela, ou ainda caracteres com acentuação, e variam
bastante de fabricante para fabricante.
Temos a seguir a Tabela ASCII:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 155

0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240


0 NUL DLE SP 0 @ P ` p Ç É á ░ └ ╨ α ≡
1 SOH DC1 ! 1 A Q a q u æ í ▒ ┴ ╤ ß ±
2 STX DC2 “ 2 B R b r é Æ ó ▓ ┬ ╥ Γ ≥
3 ETX DC3 # 3 C S c s â ô ú │ ├ ╙ π ≤
4 EOT DC4 $ 4 D T d t ä ö ñ ┤ ─ ╘ Σ ⌠
5 ENQ NAK % 5 E U e u à ò Ñ ╡ ┼ ╒ σ ⌡
6 ACK SYN & 6 F V f v å û ª ╢ ╞ ╓ µ ÷
7 BEL ETB ‘ 7 G W g w ç ù º ╖ ╟ ╫ τ ≈
8 BS CAN ( 8 H X h x ê ÿ ¿ ╕ ╚ ╪ Φ °
9 HT EM ) 9 I Y i y ë Ö ® ╣ ╔ ┘ Θ ∙
10 LF SUB * : J Z j z è U ¬ ║ ╩ ┌ Ω ·
11 VT ESC + ; K [ k { ï ø ½ ╗ ╦ █ δ √
12 FF FS , < L \ l | î £ ¼ ╝ ╠ ▄ ∞ ⁿ
13 CR GS - = M ] m } ì Ø ¡ ╜ ═ ▌ φ ²
14 SO RS . > N ^ n ~ Ä × « ╛ ╬ ▐ ε ■
15 SI US / ? O _ o ¦ Å ƒ » ┐ ╧ ▀ ∩ DEL

Os caracteres de 0 a 31 e o 127 são chamados caracteres de controle. Alguns são muito usados
como, por exemplo, os listados abaixo:
0 - NUL -- Caracter Nulo
7 - BEL -- Apito sonoro
8 - BS -- BackSpace - retorna um caracter à esquerda
10 - LF -- Line Feed - pula para a linha seguinte
12 - FF -- Form Feed - avança uma página da impressora
13 - CR -- Carriage Return - retorna para a primeira coluna da linha atual– tecla [ENTER]
27 - ESC -- Usado para várias funções como programação de tela e de impressora
Os demais caracteres de controle têm vários usos, como em transmissão de dados. O caracter 32
+ 0 = 32, SP é o espaço em branco.
Os 256 caracteres da tabela ASCII são os caracteres que formam os arquivos. Para criar um
arquivo texto, deve-se digitar o texto por meio de um editor, que pode ser o Bloco de Notas,
TextPad, Visual Studio ou o próprio editor do PyCharm. Um arquivo texto, portanto, é uma
sequência de caracteres. Os componentes de um arquivo texto são cada caracter individual do
arquivo.
O arquivo texto é também formado por linhas. Cada linha de texto termina com caracteres não
visualizáveis: geralmente os caracteres de controle CR e LF, que indicam o final da linha e, em
Python, podem ser representados pela sequência “\n”. Esses caracteres de controle, embora
armazenado nos arquivos, não são parte dos dados existentes no arquivo, e servem para que os
editores de texto saibam que uma linha do arquivo acabou e, assim, possam estruturar a maneira
como as linhas serão apresentadas na tela.
Para se trabalhar com os códigos e caracteres da tabela ASCII, usamos variáveis inteiras e
strings. Por exemplo, para obtermos o valor do código ASCII de um caracter, fazemos:

codigo = ord(‘a’) # devolve o valor 97


codigo = ord(caracter) # código ASCII do valor armazenado em caracter

Para, a partir do código ASCII, obtermos o caracter correspondente, fazemos da seguinte forma:

caracter = chr(código)
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 156

8.3. Processamento e Análise de Arquivos Texto


Atualmente existem muitas aplicações que analisam
textos digitados pelo usuário para tomar decisões, ou
mesmo leem esses textos de arquivos já existentes
para analisar seu conteúdo. Algums programas
editores de texto possuem uma opção que informa
características do arquivo aberto, como vemos na
figura ao lado, da opção Revisão | Contagem de
Palavras do Microsoft Word:
Também tem sido usada a linguagem Python para
efetuar Processamento de Linguagem Natural (PLN),
que é o tratamento de linguagens faladas ou escritas
por seres humanos quando eles se comunicam entre si.
Essa área está ligada à Inteligência Artificial, e Python possui um conjunto rico de bibliotecas que
atendem às necessidades do PLN. Uma dessas bibliotecas é a NLTK. Podem ser usadas para
extrair informações de arquivos contendo textos, mecanismos de buscas na Internet e em
programas de edição de textos, filtrar spam em e-mails, efetuar traduções de textos entre
diferentes linguagens humanas, análise de sentimentos e outras aplicações.
O link abaixo leva a um curso aprofundado nesses assuntos:
https://www.tutorialspoint.com/python_text_processing/
Vamos desenvolver algumas aplicações simples que leem e processam arquivos contendo textos.

8.3.1. Abertura de Arquivo


Um arquivo contendo um texto é organizado por linhas, sendo que cada linha é terminada por um
ou mais caracteres especiais que indicam ao sistema operacional que essa linha terminou e que
outra linha lhe segue.
Em geral, esses caracteres são conhecidos por End Of Line ou EOL, e cada sistema operacional
pode usar diferentes caracteres para indicar essa situação. Em Windows, por exemplo, sempre
que você digita um texto em um arquivo e tecla [Enter], são introduzidos no arquivo o par CR-LF
(Carriage Return – Line Feed) que indicam que essa linha acabou.
Quando um programa em Python lê um arquivo texto, cada leitura busca a próxima linha não-lida
do arquivo, e para de lê-la quando encontra o EOL.
Mas, antes de podermos ler um arquivo, precisamos informar ao programa qual o nome do
arquivo e em que local esse arquivo está armazenado. Ele pode estar armazenado na unidade de
armazenamento local, em uma unidade de rede, num dispositivo auxiliar como um pen-drive,
dentre outras várias possibilidades.
A unidade de armazenamento possui inúmeros arquivos e, portanto, todo arquivo em disco deverá
possuir um nome que o identifique de maneira única. Esse nome inclui a unidade de disco e a
sequência de pastas que se deve seguir para encontrar o local do disco onde esse arquivo está
armazenado. Após isso, juntamos o nome do arquivo propriamente dito e, com todas essas
informações, podemos informar um programa onde o arquivo desejado se encontra, e o programa
poderá acessá-lo para leitura de seus dados ou escrita de novos dados.
Essa operação é chamada de Abertura de Arquivo e, sem ela, o nome e local do arquivo não
serão conhecidos e o programa não terá como acessá-lo para posterior processamento.
Há alguns modos de abertura do arquivo. Em geral, um arquivo pode ser aberto para entrada ou
para saída:
• Abrir um arquivo para entrada: significa que desejamos acessar o arquivo para ler suas
linhas para serem processadas.
• Abrir um arquivo para saída: significa que desejamos acessar o arquivo para nele escrever
linhas, gerando o conteúdo de um arquivo.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 157

Para abrirmos um arquivo, usamos o método open(), que tem a seguinte forma geral:

variavelDeArquivo = open(r”caminho e nome do arquivo”, “modo de acesso”)

A letra “r”, colocada antes do “caminho e nome do arquivo” evita que caracteres antecedidos por
“\” sejam considerados como caracteres de sequência de escape. “c:\temp\new.txt” terá os
caracteres “\t” considerados como um caractere de tabulação e \n como o caractere de mudança
de linha (new line) e o sistema operacional procuraria por um arquivo chamado
“c: emp
ew.txt”
Já r”c:\temp\new.txt” indicará ao interpretador Python para ignorar sequências de escape dentro
dessa string, de forma que o sistema operacional abriria o arquivo new.txt da pasta temp da
unidade C do computador.
A variavelDeArquivo representa um objeto tratador de arquivo (file handler), que permite ao
Python encontrar o arquivo no local de armazenamento e, também, processá-lo. O tratador de
arquivo possui uma variável interna que informa em que ponto do arquivo está o processamento
(no início, no fim, em uma das linhas intermediárias) e essa variável é chamada de ponteiro do
arquivo. Sempre que lermos ou escrevermos algo num arquivo, o ponteiro muda de lugar, indo
para o ponto seguinte de leitura ou escrita.
variavelDeArquivo será usada, depois da abertura do arquivo, para acessar o arquivo e, através
dele, a unidade de armazenamento, para ler e/ou escrever dados.
“modo de acesso” informa se a abertura é para entrada ou para saída. A seguir vemos os seis
modos de acesso que Python prevê:

Modo Uso
“r” Read Only (somente leitura) – abre o arquivo para leitura (entrada de dados). O ponteiro
do arquivo fica posicionado logo em seu início. É o modo padrão de abertura. Se o
arquivo cujo nome foi especificado não existir, ocorre um erro de I/O (Input/Output
error).
“w” Write Only (somente escrita) - abre o arquivo para escrita (saída de dados). Se o
arquivo cujo nome foi especificado não existir, ele é criado vazio. Caso já exista, seu
conteúdo anterior é apagado.
“a” Append Only (somente anexo) – abre o arquivo para escrita (saída de dados). Se o
arquivo cujo nome foi especificado não existir, ele é criado vazio. Caso já exista, seu
conteúdo anterior não é apagado e as novas escritas ocorrem após o que já estava
escrito anteriormente. O ponteiro do arquivo é posicionado ao final do arquivo.
“r+” Read and Write (leitura e escrita) - abre o arquivo para leitura e escrita (entrada e saída
de dados). O ponteiro do arquivo fica posicionado logo em seu início. Se o arquivo cujo
nome foi especificado não existir, ocorre um erro de I/O (Input/Output error).
“w+” Write and Read (escrita e leitura) – abre o arquivo para escrita e leitura (saída e entrada
de dados). Se oi arquivo não existir, ele será criado vazio. Para um arquivo já existente,
os dados são apagados. O ponteiro do arquivo fica posicionado no início do arquivo.
“a+” Append and Read (anexar e leitura) - abre o arquivo para anexação ao final e leitura. Se
o arquivo indicado não existir, será criado vazio. Se existir, o ponteiro do arquivo será
posicionado ao seu final, de forma que novas escritas não apagarão o que já estiver
anteriormente gravado no arquivo.
Assim, para abrirmos arquivos usaremos os comandos como os abaixo:
Abre arquivo texto.txt da
pasta temp do disco C
arquivoDeEntrada = open(r”c:\temp\texto.txt”,”r”) para leitura de suas
linhas
Cria o arquivo relatorio.txt
na pasta sistema do
disco D para escrita de
linhas
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 158

arquivoDeSaida = open(r”d:\sistema\relatorio.txt”,”w”)
arquivoContinuado = open(r”c:\temp\texto.txt”,”a”) Abre arquivo texto.txt da
pasta temp do disco C
para escrever linhas após
seu final anterior
8.3.2. Fechamento de Arquivo

Depois que um arquivo foi processado (tanto para escrita quanto para leitura) e não mais será
necessário, é importante fechá-lo, usando o método close() do tratador de arquivo:

arquivoDeEntrada.close()
arquivoDeSaida.close()
arquivoContinuado.close()

O método close() faz com que o arquivo deixe de ser disponível ao programa. Para acessá-lo
novamente, ele deverá ser reaberto.
Esse método faz com que o espaço de memória usado para armazenar dados transferidos de e
para o arquivo seja liberado, de forma que o sistema operacional possa reaproveitar esse espaço.
É muito importante fechar arquivos abertos em modo de escrita ou anexação (“w” ou “a”). Caso
arquivos assim abertos não sejam fechados, o último bloco de dads dados recém-gravados serão
perdidos, pois close() faz com que dados do arquivo que ainda estejam na memória principal do
computador sejam gravados na unidade de armazenamento. Se não se fechar o arquivo, esses
dados poderão não ser transferidos para a unidade e, portanto, serão perdidos.
Um arquivo de entrada que não seja fechado poderá não ficar disponível para outros programas
acessá-lo.
Portanto, sempre é importante fechar arquivos, tanto de entrada quanto de saída, quando não
mais forem necessários para o programa.

8.3.3. Leitura de Arquivo Texto

Há três métodos para leitura de linhas de um arquivo texto. Eles fazem com que uma ou mais
linhas do arquivo sejam trazidos para a memória e armazenados em variáveis como strings:

1. read() – retorna todo o arquivo em formato de string, contendo caracteres “\n” indicando
finais de linha. Se esse método for chamado com um argumento inteiro, o valor desse
argumento informará o número máximo de bytes que será lido. Geralmente, cada caracter
ocupa dois bytes.
stringComTudo = arquivoDeEntrada.read()

2. readline() – lê uma única linha do arquivo e a retorna como uma string. Se um argumento
inteiro for fornecido, lerá, no máximo, a quantidade de bytes igual a esse inteiro. No
entanto, não lerá mais de uma linha, mesmo que o argumento inteiro exceda o tamanho da
linha atual a ser lida. Essa string poderá ser processada posteriormente.
stringComUmaLinha = arquivoDeEntrada.readline()

3. readlines() - lê todas as linhas do arquivo texto e as armazena em uma lista de strings.


listaDeStrings = arquivoDeEntrada.readlines()

8.3.4. Descobrindo que o arquivo acabou de ser lido

Algumas linguagens de programação possuem um método ou uma propriedade para informar que
o ponteiro do arquivo chegou ao final do arquivo após uma sequência de leituras. No entanto,
Python não tem um método com essa função.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 159

Em Python, caso estejamos lendo um arquivo texto linha a linha, dentro de uma repetição, será
retornada uma string vazia (“”) quando se chegar ao final do arquivo.
Já uma linha em branco existente no interior do arquivo não acarretará o retorno da string vazia e
sim de um caractere de final de linha “\n”.
Portanto, o trecho abaixo lê um arquivo texto e exibe seu conteúdo na tela console:

arquivo = open(r"c:\temp\poema.txt", "r")


quantasLinhas = 0
linha = "-" # para linha não começar como string vazia
while linha != "" : # enquanto o arquivo não acabou
linha = arquivo.readline()
if linha != "" :
print(linha, end="")
quantasLinhas = quantasLinhas + 1

arquivo.close()
print(f"\nForam lidas {quantasLinhas} linhas")

O arquivo de dados e o resultado escrito na tela vêm abaixo:

Arquivo texto com as linhas processadas Exibição do resultado na tela console

No programa acima, observe a abertura do arquivo para leitura, seu processamento linha a linha
enquanto a linha lida é diferente de cadeia vazia e o fechamento do arquivo ao final do
processamento.
A cada vez que uma linha foi lida (inclusive linhas em branco), a variável quantasLinhas foi
incrementada, para contar quantas linhas esse arquivo tem. O valor dessa variável foi exibido
após o final do arquivo ter sido encontrado, ou seja, após o while terminar e o arquivo ter sido
fechado.
Observe também que a palavra lã teve a letra ã escrita de uma maneira estranha na tela console.
Teremos de resolver esse problema, que tem a ver com a configuração de local do computador.

8.3.5. Escrita em Arquivo Texto

Há dois métodos para escrever linhas em um arquivo texto:

1. write() – insere no arquivo texto uma única linha com a string passada como argumento:

arquivoDeSaida.write(“Linha a ser inserida”)

arquivoContinuado.write(variavelComLinhaAInserir)

2. writelines() – é usado para escrever no arquivo texto múltiplas linhas de uma única vez.
Escreve no arquivo todas as linhas presentes em uma lista de strings:

arquivoDeSaida.writelines(variavelDeListaDeStrings)

arquivoContinuado.writelines([“Maria”, ”tinha”, ”um”,”cachorrinho”])


COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 160

O programa a seguir lê um arquivo texto e escreve no arquivo de saída apenas as linhas que
iniciam com uma vogal:

arquivoDeEntrada = open(r"c:\temp\poema.txt", "r")


arquivoDeSaida = open(r"c:\temp\saida.txt","w")
linha = "-" # para linha não começar como string vazia
while linha != "" :
linha = arquivoDeEntrada.readline()
if linha != "" :
if linha[0].upper() in "AEIOU":
arquivoDeSaida.write(linha)
arquivoDeEntrada.close()

Note que o arquivo de saída não foi fechado e, por isso,


o arquivo saída.txt ficou vazio. Caso tivéssemos
colocado o comando arquivoDeSaida.close(), esse
arquivo teria seus dados transferidos da memória e
gravados na unidade de armazenamento, ficando como
na figura abaixo, onde vemos gravada a única linha do
arquivo de entrada que começa com vogal:

Exercícios
8.1. Faça um programa em Python que leia um arquivo texto, cujo conteúdo é uma página de
um livro, e conte quantas vogais, quantos símbolos diferentes de letras, quantas palavras e
qual o tamanho da maior palavra que existem no texto, além de qual o número de palavras
que terminam com vogal.
Como o arquivo está em disco, o usuário não verá esse texto, a menos que seu programa
exiba cada caracter lido, enquanto processa os caracteres do texto. Portanto, você deve
providenciar que isto ocorra.
Uma palavra é um conjunto de caracteres que começa com uma letra e termina com um
caracter que não é letra. Este caracter de terminação pode ser um final de linha, um
espaço em branco, uma vírgula, etc.

Enquanto não é fim de arquivo


Enquanto não é fim de linha
Leia caracter do arquivo e escreva-o na tela
Verifique se é palavra
Leia os demais caracteres até terminar a palavra
Conte esta palavra
Veja o tamanho dela e compare com a maior até agora
A cada leitura verifique se o caracter lido é vogal
No final de linha, pule de linha na tela e leia nova linha do arquivo
8.2. Faça um programa em C que codifique um texto lido de um arquivo, usando a seguinte
chave de criptografia:
ZENIT
POLAR
Ou seja, todos os caracteres Z na entrada deverão ser trocados por um P na saída, e
assim por diante. O programa, portanto, deve gerar um arquivo de saída, com a mesma
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 161

estrutura do de entrada, mas codificado. O arquivo também deve ser exibido na tela. Além
disso, a tela deve ser emoldurada, e quando o texto exibido chegar à linha 23, a moldura e
seu conteúdo devem ser apagados, e o texto deve continuar a ser exibido.
A exibição do texto deve ser feita da seguinte forma: numa linha, os caracteres lidos do
arquivo, e abaixo de cada um deles, o caracter codificado. Exemplo:

Em ambos os programas, nao se pode usar quaisquer elementos


Om imbes es ztegtimis, lie so zedo usit quiasquot onomolres
da linguagem nao mencionados em aula ou apostila até agora.
di nolguigom lie molcailides om iuni eu izesrani iró igeti.
8.3. Faça um programa em Python que leia um arquivo texto e o processe, verificando:
a. quantas palavras começam com vogal e terminam com consoante;
b. quantas palavras existem no texto;
c. qual o tamanho da maior palavra encontrada.
O texto lido deve ser exibido na tela, levando-se em conta finais de linha e de arquivo.
Quando se escrever 20 linhas, deve-se parar a exibição. Após isso, peça para o usuário
pressionar [Enter] e recomece o processamento. Cada palavra que tenha mais de 2 letras
deve ter suas letras minúsculas a partir da terceira letra escritas em maiúsculo na tela.
8.4. Faça um programa em Python que leia um arquivo texto previamente digitado e o escreva,
de 16 em 16 caracteres, à esquerda da tela. À direita da tela, haverá um espaço de 64
colunas, onde se deve escrever o código ASCII de cada caracter escrito à esquerda, como
no exemplo abaixo. Você pode usar duas strings para preparar cada lado do resultado,
uma para o lado esquerdo (texto) e outra para o lado direito (códigos ASCII). Use
formatação de strings para escrever os códigos.
Maria tinha um ca 077 097 114 105 097 032 116 105 110 104 097 032 117 109 032 099 097
rneirinho. O carn 114 110 101 105 114 105 110 104 111 046 032 079 032 099 097 114 110
eirinho de Maria. 101 105 114 105 110 104 111 032 100 101 032 077 097 114 105 097 CR
.era branco como LF 101 114 097 032 098 114 097 110 099 111 032 099 111 109 111 032
a neve. 097 032 110 101 118 101 EOF

Note que os caracteres não representáveis na tela foram colocados como pontos à
esquerda da tela e como palavras entre os códigos, à direita.
Quando se chegar a 16 caracteres numa linha da tela, deve-se pular para a linha seguinte
da tela. Os caracteres não-representáveis devem ser escritos, também.

8.5. Caixa de Diálogo para abertura de arquivo


Podemos usar o módulo TKinter, que possui controles de
interface gráfica com o usuário (janelas, botões, caixas de
texto, etc.), para exibir uma janela que solicita ao usuário a
seleção de uma pasta e nome de arquivo, como fazemos
em vários programas quando executamos a opção de abrir
arquivo.
Possilvemente o TKinter já foi instalado quando foi feita a
instalação do gerenciador de pacotes Miniconda.
Mas, caso seja necessário instalar o módulo TKinter abra o
terminal do Miniconda e, nele, digite o comando conda
install tk [Enter].
O comando conda list digitado no terminal do Miniconda
mostra a relação de pacotes instalados.
Depois de se certificar que o TKinter foi instalado, vamos criar um programa que abre um arquivo
e o exibe na tela Console do PyCharm.
A classe filedialog do TKinter possui métodos para criar e exibir janelas de seleção de pastas e
arquivos.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 162

O método askopenfile() dessa classe é usado para solicitar a pasta e nome de um arquivo para
ser aberto. Note que esse método não abre o arquivo, apenas permite a busca do nome do
arquivo que se deseja abrir, usando uma interface gráfica com o usuário. Ele possui vários
argumentos, que relacionamos abaixo:
title: título da janela de seleção de arquivos
initialdir: pasta no qual a caixa de diálogo começa a busca do arquivo
initialfile: nome do arquivo que se deseja estar selecionado ao abrir a caixa de diálogo
filetypes: uma tupla contendo os tipos de arquivos desejados, no formato (“rótulo”,
“padrão”)
defaultextension: extensão padrão para anexar a arquivos em caixas de diálogo para
salvar
multiple: se True, a seleção de vários arquivos é permitida; se False, só um arquivo
poderá ser selecionado

Uma tupla é o nome que se dá, em Python, a uma coleção de valores colocados entre ( e ).
Tuplas são imutáveis, ou seja, não se pode alterar seu conteúdo.
from tkinter import filedialog Uma tupla é uma coleção de
def Converter(): itens colocados entre ( e ). Não
pode ser alterada.
tiposDeArquivos = ( Cada item dessa tupla segue o
('Arquivos de texto', '*.TXT'), formato (“rótulo”, padrão”)
('Arquivos JSON', '*.json'),
('Qualquer arquivo', '*.*')
)
nomeDoArquivo = filedialog.askopenfilename(title = 'Selecione o arquivo',
initialdir = r"c:\temp",
multiple = False,
filetypes = tiposDeArquivos)
if nomeDoArquivo != "" : # não se pressionou [Cancelar]
arquivoDeEntrada = open(nomeDoArquivo, "r")
...
arquivoDeEntrada.close()

Abaixo vemos a caixa de diálogo criada no código acima. Se não se selecionar arquivo
(pressionar o botão [Cancelar]), nomeDoArquivo ficará com uma string vazia (“”).

filedialog.asksaveasfilename() solicita a pasta e o nome de um arquivo para gravação (saída).


Funciona de maneira semelhante a askopenfile().
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 163

8.6. Leitura de números em arquivos texto

Após abrir um arquivo para leitura, você pode ler seus dados. Se cada linha contiver um número,
inteiro ou real, você poderá converter a linha lida (uma string) contendo esse os dígitos desse
número para o tipo numérico desejado, como vemos abaixo:

quantosValores = int(arquivo.readline()) 7
soma= 0.0
12.04
for i in range( 0, quantosValores, 1):
umValorReal = float(arquivo.readline())
-7.25
soma += umValorReal 4.8
print(f”A soma dos valores do arquivo é {soma}”) 10.0
arquivo.close() 6.52
5128.4945
213.5

O trecho acima lê uma linha de um arquivo texto já aberto. Nessa linha, há um número inteiro que
informa quantas linhas do arquivo, contendo valores reais, serão lidas em seguida. A primeira
linha lida é convertida para um valor inteiro, armazenado na variável quantosValores.
O comando for continua a leitura do arquivo texto, lendo uma linha por repetição, tantas vezes
quanto o número de valores informado pela variável quantosValores. Cada linha lida é convertida
para um valor real, armazenado na variável umValorReal que, logo em seguida, é acumulado na
variável soma.
Ao final do for, essa variável conterá a somatória dos “quantosValores” lidos do arquivo.

8.7. Estudo dirigido: conversão de texto para fala


Artigo original em https://www.geeksforgeeks.org/convert-text-speech-python/

Há várias APIs disponíveis para converter texto para fala em Python. Uma dessas APIs é a API do
Google Text to Speech comumente conhecida como a API gTTS.
gTTS é uma ferramenta muito fácil de usar
que converte o texto inserido, em áudio que
pode ser salvo como um arquivo mp3.
A API do gTTS suporta vários idiomas,
incluindo inglês, hindi, tâmil, francês, alemão
e muitos outros. A fala pode ser feita em
qualquer uma das duas velocidades de
áudio disponíveis, rápida ou lenta.
Entretanto, a partir da última atualização,
não é possível alterar a voz do áudio
gerado.
Para instalar a API gTTS abra o terminal do
VSCode:

Digite pip install gTTS e [Enter].

No PyCharm, abra a janela de Packages (pacotes) e busque gtts, clique duas vezes no item que
aparecer subordinado a conda, ou clique em [Install with conda]:
Agora vamos codificar um programa que lê um arquivo texto e o converte para fala.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 164

# Importe o módulo necessário para conversão de texto para fala


from gtts import gTTS
from playsound import playsound
from tkinter import filedialog

def converter():
tiposDeArquivos = (
('Arquivos de texto', '*.TXT'),
('Arquivos mp3', "*.mp3"),
('Qualquer arquivo', '*.*')
)

nomeDoArquivo = filedialog.askopenfilename(
title = 'Selecione o arquivo',
initialdir = r"c:\temp",
initialfile=r"poema.txt",
multiple = False,
filetypes = tiposDeArquivos)

if nomeDoArquivo != "" : # não se pressionou [Cancelar]


arquivoDeEntrada = open(nomeDoArquivo, "r")
linhasDeTexto = arquivoDeEntrada.read()
arquivoDeEntrada.close()

# instancia um objeto de conversão usando as linhas lidas do arquivo


# e já realiza a geração do áudio nesse objeto
linguagem = "pt-br"
conversor = gTTS(text=linhasDeTexto, lang=linguagem, slow=True)

# salva o áudio gerado em um arquivo mp3


conversor.save("convertido.mp3")

# reproduz o áudio obtido da conversão de texto para fala


playsound("convertido.mp3")

if __name__ == '__main__':
converter()

Digite e execute esse programa, e ouça o programa “declamar” o poema presente no arquivo.

Clique aqui para acessar uma página com um poema de verdade. Copie sua versão em inglês
num arquivo texto, gere e ouça sua versão em áudio na língua inglesa, usando “en” como
informação de linguagem.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 165

9.1. Aplicações Matemáticas da Contagem


9.
Solução de Relação de Divisores de um número natural

Problemas Imaginemos que nosso usuário deseja saber quais são os


divisores de um número natural N qualquer. Sabemos, da
Matemáticos Matemática do Ensino Fundamental, que números naturais
são os inteiros maiores que zero e que todo número natural
possui ao menos dois divisores: 1 e o próprio número.
Os possíveis divisores de N são todos os números naturais da sequência de 1 até N, ou seja,
números inteiros i, onde 1 <= i <= N, poderão ser divisores de N. Sabemos, por outro lado, que
nem todos os números nessa sequência, em geral, serão divisores de N. Somente serão divisores
de N aqueles números naturais que atendam à seguinte propriedade, que é o critério de
divisibilidade:

Se resto (N / i) = 0, então i é divisor de N, onde 1 <= i <= N

Por exemplo, seja N igual a 18 (ou seja, nosso usuário digitou o valor 18 para saber quais são
seus divisores).
Os possíveis divisores de 18 são, assim 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 e 18.
Os divisores efetivos de 18 são os números naturais da sequência acima em que o resto da
divisão de 18 pelo número resulte em zero. Portanto, temos que verificar, para cada número da
sequência acima, se o resto da divisão de 18 pelo número é igual a zero. Se for, o número é
divisor de 18. Se o resto da divisão não for zero, o número testado não é divisor de 18.
Essa sequência pode ser gerada através de uma contagem, onde o contador varia de 1 a 18.
Podemos resumir o raciocínio de busca dos divisores na tabela abaixo:

i Quociente Resto da Divisão É divisor? Sucessor Novo i


N // i N%i Resto = 0 ?
1 18 0 Sim i+1=1+1 i=2
2 9 0 Sim i+1=2+1 i=3
3 6 0 Sim i+1=3+1 i=4
4 4 2 i+1=4+1 i=5
5 3 3 i+1=5+1 i=6
6 3 0 Sim i+1=6+1 i=7
7 2 4 i+1=7+1 i=8
8 2 2 i+1=8+1 i=9
9 2 0 Sim i+1=9+1 i = 10
10 1 8 i + 1 = 10 + 1 i = 11
11 1 7 i + 1 = 11 + 1 i = 12
12 1 6 i + 1 = 12 + 1 i = 13
13 1 5 i + 1 = 13 + 1 i = 14
14 1 4 i + 1 = 14 + 1 i = 15
15 1 3 i + 1 = 15 + 1 i = 16
16 1 2 i + 1 = 16 + 1 i = 17
17 1 1 i + 1 = 17 + 1 i = 18
18 1 0 Sim i + 1 = 18 + 1 i = 19
19 Fim do processo repetitivo

Acima, podemos ver que i é uma variável que foi sendo incrementada a cada iteração (ou a cada
repetição). Cada linha da tabela corresponde a uma repetição no programa, portanto.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 166

O valor de i a cada iteração (cada linha) foi usado como divisor para calcular o quociente e o resto
da divisão (resto = N % i). Se o resto calculado é igual a zero, concluímos que i é divisor de N.
Após cada verificação, incrementamos i para prepará-lo para a iteração (linha, repetição) seguinte.
Observe que, após a contagem alcançar a metade do número, só haverá um único divisor, o
próprio número.
Isso significa que podemos modificar um pouco nossa abordagem da solução do problema e
verificar apenas metade da sequência de possíveis divisores, pois sabemos que 1 e N sempre
serão divisores de N.
Codifique um programa que solicite ao usuário que digite um número inteiro natural e informe
quais são e quantos são os divisores desse número.

Verificação se um número natural é primo

Uma outra aplicação da contagem é verificar se um número inteiro é primo. Podemos usar a idéia
desenvolvida no exemplo anterior para essa verificação. Mas, antes, vamos relembrar o que é um
número primo.
A definição de número primo é:
[http://pt.wikipedia.org/wiki/N%C3%BAmero_primo]
Um número natural é um número primo quando ele tem exatamente dois divisores naturais
distintos: o número um e ele mesmo[1].
Existem infinitos números primos, como demonstrado por Euclides por volta de 300 a.C..[2]
A propriedade de ser um primo é chamada "primalidade", e a palavra "primo" também é utilizada
como substantivo ou adjetivo. Como "dois" é o único número primo par, o termo "primo ímpar"
refere-se a todo primo maior do que dois.
Se um número inteiro tem módulo maior que um e não é primo, diz-se que é composto. Por
convenção, os números 0, 1 e -1 não são considerados primos nem compostos.
O conceito de número primo é muito importante na teoria dos números.
[http://pt.wikipedia.org/wiki/Teoria_dos_n%C3%Bameros]
Um dos resultados da teoria dos números é o Teorema Fundamental da Aritmética, que afirma
que qualquer número natural diferente de 1 pode ser escrito de forma única (desconsiderando a
ordem) como um produto de números primos (chamados fatores primos): este processo se
chama decomposição em fatores primos (fatoração).
Assim, resumindo, podemos deduzir que:
• número N é primo quando possui apenas dois divisores: 1 e N.
• um número N é primo se ele não possui nenhum divisor entre 2 e N – 1.
• se um número N tiver qualquer divisor i, onde 2 <= i < N, então N não é primo.
As afirmações acima são equivalentes em termos de primalidade (ou não) de um número N
natural qualquer. Podemos usar essas afirmações para verificar se um número N qualquer é
primo.
Por exemplo, basta contarmos quantos divisores N possui entre 2 e N-1. Se essa contagem
resultar em 0, o número é primo. Ou, contamos quantos divisores N possui entre 1 e N. Se essa
contagem resultar em 2, o número é primo.
Os números inteiros entre 1 e N são possíveis divisores de N, como vimos no exemplo anterior.
Sabemos que nem todos os valores dessa sequência serão divisores efetivos. Na verdade, no
caso da solução do problema de verificar se um número é primo, o que desejamos é não
encontrarmos divisores entre 2 e N-1. Mas teremos de verificar cada um dos divisores da
sequência de 2 a N-1, pois não sabemos, a priori, quais são ou não divisores de N.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 167

Podemos melhorar um pouco a nossa lógica lembrando que 2 é o único primo par e que todos os
demais primos são ímpares. Assim, a tabela abaixo procura sistematizar o processo de
verificação:
Seja N igual a 17 (ou seja, nosso usuário digitou o valor 17 para saber se é primo)
Os possíveis divisores de 17 são, portanto, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 e 17.
Sabemos, de antemão, que 1 e N são os únicos divisores permitidos de um número primo e,
portanto, não precisaremos testá-los. Também usaremos apenas potenciais divisores ímpares.
Assim, nossa sequência será 3, 5, 7, 9, 11, 13 e 15.
Os divisores efetivos de 17 serão os números naturais da sequência acima descrita em que o
resto da divisão de 17 pelo número resulta em zero. Portanto, temos que verificar, para cada
número da sequência acima, se o resto da divisão de 17 pelo número é igual a zero. Se for, o
número é divisor de 17 e contamos esse divisor. Se o resto da divisão não for zero, o número
testado não é divisor de 17. Podemos resumir da seguinte maneira:
i Quociente Resto da Divisão É divisor? Quantos Sucessor Novo i
N div i N mod i Resto = 0 ? Divisores
3 5 2 Não 0 i+2=3+2 i=5
5 3 2 Não 0 i+2=5+2 i=7
7 2 4 Não 0 i+2=7+2 I=9
9 1 8 Não 0 i+2=9+2 I = 11
11 1 6 Não 0 i + 2 = 11 + 2 I = 13
13 1 4 Não 0 i + 2 = 13 + 2 I = 15
15 1 2 Não 0 i + 2 = 15 + 2 I = 17
17 Fim do processo repetitivo
Como QuantosDivisores é igual a zero, isso significa que não achamos nenhum divisor entre 2 e
N-1 e, portanto, N é primo.
Já se N fosse igual a 21, teríamos a seguinte situação:
i Quociente Resto da Divisão É divisor? Quantos Sucessor Novo i
N div i N mod i Resto = 0? Divisores
3 7 0 Sim 1 i+2=3+2 i=5
5 4 1 Não 1 i+2=5+2 i=7
7 3 0 Sim 2 i+2=7+2 I=9
9 2 3 Não 2 i+2=9+2 I = 11
11 Fim do processo repetitivo

Como QuantosDivisores é igual a 2, isso significa que achamos dois divisores entre 3 e N-1 e,
portanto, 21 não é primo.
Nos processos acima, levamos em consideração que o número N é ímpar. Se N fosse par e
diferente de 2, ele não será primo.

Exercícios
9.1. Crie tabelas semelhantes às dos exemplos para relacionar os divisores de 14, 24, 37
9.2. Codifique um programa que leia um número natural e informe quais são os seus divisores.
9.3. Codifique um programa que leia um número natural e informe se ele é ou não primo.
9.4. Codifique um programa que exiba na tela todos os números primos entre 1 e um valor final
informado pelo usuário.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 168

9.2. Uma Classe de Problemas Matemáticos


Todos os problemas que tenham um algoritmo podem ser programados. Os problemas matemáti-
cos se enquadram nessa situação. Estaremos desenvolvendo uma classe chamada Matematica
que terá a capacidade de realizar as soluções de vários problemas, encapsulando-as e deixando-
as disponíveis para aplicações que delas necessitem.
A maioria dos problemas que estudaremos a seguir se referem a propriedades de números
inteiros: fatorial de um número inteiro, relação de divisores de um número inteiro, MDC entre um
número inteiro e outro, verificar se um número inteiro é palíndromo. Assim, podemos modelar uma
versão preliminar da nossa classe Matematica como se segue:

Matematica
numeroInteiro : inteiro

construtor Matematica(valorDesejado : inteiro)


função Fatorial() : inteiro
função Divisores() : lista de inteiros
função Mdc(outroNumero : inteiro) : inteiro
função EhPalindromo() : boolean

Crie uma nova aplicação no PyCharm usando o ambiente Conda. Chame-a de pyMatematica, na
pasta \tecnico\1oSemestre\TecPro\cap08.
Teremos de incorporar nesse projeto as classes Somatoria e Produtorio que desenvolvemos
anteriormente. Para isso, você pode abrir o projeto em que essas classes estão (por exemplo, o
projeto pySomatoria), informando o PyCharm para manter o projeto aberto em outra janela. Em
seguida, arraste os arquivos de classe desejados para a janela do novo projeto (pyMatematica),
até o nome do projeto na janela Project e os largue ai. Eles serão incorporados ao projeto atual.
Feche o projeto anterior e mantenha aberto apenas o novo projeto.

Você também pode copiar os arquivos de classe na pasta do novo projeto, usando o gerenciador
de arquivos do sistema operacional que está usando (por exemplo, Windows Explorer no
Windows e Finder no MacOS).
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 169

Na janela Project, adicione um novo arquivo ao projeto, chamada mat.py. Para isso, clique com o
botão direito no nome do projeto pyMatematica, clique em New | Python File. Digite o nome mat e
[Enter].
Com base no Diagrama de de Classes acima, a classe Matematica tem o atributo privativo
numeroInteiro. Esse atributo armazenará o número para o qual os diversos cálculos serão feitos
Nós o declaramos abaixo, bem como codificamos o construtor da classe:

class Matematica:
def __init__(self, valorDesejado):
self._numeroInteiro = valorDesejado

Note que colocamos um parâmetro no construtor da classe Matematica. Isso foi feito para que,
quando instanciarmos um objeto dessa classe, o construtor receba o valor que será armazenado
no atributo numeroInteiro. A aplicação fornecerá esse valor na instanciação de um objeto dessa
classe. Salve o arquivo dessa classe.
No arquivo do programa principal (main.py), vamos criar um seletor de opções que permitirá ao
usuário escolher que operação deseja realizar. Apague os comentários e códigos que o PyCharm
colocou automaticamente acima do comando if que dá início à execução desse móduolo e digite o
código abaixo, fazendo as correções necessárias:

import os

def seletor():
numeroDigitado = 0
opcao = 1
while opcao != 0:
os.system('cls') or None
print("Operações disponíveis:")
print("1 - Fatorial")
print("2 - Lista de Divisores")
print("3 - MDC de 2 inteiros")
print("4 - Número palíndromo\n")
opcao = int(input("Digite sua opção:"))
match opcao:
case 1:
fazFatorial()
case 2:
fazDivisores()
case 3:
fazMDC()
case 4:
fazPalindromo()

if __name__ == '__main__':
seletor()

Note que seguimos o padrão de seletor de opções que temos usado, sem nos importarmos com
posicionamento de textos em posições específicas da tela, apenas para focarmos no
desenvolvimento da classe Matematica.
Em cada uma das funções associadas a operações acima chamadas, faremos a leitura do valor
desejado para o cálculo realizado pela operação associada, e o armazenaremos na variável
numeroDigitado. O resultado de cada operação também será mostrado ao final da execução de
cada uma dessas funções. Dessa forma, a interação com o usuário ficará sob responsabilidade do
módulo principal, e não da classe Matematica que, por sua vez, ficará responsável pelos
algoritmos de resolução de cada um dos problemas que ela resolve.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 170

Agora que já temos uma ideia aproximada de onde nossos dados serão digitados e exibidos,
vamos passar à discussão do primeiro problema matemático que solucionaremos e cujo algoritmo
encapsularemos na classe Matematica. O atributo numeroInteiro será usado como valor base para
os cálculos das diversas operações, ou seja, ele indicará qual valor deve ser usado para os
cálculos. O valor desejado para cada operação será lido no programa principal e será
transportado para o objeto da classe Matematica através de passagem de parâmetro para o
construtor da classe.
O primeiro problema, portanto, é o de cálculo de fatorial que, na verdade, já fizemos anteriormente
mas é interessante encapsular a solução em uma classe para deixa-la pronta e disponível para
quando for necessária.

O fatorial de um número inteiro positivo A é o produtório de todos os


Fatorial números inteiros entre 1 e A. Por definição, o fatorial de 0 e o fatorial de 1
valem 1.

O fatorial é uma operação aritmética usada em muitos cálculos matemáticos, como estatística,
cálculo de senos, cossenos e logarítmos, dentre outros usos. Todo número inteiro positivo possui
um fatorial. O enunciado acima informa que, para se calcular o fatorial de um número inteiro A,
deve-se efetuar o produtório entre todos os números inteiros entre 1 e A. Em outras palavras, o
fatorial de A corresponde a:

1 * 2 * 3 * … * (A-1) * A ∏𝐴𝑖=1 𝑖
Se A vale 7, o fatorial de A, portanto, é igual ao produtório dos inteiros entre 1 e 7, ou seja, 1 * 2 *
3 * 4 * 5 * 6 * 7, que resulta em 5040.
Como, na classe Matematica, usamos o atributo numeroInteiro como base dos cálculos
efetuados, para resolver o fatorial usando essa classe precisaremos gerar todos os valores
inteiros entre 1 e numeroInteiro, para usá-los no cálculo do seu produtório.
Portanto, para gerá-los usaremos os conceitos aprendidos e praticados sobre contagem. A
geração é feita através de um contador, que gera os números de 1 a numeroInteiro enquanto faz a
contagem:

...
contador = 1
while contador <= self._numeroInteiro:
# aqui usa o número gerado por contador para
# o que for necessário
contador+=1 # gera o próximo número

...

Poderemos alternativamente usar um comando for para realizar a contagem, como no trecho
abaixo:
...
for contador in range(1,self._numeroInteiro+1, 1):
# aqui usa o número gerado por contador para
# o que for necessário

Os trechos acima, portanto, usam uma variável inteira contador para gerar cada um dos números
que usaremos para compor o produtório, através de sucessivas multiplicações por cada um dos
números gerados pelo cntador. Ao final da geração dos números e do cálculo do produtório,
temos calculado o valor do fatorial de _numeroInteiro.
Isso é feito abaixo:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 171

def fatorial(self):
oFatorial = prod.Produtorio()
for contador in range(1,self._numeroInteiro+1, 1):
oFatorial.multiplicar(contador)

return oFatorial.valor

Voltando para o módulo principal main.py, vamos codificar a função fazFatorial(), que será
responsável por solicitar o valor cujo fatorial se deseja calcular e exibir esse resultado, após ter sido
calculado pela classe Matematica.

def fazFatorial():
numeroDigitado = int(input("Número para calcular fatorial: "))
if numeroDigitado > 0 :
meuMat = mat.Matematica(numeroDigitado)
fat = meuMat.fatorial()
print(f"Fatorial de {numeroDigitado} vale {fat}")
tecla = input("Pressione [Enter] para retornar ao seletor")

A função acima deve ser digitada após o final da função seletor, fora dela, sem indentação.
O valor digitado foi convertido de string para inteiro e armazenado na variável numeroDigitado. Em
seguida, o valor dessa variável foi usado como parâmetro do construtor do objeto meuMat da classe
Matematica, que encapsula, além do número inteiro que será base das operações, as próprias
operações que serão feitas com esse valor.
A chamada ao construtor acima, além de instanciar na memória um objeto da classe Matematica,
também armazenará no atributo _numeroInteiro desse objeto o valor numérico passado como
argumento ao construtor.
Após obter-se o valor digitado e criar-se o objeto que possui as funcionalidades para tratá-lo, temos
que chamar o método que calcula e retorna o fatorial desse valor, exibindo o resultado logo em
seguida.
O trabalho de gerar todos os números inteiros entre 1 e o valor digitado, para depois usá-los no
cálculo do seu produtório já foi feito na classe Matematica por um contador no comando for.
Assim, basta pedirmos ao ao objeto meuMat que execute o método-função fatorial(), que calcula e
retorna o fatorial do valor que está armazenado no atributo numeroInteiro do objeto umNumero:
Portanto, nossa classe Matematica está, no momento, com a codificação abaixo:
import prod
import soma

class Matematica:
def __init__(self, valorDesejado):
self._numeroInteiro = valorDesejado

def fatorial(self):
oFatorial = prod.Produtorio()
for contador in range(1,self._numeroInteiro+1, 1):
oFatorial.multiplicar(contador)

return oFatorial.valor

Conforme estudarmos os outros problemas, iremos introduzindo suas soluções nessa classe e,
quando necessário, aprimoraremos a codificação anterior. Em paralelo, adicionaremos novas
funcionalidades ao nosso módulo principal, num tratamento simultâneo entre a classe de solução
dos problemas matemáticos o programa que interage com o usuário.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 172

A seguir, temos outro problema que também envolve contagem e geração de valores.

Deseja-se listar todos os números divisores de um número inteiro


Divisores de um
maior que zero, que será fornecido pelo usuário. Um número é
número inteiro
divisor de outro quando o resto da divisão resulta em zero.

Na matemática do Ensino Fundamental aprendemos que todo número inteiro é divisível por 1 e
por ele mesmo. Assim sendo, já conhecemos esses dois divisores de qualquer número. No
entanto, é necessário que verifiquemos todos os outros possíveis divisores entre 2 e a metade do
número fornecido. Por exemplo, se o número cujos divisores se deseja listar for 6, teremos de
listar, além do 1 e do próprio 6, os números 2 e 3. Se o número fornecido for 10, deveremos listar
1, 2, 5 e 10.
Note que o maior divisor de qualquer número é a sua metade, caso não consideremos o próprio
número.
Como nosso problema é descobrir os divisores do número fornecido, temos que testar todos os
numeros inteiros entre 1 e a metade do número. A princípio, o computador não tem como saber
que 3 não é divisor de 10, a menos que calcule o quociente e o resto da divisão de 10 por 3.
Como o resto dessa divisão não é nulo, 3 não divide 10 e, portanto, 3 não será listado como
divisor. O mesmo ocorre com o número 4. Já com 2 e 5, a divisão é exata, de modo que esses
dois valores deverão ser listados.
Portanto, teremos que gerar todos os números entre 1 e a metade do número fornecido e, para
cada um desses números gerados, calcular o quociente e o resto da divisão.
De modo geral, um algoritmo para realizar essa solução teria o seguinte aspecto:

Algoritmo Divisores;
1. Inicio
2. Ler(numeroDesejado);
3. Gerar valores dos possíveis divisores de 1 até a metade do numerodesejado
4. Calcular o quociente e o resto da divisão de numeroDesejado pelo
possível divisor gerado
5. Se o resto é igual a zero, listar o possível divisor gerado
(pois ele é um divisor exato)
6. Repetir os passos 3, 4 e 5 enquanto há possíveis divisores
7. Fim.

Não podemos, simplesmente, digitar essas linhas num computador e esperar que ele as execute.
Por exemplo, ele não sabe o significado de gerar valores. Isso tem que ser definido pelo
programador usando comandos que o computador entenda.
No caso acima, gerar os valores dos possíveis divisores indica que temos de ter um contador,
cujos valores comecem em 1 e prossigam até a metade do número, pois sabemos o próprio
número já é seu divisor naturalmente. Isso evitará repetições desnecessárias.
Portanto, para gerarmos os valores dos possíveis divisores do número fornecido pelo usuário,
temos que ter um contador que varia de 1 até a metade do número desejado. Isso é feito abaixo,
como um método da classe Matematica:

def divisores(self):
metadeNumero = self._numeroInteiro // 2
for possivelDivisor in range(1, metadeNumero+1, 1):
# usa valor do possivelDivisor no que precisar

Já a divisão e o cálculo do resto de um número inteiro (A) por outro (B) é feita como abaixo:

A B q = parte inteira de A / B
r r=A–q.B
q
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 173

quociente = A // B
No programa escreveremos: resto = A - quociente * B

O operador "//", quando aplicado a valores inteiros, gerará um resultado inteiro da divisão, ou seja,
apenas o valor inteiro do quociente. Para calcularmos o resto da divisão, usamos a fórmula
apresentada no quadro acima ou o operador %. Assim, já sabemos calcular o quociente e o resto
da divisão do numeroInteiro por possivelDivisor:
def divisores(self):
metadeNumero = self._numeroInteiro // 2
for possivelDivisor in range(1, metadeNumero+1, 1):
quociente = self._numeroInteiro // possivelDivisor
resto = self._numeroInteiro – quociente * possivelDivisor

Em seguida, o algoritmo nos diz que se o resto dessa divisão for nulo (igual a Zero), então
possivelDivisor contém realmente um divisor exato do _numeroInteiro. Portanto, nesse caso o
divisor deveria constar da lista de divisores a ser exibida pelo programa.

def divisores(self):
metadeNumero = self._numeroInteiro // 2
for possivelDivisor in range(1, metadeNumero+1, 1):
quociente = self._numeroInteiro // possivelDivisor
resto = self._numeroInteiro - quociente * possivelDivisor
if resto == 0:
print(f"{possivelDivisor}")

Após cada ciclo do for, geramos o próximo valor do contador possivelDivisor, como explicado
anteriormente e seguimos o fluxo de execução, repetindo a operação de cálculo de quociente e
resto até que o valor do contador possivelDivisor fique maior que a metade do número inteiro.
No entanto, veja que acima estamos escrevendo na tela o valor do divisor encontrado. Sabemos
que isso é o que o programa de aplicação fará, mas fica a pergunta: o objeto Matematica deveria
realizar esse tipo de tarefa que é específica de um programa? Não seria melhor que o objeto
enviasse uma lista de valores sem se importar o que será feito com eles? Afinal de contas,
estamos desenvolvendo uma aplicação que poderia não ser de interface console, e sim de
interface gráfica ou mobile. Também já estudamos que não é aconselhável que as classes de
solução de problemas se prendam a detalhes de tipo de Interface com Usuário.
Foi por esse motivo que o método-função Divisores dessa classe no diagrama tem o tipo Lista de
Inteiros, porque ele retornará uma coleção composta pelos divisores encontrados. Uma lista
de inteiros é uma coleção de valores inteiros e aprenderemos a usá-la agora.
Conforme formos encontrando novos divisores do numeroInteiro, deveremos adicioná-los a essa
lista na sequência em que forem descobertos, ou seja, sempre após o item adicionado
anteriormente. O método append de uma lista de inteiros faz essa adição. Ao final da procura
pelos divisores, todos os valores de divisores estariam armazenados na lista, um após o outro, e
poderão ser tratados tanto coletivamente quanto individualmente.
Para usarmos uma lista de inteiros, precisamos declarar uma variável de tipo lista, que fazemos
como abaixo:

def divisores(self):
listaDeDivisores = [] #lista vazia

Agora, deveremos modificar o comando If dentro do for para:

if resto == 0:
listaDeDivisores.append(possivelDivisor)
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 174

Assim, não mais estamos escrevendo na tela console o valor do divisor detectado, e sim o
agrupando numa variável composta, essa listaDeDivisores.
Por enquanto, vamos apenas imaginar que ela atenderá nossas necessidades.
Assim, não estamos escrevendo na tela console nem em um formulário de interface gráfica ou
widget mobile, pois não é responsabilidade da classe de solução de problemas tratar da interface
com o usuário.
Esse método retornará a listaDeInteiros para o aplicativo, e este se encarregará de recuperar os
valores individuais dessa coleção e os apresentar na tela, da forma que tiver sido definida para
interface de usuário nesse aplicativo.
Assim, podemos já criar o método-função divisores() como se segue, dentro da classe
Matematica:

def divisores(self): []: especifica que a variável será uma lista de


quaisquer tipos de dados, dentre eles, inteiros.
listaDeDivisores = []
metadeNumero = self._numeroInteiro // 2
for possivelDivisor in range(1, metadeNumero+1, 1):
quociente = self._numeroInteiro // possivelDivisor
resto = self._numeroInteiro - quociente * possivelDivisor
if resto == 0:
listaDeDivisores.append(possivelDivisor)

listaDeDivisores.append(self._numeroInteiro)
append(valor) adiciona o valor ao final da lista de
return listaDeDivisores inteiros. Como a divisão atual é exata (tem resto
zero), adicionamos o divisor ao fim da lista.

A coleção de divisores foi


montada e é retornada à Como não repetimos a contagem até o próprio
aplicação. número (para o processo ser mais veloz) e o
próprio número é divisor de si mesmo o
adicionamos separadamente à lista de divisores

Se numeroInteiro fosse igual a 1150, a


0 1 2 3 4 5 6 7 8 9 10 11
coleção lista seria retornada com o
conteúdo visto ao lado: lista 1 2 5 10 23 25 46 50 115 230 575 1150

Veja que acabmos não usando o


quociente para qualquer ação que não seja o cálculo do resto. Sendo assim, podemos substituir os
dois comandos logo após o for por um único comando, no qual usamos o operador %, como vemos
abaixo:

for possivelDivisor in range(1, metadeNumero+1, 1):


resto = self._numeroInteiro % possivelDivisor
if resto == 0:
...

Voltando ao módulo principal, quando executarmos a operação de lista de divisores, deveremos ler
o número, instanciar um objeto da classe Matematica para que este chame o método divisores() e
guardar numa variável a lista de inteiros retornada por esse método. Em seguida, temos que exibir
na tela cada um dos divisores presentes na coleção retornada pelo método divisores(), um por linha.
Para isso, percorreremos sequencialmente a lista de inteiros retornada, acessaremos cada
número da coleção e o exibiremos com print(). O código que faz isso segue abaixo:
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 175

def fazDivisores():
numeroDigitado = int(input("Número para listar divisores: "))
if numeroDigitado > 0 :
meuMat = mat.Matematica(numeroDigitado)
listaDiv = meuMat.divisores()

print(f"Relação de divisores de {numeroDigitado}:")


for umDivisor in listaDiv:
print(f"{umDivisor}")

tecla = input("Pressione [Enter] para retornar ao seletor")

Execute agora o programa, escolha a opção


2 e digite 1150 como resposta à solicitação
do número cujos divisores se deseja listar.
A figura ao lado mostra o resultado dessa
execução:

Segue um novo problema, um pouco mais complicado:

O cálculo do MDC entre dois valores inteiros tem um algoritmo matemático descrito
pelo grego Euclides. Tal algoritmo envolve a divisão inteira entre dois valores,
digamos A e B, e o cálculo do resto dessa divisão. Se este resto for NULO, o MDC
MDC será o valor do divisor. Se for não nulo, então deve-se dividir o divisor anterior pelo
resto, e repetir-se o cálculo do resto dessa nova divisão. Da mesma forma que
antes, se o resto for não nulo, deve-se repetir o processo, com o resto passando a
ser o novo divisor e o dividendo sendo o divisor anterior.

Vamos dividir essa descrição em partes, para facilitar seu entendimento e também facilitar o
raciocínio de sua solução. Em primeiro lugar, temos dois valores inteiros, A e B, que devem ser
manipulados para encontrarmos o valor do MDC entre eles.
COTUCA - UNICAMP - Depto Processamento de Dados – Prof. Francisco Rodrigues - Técnicas de Programação I 176

Em segundo lugar, sabemos que essa manipulação entre A e B se dará por meio de uma divisão
e do resto dessa divisão. Assim, temos que uma das primeiras ações a fazer é calcular o
quociente e o resto da divisão.
quociente = A // B
resto = A - B * quociente

Em seguida, a descrição nos diz que se o resto for nulo (igual a zero), então o valor do MDC será
B. Caso contrário, devemos repetir a divisão e o cálculo do resto acima, mas usando B como
dividendo e o resto como divisor, como nos comandos abaixo: