TecPro - 08 03 2024
TecPro - 08 03 2024
1o Informática
1o Desenvolvimento de Sistemas
TÉCNICAS DE PROGRAMAÇÃO
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
0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1
É ó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
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
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
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
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:
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 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
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.
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
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
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
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().
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]:
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
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.
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
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.
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
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:
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.
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:
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.
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.
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
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 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
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:
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.
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:
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
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.
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:
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
Novamente pressionando [F7], iremos executar o comando que chama o método Somar do objeto
umaCalc:
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.
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:
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
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
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
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):
, 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:
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.
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:
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
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
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
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.
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:
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
Executando ExibirResultado():
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:
<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 == "/" :
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
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()
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 == "+" :
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 == "*" :
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.
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
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
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.
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
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.
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.
ax + b = 0
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.
umaCalc = calc.Calculadora()
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
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: "))
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.
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.
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 :
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
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
def EsperarEnter() :
espera = input("Tecle[Enter] para prosseguir")
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:
class Equacao1oGrau :
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:
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!")
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
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:
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.
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}")
Variável = Expressão
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
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:
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):
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.
2
3
6
8
9
11
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:
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:
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
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
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)
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
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
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.
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.
if __name__ == ‘__main__’ :
principal()
O nome da classe deve identificar para que ela serve. Por exemplo:
class EquacaoSegundoGrau :
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.
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.
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
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
if contador == 10 :
print 4 * 2 / 0.9
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()
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
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.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:
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()
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:
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:
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:
Podemos remover o
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 é:
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>
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.
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:
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 é!")
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
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:
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.
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():
Existe uma outra maneira de usar o comando for, para percurso em listas e vetores, que
aprenderemos posteriormente.
Exemplos:
1. Cálculo de fatorial
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.
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()
Exercícios
Baseados na apostila de C# da Caelum
Referência: https://www.caelum.com.br/apostila-csharp-orientacao-objetos/
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
9. Ocorrências de Loops;
1,0%
8. Cálculos, Somas,
Produtos; 9,4%
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%
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 ‘:
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:
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
b. Atribuição
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
d. Escrita no Console
print(cadeia)
e. Concatenação
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:
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:
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
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"
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
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 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
Outra possibilidade de percorrer e processar uma string é por meio do comando for:
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.
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]
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:
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:
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:
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:
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:
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:
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:
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:
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:
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
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
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
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 1
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:
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:
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:
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:
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.1. Somatória
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
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
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:
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.
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
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.
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.
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
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:
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
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:
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
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.
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:
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 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 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
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
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
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
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).
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
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:
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
Para abrirmos um arquivo, usamos o método open(), que tem a seguinte forma geral:
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.
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()
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.close()
print(f"\nForam lidas {quantasLinhas} linhas")
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.
1. write() – insere no arquivo texto uma única linha com a string passada como argumento:
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)
O programa a seguir lê um arquivo texto e escreve no arquivo de saída apenas as linhas que
iniciam com uma 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.
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:
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.
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 (“”).
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.
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:
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
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 __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
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:
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.
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
Matematica
numeroInteiro : inteiro
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 é 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.
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
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:
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.
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()
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: