Projeto Calculadora

O primeiro projeto do guia: uma calculadora que roda no terminal. Ela pede um número, a operação e outro número, mostra o resultado e pergunta se você quer fazer outra conta. A conta em si é a parte fácil: o Python já sabe somar. O desafio é fazer o programa não quebrar quando alguém divide por zero, escolhe uma operação que não existe ou digita "seis" no lugar de um número.

Você vai praticar

O que vamos construir

Esta é a calculadora pronta, rodando no computador da Ana. O que aparece depois dos dois-pontos de cada pergunta foi digitado por ela (o 10, o +, o s…); o resto foi o programa que escreveu:

$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15
Outra conta? (s/n): s
Primeiro número: seis
"seis" não é um número. Tente de novo.          « texto no lugar de número
Primeiro número: 6
Operação (+ - * /): %
Operação desconhecida: %                        « operação que não existe
Operação (+ - * /): *
Segundo número: 1.5
6 * 1.5 = 9
Outra conta? (s/n): s
Primeiro número: 2,5                            « com vírgula também vale
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.                   « divisão por zero
Outra conta? (s/n): s
Primeiro número: 0.1
Operação (+ - * /): +
Segundo número: 0.2
0.1 + 0.2 = 0.3
Outra conta? (s/n): s
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 3
10 / 3 = 3.333333
Outra conta? (s/n): n
Até logo!

Vamos chegar lá em quatro versões. No fim de cada uma, o programa roda; cada versão acrescenta uma coisa só:

  1. Dois números e um operador (input, float, if/elif).
  2. match e a divisão por zero.
  3. Repetir até a pessoa sair (while True e break).
  4. Funções e entrada validada (def, return, try/except).

Antes de começar

Você precisa saber criar e rodar um arquivo .py (Rodando um Programa), usar variáveis e f-strings. No seu computador, crie a pasta do projeto; o arquivo vai se chamar calculadora.py:

$ mkdir -p projetos/calculadora
$ cd projetos/calculadora

Em cada versão há também uma caixa Experimente, que roda Python de verdade aqui na página. Nas missões deste projeto, a caixa tem um campo Entrada, já preenchido com as respostas que o programa vai receber, uma por linha, como se alguém as digitasse (a missão diz quais são).


Antes da versão 1: o tropeço clássico

O jeito mais curto de pedir dois números e somar seria este:

print("=== Calculadora da Ana ===")
a = input("Primeiro número: ")
b = input("Segundo número: ")
print(f"{a} + {b} = {a + b}")
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Segundo número: 5
10 + 5 = 105

10 + 5 = 105? Não deu erro nenhum, mas a conta está errada. O motivo: o input() sempre devolve um texto (uma str), mesmo que a pessoa tenha digitado só algarismos. Para o Python, a vale "10" e b vale "5", e o + entre dois textos junta os textos (veja Juntar e Repetir Textos): "10" + "5" é "105". Para fazer conta, é preciso converter o texto em número antes. É o primeiro passo da versão 1.


Versão 1 · Dois números e um operador

A calculadora pede o primeiro número, a operação e o segundo número. Os números passam pelo float(), que transforma o texto num número com casas decimais (um float, veja Números), porque a Ana quer fazer contas como 7.5 / 2. A operação fica como texto mesmo: "+", "-", "*" ou "/". Para escolher a conta, uma escada de if e elif.

print("=== Calculadora da Ana ===")
a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
b = float(input("Segundo número: "))

if op == "+":
    print(f"{a} + {b} = {a + b}")
elif op == "-":
    print(f"{a} - {b} = {a - b}")
elif op == "*":
    print(f"{a} * {b} = {a * b}")
elif op == "/":
    print(f"{a} / {b} = {a / b}")
else:
    print(f"Operação desconhecida: {op}")
LinhaO que faz
print("=== Calculadora da Ana ===")O título, para a pessoa saber que programa está rodando.
input("Primeiro número: ")Mostra a pergunta e espera a pessoa digitar e apertar Enter. Devolve o que ela digitou, como texto. A pergunta termina com ": " (dois-pontos e espaço) para o que for digitado não grudar nela.
float(…)Converte o texto em número: "10" vira 10.0, "7.5" vira 7.5. Um input() dentro de um float(): o Python faz primeiro o de dentro (pergunta) e depois o de fora (converte).
op = input("Operação (+ - * /): ")A operação, sem conversão: "+" é um texto e assim fica.
if op == "+":"Se a operação for igual a "+"". O == compara (um = só guarda valor numa variável; veja Comparações).
elif op == "-":"Senão, se…": só é testado quando o if de cima deu falso. O Python testa de cima para baixo e faz só o primeiro que combinar.
else:Pega tudo o que não combinou com nenhum dos de cima: x, %, uma palavra qualquer.
f"{a} + {b} = {a + b}"Uma f-string: o que está entre chaves é calculado e entra no texto. O {a + b} faz a conta ali mesmo.

Rodando no terminal da Ana, duas vezes:

$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10.0 + 5.0 = 15.0
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 7.5
Operação (+ - * /): /
Segundo número: 2
7.5 / 2.0 = 3.75

Agora a conta está certa. Repare no 10.0 + 5.0 = 15.0: como os números passaram pelo float(), o Python os mostra com o .0 no fim. Está correto, só não é bonito; na versão 4 a calculadora vai mostrar 10 + 5 = 15. E uma operação que não existe cai no else:

$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 3
Operação (+ - * /): x
Segundo número: 4
Operação desconhecida: x
Por que float e não int?

O int() converte para número inteiro, e recusa "7.5" (dá erro). Com float(), a calculadora aceita as duas coisas: "10" vira 10.0 e "7.5" vira 7.5. Mais em Conversão de Tipos.

0.1 + 0.2 não dá exatamente 0.3
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 0.1
Operação (+ - * /): +
Segundo número: 0.2
0.1 + 0.2 = 0.30000000000000004

Não é defeito da calculadora nem do Python: o computador guarda os números com casas decimais em binário, e alguns, como 0.1, não têm representação exata (como 1/3 não tem em decimal: 0,3333…). Sobra um erro minúsculo na 17ª casa. Quase toda linguagem de programação dá o mesmo resultado. A aula Números explica; na versão 4, a calculadora arredonda o resultado na hora de mostrar.

Quando dá errado na versão 1

Texto no lugar de número: ValueError
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: seis
Traceback (most recent call last):
  File "/home/ana/projetos/calculadora/calculadora.py", line 2, in <module>
    a = float(input("Primeiro número: "))
        ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: could not convert string to float: 'seis'

Esta é a primeira mensagem de erro do projeto. Linha por linha:

Traceback (most recent call last):
"Rastro (a chamada mais recente por último)". O Python vai mostrar o caminho até o erro.
File "…/calculadora.py", line 2, in <module>
O erro aconteceu no arquivo calculadora.py, na linha 2. O <module> quer dizer "no corpo principal do programa", fora de qualquer função.
a = float(input("Primeiro número: "))
A linha culpada, copiada do arquivo.
~~~~~^^^^^^^^^^^
O sublinhado aponta o pedaço da linha que deu erro: o float(…) (o input deu certo; quem falhou foi a conversão).
ValueError: could not convert string to float: 'seis'
O tipo do erro e a explicação: "erro de valor: não consegui converter o texto 'seis' em float". O float() só entende algarismos, não números por extenso.

O programa parou ali: nem chegou a perguntar a operação. A versão 4 conserta isso com try/except. A aula Lendo uma Mensagem de Erro mostra mais tracebacks.

Vírgula no lugar do ponto
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 2,5
Traceback (most recent call last):
  File "/home/ana/projetos/calculadora/calculadora.py", line 2, in <module>
    a = float(input("Primeiro número: "))
        ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: could not convert string to float: '2,5'

Em português escrevemos 2,5; no Python, o decimal é com ponto: 2.5. Para o float(), "2,5" é um texto qualquer, como "seis". Na versão 4, a calculadora troca a vírgula pelo ponto antes de converter.

Divisão por zero: ZeroDivisionError
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 1
Operação (+ - * /): /
Segundo número: 0
Traceback (most recent call last):
  File "/home/ana/projetos/calculadora/calculadora.py", line 13, in <module>
    print(f"{a} / {b} = {a / b}")
                         ~~^~~
ZeroDivisionError: division by zero

Agora o erro está na linha 13, e o ^ aponta exatamente a barra de a / b. ZeroDivisionError: division by zero = "erro de divisão por zero". Na matemática, dividir por zero não tem resposta, e o Python se recusa a inventar uma: ele para o programa. A versão 2 cuida disso.

Missão (versão 1): o programa ainda não pede o segundo número e não sabe subtrair. Onde está o comentário, peça e leia b com float(input("Segundo número: ")), e acrescente um elif para a operação "-". A entrada da missão é 10, -, 4: o resultado deve ser 10.0 - 4.0 = 6.0.

print("=== Calculadora da Ana ===")
a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
# peça e leia o segundo número

if op == "+":
    print(f"{a} + {b} = {a + b}")
elif op == "*":
    print(f"{a} * {b} = {a * b}")
elif op == "/":
    print(f"{a} / {b} = {a / b}")
else:
    print(f"Operação desconhecida: {op}")
print("=== Calculadora da Ana ===")
a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
b = float(input("Segundo número: "))

if op == "+":
    print(f"{a} + {b} = {a + b}")
elif op == "-":
    print(f"{a} - {b} = {a - b}")
elif op == "*":
    print(f"{a} * {b} = {a * b}")
elif op == "/":
    print(f"{a} / {b} = {a / b}")
else:
    print(f"Operação desconhecida: {op}")

Versão 2 · match e a divisão por zero

Duas mudanças. A primeira é só de forma: a escada de if/elif que compara a mesma variável com vários valores fica mais fácil de ler com match e case ("combinar" e "caso"). A segunda conserta um defeito: antes de dividir, a calculadora confere se o segundo número é zero.

print("=== Calculadora da Ana ===")
a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
b = float(input("Segundo número: "))

match op:
    case "+":
        print(f"{a} + {b} = {a + b}")
    case "-":
        print(f"{a} - {b} = {a - b}")
    case "*":
        print(f"{a} * {b} = {a * b}")
    case "/":
        if b == 0:
            print("Não dá para dividir por zero.")
        else:
            print(f"{a} / {b} = {a / b}")
    case _:
        print(f"Operação desconhecida: {op}")
match op:
"Compare o valor de op com os casos abaixo." O match existe desde o Python 3.10.
case "+":
Se op for "+", faz o bloco de baixo (indentado com mais 4 espaços) e pula todos os outros casos.
if b == 0:
Dentro do caso da divisão, um if comum: se o divisor for zero, avisa em vez de dividir. Um bloco pode ter qualquer coisa dentro, inclusive outro if (veja Condições Aninhadas).
case _:
O sublinhado é o "qualquer outra coisa": combina com tudo, por isso fica por último. Faz o papel do else.
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 1
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 9
Operação (+ - * /): -
Segundo número: 12
9.0 - 12.0 = -3.0

Em vez do traceback da versão 1, uma frase que a Ana entende. Isso é prevenir o erro: o programa confere antes de fazer a conta perigosa. (A outra estratégia, tratar o erro depois que ele acontece, com try/except, aparece na versão 4.)

Comparar com 0 funciona com 0.0

Depois do float(), o b vale 0.0, e não 0. Mesmo assim, b == 0 dá True: o Python compara o valor dos números, não o tipo. 0.0 == 0 é verdadeiro.

Esquecer o case _

Se o case _: sair do programa e a Ana digitar x, nenhum caso combina e o match simplesmente não faz nada: o programa termina sem mostrar resultado nem aviso. Não é um erro para o Python (por isso não há traceback), mas para quem usa parece que a calculadora "travou calada". Sempre que os casos não cobrirem tudo, ponha um case _ no fim.

Missão (versão 2): o caso da divisão ainda divide sem olhar, e a entrada da missão (1, /, 0) derruba o programa com ZeroDivisionError. Dentro do case "/", confira se b == 0: se for, mostre Não dá para dividir por zero.; senão, faça a conta.

print("=== Calculadora da Ana ===")
a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
b = float(input("Segundo número: "))

match op:
    case "+":
        print(f"{a} + {b} = {a + b}")
    case "-":
        print(f"{a} - {b} = {a - b}")
    case "*":
        print(f"{a} * {b} = {a * b}")
    case "/":
        print(f"{a} / {b} = {a / b}")
    case _:
        print(f"Operação desconhecida: {op}")
print("=== Calculadora da Ana ===")
a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
b = float(input("Segundo número: "))

match op:
    case "+":
        print(f"{a} + {b} = {a + b}")
    case "-":
        print(f"{a} - {b} = {a - b}")
    case "*":
        print(f"{a} * {b} = {a * b}")
    case "/":
        if b == 0:
            print("Não dá para dividir por zero.")
        else:
            print(f"{a} / {b} = {a / b}")
    case _:
        print(f"Operação desconhecida: {op}")

Versão 3 · Repetir até a pessoa sair

Até agora, cada conta exige rodar o programa de novo. A Ana quer fazer várias contas seguidas. A solução é colocar tudo dentro de um laço while ("enquanto") e, no fim de cada volta, perguntar se ela quer continuar.

print("=== Calculadora da Ana ===")

while True:
    a = float(input("Primeiro número: "))
    op = input("Operação (+ - * /): ")
    b = float(input("Segundo número: "))

    match op:
        case "+":
            print(f"{a} + {b} = {a + b}")
        case "-":
            print(f"{a} - {b} = {a - b}")
        case "*":
            print(f"{a} * {b} = {a * b}")
        case "/":
            if b == 0:
                print("Não dá para dividir por zero.")
            else:
                print(f"{a} / {b} = {a / b}")
        case _:
            print(f"Operação desconhecida: {op}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")
while True:
"Enquanto verdadeiro": um laço que, sozinho, nunca termina. Tudo o que está indentado embaixo dele se repete. Quem decide a hora de parar é o break lá dentro.
(o bloco inteiro andou 4 espaços)
As perguntas e o match agora estão dentro do while, por isso ganharam mais um nível de indentação. É a indentação que diz ao Python o que faz parte do laço (veja Sintaxe e Indentação).
input("Outra conta? (s/n): ").strip().lower()
Três passos em fila: pergunta, tira os espaços das pontas (strip, "aparar") e passa para minúsculas (lower, "mais baixo"). Assim, S, s e " s " viram todos "s". Veja Métodos de Texto.
if continuar != "s":
"Se a resposta for diferente de s". Qualquer outra coisa (n, não, só Enter) encerra.
break
"Quebrar": sai do laço na hora. O programa continua na primeira linha depois do while (veja break e continue).
print("Até logo!")
Fora do laço (sem indentação): roda uma vez só, quando o break acontece.
$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10.0 + 5.0 = 15.0
Outra conta? (s/n): s
Primeiro número: 3
Operação (+ - * /): *
Segundo número: 4
3.0 * 4.0 = 12.0
Outra conta? (s/n): S                « maiúsculo também vale, graças ao lower()
Primeiro número: 100
Operação (+ - * /): /
Segundo número: 8
100.0 / 8.0 = 12.5
Outra conta? (s/n): n
Até logo!
Por que while True com break?

Dava para escrever while continuar == "s":, mas aí a variável continuar teria de existir antes do laço (com um continuar = "s" só para entrar na primeira volta). O while True com break no fim deixa a ordem natural: primeiro faz a conta, depois pergunta. É um padrão muito comum em programas que conversam com a pessoa.

Sair no meio com Ctrl+C

Se a Ana desistir no meio de uma conta, ela pode apertar Ctrl+C, que interrompe qualquer programa no terminal:

$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): ^CTraceback (most recent call last):
  File "/home/ana/projetos/calculadora/calculadora.py", line 5, in <module>
    op = input("Operação (+ - * /): ")
         ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt

O ^C é o terminal mostrando a tecla. O Python responde com KeyboardInterrupt ("interrupção pelo teclado") e o traceback mostra em que linha o programa estava esperando: a linha 5, a pergunta da operação. Não é defeito do programa: é o jeito normal de interromper. Mas assusta quem usa; a aula try e except mostra como trocar esse traceback por um "Até logo!". (Nas caixas "Experimente", o Ctrl+C também para o programa, mas sem traceback: aparece só ^C e [programa interrompido].)

Missão (versão 3): o programa faz uma conta, pergunta "Outra conta?", mas não repete. Coloque as perguntas, o match e a pergunta "Outra conta?" dentro de um while True: (indente tudo com mais 4 espaços) e saia com break quando a resposta não for s. A entrada da missão faz 10 + 5, responde s, faz 3 * 4 e responde n.

print("=== Calculadora da Ana ===")

a = float(input("Primeiro número: "))
op = input("Operação (+ - * /): ")
b = float(input("Segundo número: "))

match op:
    case "+":
        print(f"{a} + {b} = {a + b}")
    case "-":
        print(f"{a} - {b} = {a - b}")
    case "*":
        print(f"{a} * {b} = {a * b}")
    case "/":
        if b == 0:
            print("Não dá para dividir por zero.")
        else:
            print(f"{a} / {b} = {a / b}")
    case _:
        print(f"Operação desconhecida: {op}")

continuar = input("Outra conta? (s/n): ").strip().lower()

print("Até logo!")
print("=== Calculadora da Ana ===")

while True:
    a = float(input("Primeiro número: "))
    op = input("Operação (+ - * /): ")
    b = float(input("Segundo número: "))

    match op:
        case "+":
            print(f"{a} + {b} = {a + b}")
        case "-":
            print(f"{a} - {b} = {a - b}")
        case "*":
            print(f"{a} * {b} = {a * b}")
        case "/":
            if b == 0:
                print("Não dá para dividir por zero.")
            else:
                print(f"{a} / {b} = {a / b}")
        case _:
            print(f"Operação desconhecida: {op}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")

Versão 4 · Funções e entrada validada

A versão 3 já faz várias contas, mas ainda quebra com seis, com 2,5 e com qualquer coisa que não seja número. E o programa ficou comprido: está tudo misturado num bloco só. A versão 4 resolve as duas coisas de uma vez: cada tarefa vira uma função com nome, e a função que lê números insiste até receber um número de verdade.

OPERACOES = "+-*/"


def ler_numero(pergunta):
    """Pergunta até a pessoa digitar um número; aceita vírgula ou ponto."""
    while True:
        texto = input(pergunta).strip()
        try:
            return float(texto.replace(",", "."))
        except ValueError:
            print(f'"{texto}" não é um número. Tente de novo.')


def ler_operacao():
    """Pergunta até a pessoa digitar uma das quatro operações."""
    while True:
        op = input("Operação (+ - * /): ").strip()
        if len(op) == 1 and op in OPERACOES:
            return op
        print(f"Operação desconhecida: {op}")


def formatar(numero):
    """Mostra 15 em vez de 15.0, com no máximo 6 casas decimais."""
    numero = round(numero, 6)
    if numero.is_integer():
        return str(int(numero))
    return str(numero)


def calcular(a, op, b):
    """Faz a conta e devolve o resultado (ou None, se for dividir por zero)."""
    match op:
        case "+":
            return a + b
        case "-":
            return a - b
        case "*":
            return a * b
        case "/":
            if b == 0:
                return None
            return a / b


print("=== Calculadora da Ana ===")

while True:
    a = ler_numero("Primeiro número: ")
    op = ler_operacao()
    b = ler_numero("Segundo número: ")

    resultado = calcular(a, op, b)
    if resultado is None:
        print("Não dá para dividir por zero.")
    else:
        print(f"{formatar(a)} {op} {formatar(b)} = {formatar(resultado)}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")

Parece muito código novo, mas o "programa principal" (do print do título até o fim) ficou mais curto e mais fácil de ler do que na versão 3: ele só chama funções com nomes que dizem o que fazem. Vamos às peças.

As funções, uma por uma

PeçaO que faz
OPERACOES = "+-*/"As operações aceitas, num lugar só. O nome em MAIÚSCULAS é o costume do Python para um valor que não muda durante o programa (uma "constante").
def ler_numero(pergunta):Cria a função ler_numero, que recebe o texto da pergunta. Assim, a mesma função serve para o primeiro e para o segundo número (veja Parâmetros e Argumentos).
"""Pergunta até…"""A docstring: uma frase dizendo o que a função faz. Não muda nada na execução, mas ajuda quem lê.
texto.replace(",", ".")Troca a vírgula pelo ponto: "2,5" vira "2.5", que o float() entende.
try: … return float(…)"Tente": tenta converter. Se der certo, o return devolve o número e sai da função (e, com ela, do while).
except ValueError:Se a conversão der o ValueError que você viu na versão 1, em vez de parar o programa o Python vem para cá: mostra o aviso, e o while True pergunta de novo. Veja try e except.
len(op) == 1 and op in OPERACOESA operação tem de ter um caractere e estar dentro de "+-*/". O in procura um texto dentro de outro (veja is e in). Por que o len? Veja logo abaixo.
round(numero, 6)Arredonda para 6 casas: 0.30000000000000004 vira 0.3.
numero.is_integer()"É inteiro?": True para 15.0, False para 3.75. Se for, int() tira o .0.
def calcular(a, op, b):Só faz a conta e devolve o resultado com return; quem mostra é o programa principal. Para a divisão por zero, devolve None ("nada", veja O Valor None).
if resultado is None:Confere se veio "nada". Para comparar com None, o costume é is, e não ==.

Conferindo no modo interativo as peças novas, uma de cada vez:

$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> float("2,5".replace(",", "."))
2.5
>>> float("  7.5  ")
7.5
>>> round(0.1 + 0.2, 6)
0.3
>>> (15.0).is_integer()
True
>>> "%" in "+-*/"
False
>>> "+-" in "+-*/"
True
>>> exit()

A última linha explica o len(op) == 1: o in, entre textos, procura um pedaço. "+-" é um pedaço de "+-*/", então daria True, e a calculadora aceitaria a "operação" +-. Exigir um caractere só fecha essa porta. (O float() já ignora espaços nas pontas, como mostra o " 7.5 "; o strip() em ler_numero serve para a mensagem de erro mostrar o texto sem eles.)

$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15
Outra conta? (s/n): s
Primeiro número: seis
"seis" não é um número. Tente de novo.
Primeiro número: 6
Operação (+ - * /): %
Operação desconhecida: %
Operação (+ - * /): *
Segundo número: 1.5
6 * 1.5 = 9
Outra conta? (s/n): s
Primeiro número: 2,5
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.
Outra conta? (s/n): s
Primeiro número: 0.1
Operação (+ - * /): +
Segundo número: 0.2
0.1 + 0.2 = 0.3
Outra conta? (s/n): s
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 3
10 / 3 = 3.333333
Outra conta? (s/n): n
Até logo!

É a sessão do começo da página. Tudo o que derrubava a versão 1 agora tem resposta.

Usar a função antes de criá-la: NameError

Se o programa principal ficar em cima das funções (o while primeiro, os def depois), acontece isto:

$ python3 calculadora.py
=== Calculadora da Ana ===
Traceback (most recent call last):
  File "/home/ana/projetos/calculadora/calculadora.py", line 4, in <module>
    a = ler_numero("Primeiro número: ")
        ^^^^^^^^^^
NameError: name 'ler_numero' is not defined

NameError: name 'ler_numero' is not defined = "o nome ler_numero não está definido". O Python lê e executa o arquivo de cima para baixo. O def é uma instrução como outra qualquer: só depois que ela roda a função passa a existir. Quando a linha 4 chamou ler_numero, o def ainda estava lá embaixo, sem ter rodado. Como consertar: as funções primeiro, o programa principal no fim, como na versão 4.

Missão (versão 4): a função ler_numero ainda não se protege: com a entrada da missão (abc, 10, +, 5, n), o abc derruba o programa com ValueError. Coloque o return float(…) dentro de um try: e, no except ValueError:, mostre "abc" não é um número. Tente de novo. (com o texto que a pessoa digitou). O while True que já existe faz a pergunta de novo.

OPERACOES = "+-*/"


def ler_numero(pergunta):
    """Pergunta até a pessoa digitar um número; aceita vírgula ou ponto."""
    while True:
        texto = input(pergunta).strip()
        return float(texto.replace(",", "."))


def ler_operacao():
    """Pergunta até a pessoa digitar uma das quatro operações."""
    while True:
        op = input("Operação (+ - * /): ").strip()
        if len(op) == 1 and op in OPERACOES:
            return op
        print(f"Operação desconhecida: {op}")


def formatar(numero):
    """Mostra 15 em vez de 15.0, com no máximo 6 casas decimais."""
    numero = round(numero, 6)
    if numero.is_integer():
        return str(int(numero))
    return str(numero)


def calcular(a, op, b):
    """Faz a conta e devolve o resultado (ou None, se for dividir por zero)."""
    match op:
        case "+":
            return a + b
        case "-":
            return a - b
        case "*":
            return a * b
        case "/":
            if b == 0:
                return None
            return a / b


print("=== Calculadora da Ana ===")

while True:
    a = ler_numero("Primeiro número: ")
    op = ler_operacao()
    b = ler_numero("Segundo número: ")

    resultado = calcular(a, op, b)
    if resultado is None:
        print("Não dá para dividir por zero.")
    else:
        print(f"{formatar(a)} {op} {formatar(b)} = {formatar(resultado)}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")
OPERACOES = "+-*/"


def ler_numero(pergunta):
    """Pergunta até a pessoa digitar um número; aceita vírgula ou ponto."""
    while True:
        texto = input(pergunta).strip()
        try:
            return float(texto.replace(",", "."))
        except ValueError:
            print(f'"{texto}" não é um número. Tente de novo.')


def ler_operacao():
    """Pergunta até a pessoa digitar uma das quatro operações."""
    while True:
        op = input("Operação (+ - * /): ").strip()
        if len(op) == 1 and op in OPERACOES:
            return op
        print(f"Operação desconhecida: {op}")


def formatar(numero):
    """Mostra 15 em vez de 15.0, com no máximo 6 casas decimais."""
    numero = round(numero, 6)
    if numero.is_integer():
        return str(int(numero))
    return str(numero)


def calcular(a, op, b):
    """Faz a conta e devolve o resultado (ou None, se for dividir por zero)."""
    match op:
        case "+":
            return a + b
        case "-":
            return a - b
        case "*":
            return a * b
        case "/":
            if b == 0:
                return None
            return a / b


print("=== Calculadora da Ana ===")

while True:
    a = ler_numero("Primeiro número: ")
    op = ler_operacao()
    b = ler_numero("Segundo número: ")

    resultado = calcular(a, op, b)
    if resultado is None:
        print("Não dá para dividir por zero.")
    else:
        print(f"{formatar(a)} {op} {formatar(b)} = {formatar(resultado)}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")

Formas diferentes de escolher a conta

A calculadora usou if/elif e depois match. Há um terceiro jeito, que você vai encontrar em programas prontos: um dicionário que liga cada símbolo a uma função. O módulo operator (da biblioteca do Python) já traz as funções das contas: operator.add soma, operator.mul multiplica, e assim por diante.

import operator

a, op, b = 10.0, "*", 3.0

# Forma 1: if/elif
if op == "+":
    print("if/elif:", a + b)
elif op == "*":
    print("if/elif:", a * b)

# Forma 2: match/case
match op:
    case "+":
        print("match:", a + b)
    case "*":
        print("match:", a * b)

# Forma 3: um dicionário de funções
contas = {
    "+": operator.add,
    "-": operator.sub,
    "*": operator.mul,
    "/": operator.truediv,
}
print("dicionário:", contas[op](a, b))
$ python3 formas.py
if/elif: 30.0
match: 30.0
dicionário: 30.0

Na forma 3, contas[op] busca no dicionário a função ligada a "*" (a operator.mul), e o (a, b) logo depois a chama com os dois números.

FormaQuando usar
if / elifPoucas opções, ou condições diferentes entre si (op == "/" and b == 0). Funciona em qualquer versão do Python.
match / caseComparar uma variável com vários valores fixos. Fica limpo de ler. Precisa do Python 3.10 ou mais novo.
Dicionário de funçõesMuitas opções que fazem coisas do mesmo formato. Acrescentar uma operação é acrescentar uma linha no dicionário. Exige já conhecer dicionários e funções.
E o eval()?

Na internet você vai achar calculadoras de uma linha só: print(eval(input())). O eval() ("avaliar") executa um texto como se fosse código Python:

$ python3
Python 3.14.4 (main, Aug 20 2026, 10:41:58) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> eval("10 + 5")
15
>>> eval("2,5")
(2, 5)
>>> exit()

Parece prático, mas repare no segundo: 2,5 não deu erro nem 2.5; virou um par de números (uma tupla). E o pior: o eval() executa qualquer código que a pessoa digitar, inclusive um que apague arquivos. Nunca use eval() em algo que veio do teclado, de um arquivo ou da internet. A calculadora da Ana é mais longa, mas ela sabe exatamente o que aceita.


Desafio extra: resto e potência

A Ana quer mais duas operações: o resto da divisão (%) e a potência (**, "elevado a"), que você conhece da aula Operadores Aritméticos. O ** tem dois caracteres, então o teste len(op) == 1 and op in OPERACOES não serve mais. Com uma lista de operações, o in compara itens inteiros (e não pedaços de texto), e o len deixa de ser necessário:

OPERACOES = ["+", "-", "*", "/", "%", "**"]

Com as mudanças da missão abaixo, a Ana testou assim:

$ python3 calculadora.py
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * / % **): %
Segundo número: 3
10 % 3 = 1
Outra conta? (s/n): s
Primeiro número: 2
Operação (+ - * / % **): **
Segundo número: 10
2 ** 10 = 1024
Outra conta? (s/n): s
Primeiro número: 7
Operação (+ - * / % **): %
Segundo número: 0
Não dá para dividir por zero.
Outra conta? (s/n): n
Até logo!

O resto também é uma divisão: 7 % 0 dá o mesmo ZeroDivisionError que 7 / 0, por isso o caso "%" também confere o zero.

Desafio no editor: acrescente % e ** à calculadora. Mude três lugares: OPERACOES vira a lista ["+", "-", "*", "/", "%", "**"]; o if de ler_operacao fica só if op in OPERACOES: (e a pergunta, "Operação (+ - * / % **): "); e o match de calcular ganha case "%": (recusando o zero, como a divisão) e case "**":. Com a entrada da missão, deve aparecer 10 % 3 = 1, 2 ** 10 = 1024 e a recusa de 7 % 0.

OPERACOES = "+-*/"


def ler_numero(pergunta):
    """Pergunta até a pessoa digitar um número; aceita vírgula ou ponto."""
    while True:
        texto = input(pergunta).strip()
        try:
            return float(texto.replace(",", "."))
        except ValueError:
            print(f'"{texto}" não é um número. Tente de novo.')


def ler_operacao():
    """Pergunta até a pessoa digitar uma das operações."""
    while True:
        op = input("Operação (+ - * /): ").strip()
        if len(op) == 1 and op in OPERACOES:
            return op
        print(f"Operação desconhecida: {op}")


def formatar(numero):
    """Mostra 15 em vez de 15.0, com no máximo 6 casas decimais."""
    numero = round(numero, 6)
    if numero.is_integer():
        return str(int(numero))
    return str(numero)


def calcular(a, op, b):
    """Faz a conta e devolve o resultado (ou None, se for dividir por zero)."""
    match op:
        case "+":
            return a + b
        case "-":
            return a - b
        case "*":
            return a * b
        case "/":
            if b == 0:
                return None
            return a / b


print("=== Calculadora da Ana ===")

while True:
    a = ler_numero("Primeiro número: ")
    op = ler_operacao()
    b = ler_numero("Segundo número: ")

    resultado = calcular(a, op, b)
    if resultado is None:
        print("Não dá para dividir por zero.")
    else:
        print(f"{formatar(a)} {op} {formatar(b)} = {formatar(resultado)}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")
OPERACOES = ["+", "-", "*", "/", "%", "**"]


def ler_numero(pergunta):
    """Pergunta até a pessoa digitar um número; aceita vírgula ou ponto."""
    while True:
        texto = input(pergunta).strip()
        try:
            return float(texto.replace(",", "."))
        except ValueError:
            print(f'"{texto}" não é um número. Tente de novo.')


def ler_operacao():
    """Pergunta até a pessoa digitar uma das operações."""
    while True:
        op = input("Operação (+ - * / % **): ").strip()
        if op in OPERACOES:
            return op
        print(f"Operação desconhecida: {op}")


def formatar(numero):
    """Mostra 15 em vez de 15.0, com no máximo 6 casas decimais."""
    numero = round(numero, 6)
    if numero.is_integer():
        return str(int(numero))
    return str(numero)


def calcular(a, op, b):
    """Faz a conta e devolve o resultado (ou None, se for dividir por zero)."""
    match op:
        case "+":
            return a + b
        case "-":
            return a - b
        case "*":
            return a * b
        case "/":
            if b == 0:
                return None
            return a / b
        case "%":
            if b == 0:
                return None
            return a % b
        case "**":
            return a ** b


print("=== Calculadora da Ana ===")

while True:
    a = ler_numero("Primeiro número: ")
    op = ler_operacao()
    b = ler_numero("Segundo número: ")

    resultado = calcular(a, op, b)
    if resultado is None:
        print("Não dá para dividir por zero.")
    else:
        print(f"{formatar(a)} {op} {formatar(b)} = {formatar(resultado)}")

    continuar = input("Outra conta? (s/n): ").strip().lower()
    if continuar != "s":
        break

print("Até logo!")

Outras ideias para continuar sozinho: guardar cada conta numa lista e mostrar o histórico quando a Ana sair; aceitar a conta inteira numa linha só (10 + 5), separando as partes com split().


Referência rápida do projeto

EscritaPara que serviu
float(input("…"))Perguntar e converter o texto em número
if / elif / elseEscolher a conta (versão 1)
match op: / case "+": / case _:Escolher a conta comparando uma variável com valores fixos (versão 2)
if b == 0:Prevenir a divisão por zero
while True: … breakRepetir até a pessoa pedir para sair (versão 3)
.strip().lower()Aceitar S, s e " s " como a mesma resposta
texto.replace(",", ".")Aceitar o decimal com vírgula
try: … except ValueError:Não deixar um texto inválido derrubar o programa (versão 4)
def / returnSeparar o programa em tarefas com nome
round(x, 6), x.is_integer(), int(x)Mostrar 15 em vez de 15.0 e 0.3 em vez de 0.30000000000000004
SintomaCausa provável
10 + 5 = 105Faltou converter: o input() devolve texto, e + junta textos
ValueError: could not convert string to float: 'seis'O texto digitado não é um número (ou usa vírgula): trate com try/except
ZeroDivisionError: division by zeroDivisão (ou resto) por zero: confira o divisor antes
KeyboardInterruptA pessoa apertou Ctrl+C
NameError: name 'ler_numero' is not definedA função foi chamada antes do def (ou o nome está escrito diferente)
Uma operação inválida não mostra nadaFaltou o case _: no match

Exercícios

1. A conversão

Complete para que a guarde um número, e não um texto:

a = (input("Primeiro número: "))

2. O caso que pega o resto

Complete o caso do match que combina com qualquer operação:

case :

3. Sair do laço

Complete a palavra que encerra o while True:

if continuar != "s":
    

4. Sem float(), a Ana digitou 10 e 5 e a calculadora mostrou 10 + 5 = 105. Por quê?

5. Na versão 4, o que acontece quando a Ana digita seis no primeiro número?

6. Por que ler_operacao confere len(op) == 1 antes do op in OPERACOES?


Resumo

  • O input() sempre devolve texto. Para fazer conta, converta com float() (ou int()); sem isso, "10" + "5" dá "105".
  • if/elif e match/case escolhem a conta; o case _ pega o que sobrou.
  • A divisão por zero dá ZeroDivisionError: previna com if b == 0.
  • while True com break repete até a pessoa pedir para sair; .strip().lower() deixa a resposta tolerante.
  • Texto que não é número dá ValueError: trate com try/except dentro de um laço que pergunta de novo.
  • Funções com nomes claros deixam o programa principal curto. Elas precisam ser criadas (def) antes de serem chamadas.
  • Nunca use eval() no que a pessoa digita.