Operadores aritméticos, de comparação e lógicos
por Sutton AshfordEntenda o conceito
Operadores são os símbolos que fazem contas e perguntas sobre valores. Os aritméticos são +, -, *, / (divisão que sempre dá float), // (divisão inteira), % (resto) e ** (potência). Quando aparecem juntos, seguem uma ordem de precedência: primeiro **, depois *, /, // e %, e por fim + e -. Parênteses mudam essa ordem e deixam a intenção clara.
Os operadores de comparação (==, !=, <, <=, >, >=) sempre devolvem True ou False. Eles comparam números (7 == 7.0 é verdadeiro) e textos, letra a letra. Dois cuidados: contas com float acumulam erro de arredondamento, então prefira math.isclose() à igualdade exata, e is pergunta se é o mesmo objeto, por isso é usado com None.
Os lógicos and, or e not combinam condições. Em Python, and e or param assim que o resultado está decidido (curto-circuito) e devolvem um dos próprios operandos, o que permite escrever valores padrão (apelido or nome) e guardas (qtd > 0 and total / qtd > 7).
- Precedência:
**primeiro, depois*,/,//e%, depois+e-; na dúvida, use parênteses. -2 ** 2dá-4, porque a potência vem antes do sinal;//e%arredondam para baixo, então-7 // 2dá-4.x += 1,x *= 2,x **= 2e afins são atalhos parax = x op valor.- Comparações devolvem
bool; não use==com contas defloat(0.1 + 0.2 == 0.3éFalse): prefiramath.isclose(). ParaNone, useis. - Ordem dos lógicos:
not, depoisand, depoisor;andeorparam no primeiro operando que decide e devolvem esse operando.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Ajustando uma receita com operadores aritméticos
# porcoes.py: operadores aritméticos ao ajustar uma receita.
FARINHA_POR_PORCAO = 45 # gramas de farinha para uma porção
porcoes = 8
# + - * / funcionam como na matemática.
farinha = FARINHA_POR_PORCAO * porcoes
print("Farinha total:", farinha, "g")
# ** é a potência: área de uma forma redonda de 22 cm.
raio = 22 / 2
area = 3.14159 * raio ** 2 # ** vem antes do *
print(f"Área da forma: {area:.1f} cm²")
# Precedência: ** > * / // % > + -. Parênteses mudam a ordem.
print("2 + 3 * 4 =", 2 + 3 * 4)
print("(2 + 3) * 4 =", (2 + 3) * 4)
print("-2 ** 2 =", -2 ** 2) # o ** vem antes do sinal de menos
print("(-2) ** 2 =", (-2) ** 2)
# // e % arredondam para baixo, inclusive com negativos.
print("7 // 2 =", 7 // 2, "| -7 // 2 =", -7 // 2)
print("7 % 3 =", 7 % 3, "| -7 % 3 =", -7 % 3)
# Operadores de atribuição composta: x op= y equivale a x = x op y.
ovos = 3
ovos *= 2 # dobra a receita
ovos -= 1 # um ovo quebrou
ovos **= 2 # só para mostrar: eleva ao quadrado
print("Ovos depois das contas:", ovos)
O exemplo usa todos os operadores aritméticos numa receita: multiplicação para a farinha de 8 porções, potência para a área da forma e as linhas de precedência mostram por que 2 + 3 * 4 dá 14 e -2 ** 2 dá -4. O ** é avaliado antes do sinal de menos e do *, por isso 3.14159 * raio ** 2 calcula a área sem parênteses.
Repare também nos negativos: // e % arredondam sempre para baixo, então -7 // 2 é -4 e -7 % 3 é 2. No fim, os operadores compostos (*=, -=, **=) atualizam a mesma variável sem repetir o nome dela.
02Comparando notas e nomes de alunos
# notas.py: operadores de comparação com números e textos.
import math
nota_ana = 7.5
nota_bruno = 7.50
# Comparações devolvem bool; int e float com o mesmo valor são iguais.
print("Mesma nota?", nota_ana == nota_bruno)
print("7 == 7.0?", 7 == 7.0)
print("Texto e número: '7' == 7?", "7" == 7)
print("Bruno passou de 7?", nota_bruno > 7)
print("Ana diferente de 10?", nota_ana != 10)
# float tem erro de arredondamento: evite == com contas decimais.
media = (0.1 + 0.2) * 10
print("Média calculada:", media)
print("media == 3.0?", media == 3.0)
print("math.isclose(media, 3.0)?", math.isclose(media, 3.0))
# Textos são comparados letra a letra, pelo código de cada caractere.
print("'Ana' < 'Bruno'?", "Ana" < "Bruno")
print("'ana' < 'Bruno'?", "ana" < "Bruno") # minúsculas vêm depois
print("Ignorando a caixa:", "ana".lower() < "bruno".lower())
# is pergunta se é o MESMO objeto; use-o com None.
recuperacao = None
print("Sem recuperação?", recuperacao is None)
Cada comparação gera um bool. Números de tipos diferentes com o mesmo valor são iguais (7 == 7.0), mas um texto nunca é igual a um número. O caso da média mostra a armadilha do float: (0.1 + 0.2) * 10 dá 3.0000000000000004, então == falha e math.isclose() acerta.
Textos são comparados caractere a caractere pelo código de cada um, e todas as maiúsculas vêm antes das minúsculas; por isso "ana" < "Bruno" é falso. Converter com lower() resolve. Por fim, is None é a forma idiomática de perguntar se uma variável ainda não tem valor.
03Cupons da loja com curto-circuito
# cupom.py: and, or e not devolvem um dos operandos (curto-circuito).
def preco_final(preco, cupom, itens):
# or devolve o primeiro valor verdadeiro: "" vira "SEM-CUPOM".
codigo = cupom or "SEM-CUPOM"
# and para no primeiro falso: se itens for 0, a divisão nem acontece.
preco_medio_alto = itens > 0 and preco / itens > 50
# not tem precedência maior que and, que tem maior que or.
ganha_brinde = not preco_medio_alto and itens >= 3 or codigo == "VIP"
desconto = 0.1 if codigo != "SEM-CUPOM" else 0
total = preco - preco * desconto
return f"{codigo}: R$ {total:.2f} | brinde: {ganha_brinde}"
def main():
# and e or devolvem um dos valores, não necessariamente True/False.
print("0 or 15 ->", 0 or 15)
print("'' or 'padrão' ->", "" or "padrão")
print("3 and 'ok' ->", 3 and "ok")
print("0 and 1 / 0 ->", 0 and 1 / 0) # 1 / 0 nunca é avaliado
print()
print(preco_final(120.0, "", 3))
print(preco_final(300.0, "PROMO", 2))
print(preco_final(0.0, "VIP", 0))
if __name__ == "__main__":
main()
As primeiras linhas mostram que or devolve o primeiro valor verdadeiro e and o primeiro falso (ou o último, se todos forem verdadeiros). Em 0 and 1 / 0, o Python para no 0 e a divisão por zero nunca acontece.
Em preco_final(), cupom or "SEM-CUPOM" cria um valor padrão, itens > 0 and preco / itens > 50 protege a divisão e a linha do brinde é lida como ((not preco_medio_alto) and itens >= 3) or codigo == "VIP", pela ordem de precedência dos lógicos. O cliente VIP ganha brinde mesmo com o carrinho vazio, porque basta um lado do or ser verdadeiro.
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Forma maior para a receitaFácil
Uma receita de bolo leva 300 g de farinha e 4 ovos numa forma de 20 cm de diâmetro. Calcule as quantidades para uma forma de 26 cm, sabendo que a massa acompanha a área, e mostre o fator, a farinha e os ovos separados em parte inteira e fração.
- O fator é calculado com
/e**, usando parênteses para dividir antes de elevar ao quadrado. - A parte inteira dos ovos é obtida com
//e a fração com%. - A farinha extra é a diferença entre a quantidade nova e a original.
- A saída tem exatamente 3 linhas, formatadas com f-strings.
# forma.py: ajustar a receita para uma forma maior.
DIAMETRO_ORIGINAL = 20 # cm
diametro_novo = 26 # cm
farinha = 300 # gramas na receita original
ovos = 4
# calcule o fator e as novas quantidades
Fator de aumento: 1.69
Farinha: 507 g (207 g a mais)
Ovos: 6 inteiros e mais 0.76 de ovo
Ver dica
Pense no que acontece se o ** for aplicado só ao 20: a potência tem precedência maior que a divisão.
Ver resposta
# forma.py: ajustar a receita para uma forma maior.
DIAMETRO_ORIGINAL = 20 # cm
diametro_novo = 26 # cm
farinha = 300 # gramas na receita original
ovos = 4
# A área cresce com o quadrado do diâmetro: ** 2.
fator = (diametro_novo / DIAMETRO_ORIGINAL) ** 2
farinha_nova = farinha * fator
farinha_extra = farinha_nova - farinha
ovos_novos = ovos * fator
# // 1 tira a parte inteira e % 1 a parte fracionária.
ovos_inteiros = int(ovos_novos // 1)
fracao_ovo = ovos_novos % 1
print(f"Fator de aumento: {fator:.2f}")
print(f"Farinha: {farinha_nova:.0f} g ({farinha_extra:.0f} g a mais)")
print(f"Ovos: {ovos_inteiros} inteiros e mais {fracao_ovo:.2f} de ovo")
Passo a passo
DIAMETRO_ORIGINALfica em maiúsculas porque é um dado fixo da receita; o diâmetro novo, a farinha e os ovos são variáveis comuns.(diametro_novo / DIAMETRO_ORIGINAL) ** 2divide primeiro, por causa dos parênteses, e só então eleva ao quadrado:1.3 ** 2dá1.6900000000000002, que o:.2fmostra como1.69.farinha * fatoreovos * fatormultiplicam umintpor umfloat, então os resultados sãofloat(cerca de507e6.76, com resíduos de arredondamento nas últimas casas).farinha_nova - farinhamostra quanto de farinha a mais será preciso comprar.ovos_novos // 1dá o quociente inteiro da divisão por 1, ou seja, a parte inteira (6.0, ainda umfloat); oint()tira o.0para exibir6.ovos_novos % 1dá o resto da divisão por 1, que é a parte fracionária (0.7600000000000007, exibida como0.76pelo:.2f).- Nas f-strings,
:.0fmostra a farinha sem casas decimais e:.2fmostra o fator e a fração com duas casas.
Por que funciona
A quantidade de massa acompanha a área da forma, e a área de um círculo é proporcional ao quadrado do diâmetro. Por isso o fator é a razão entre os diâmetros elevada a 2, escrita com **, e os parênteses garantem que a divisão aconteça antes da potência.
Os operadores // e % com divisor 1 separam um número em parte inteira e parte fracionária: o primeiro arredonda para baixo e o segundo devolve o que sobrou.
Erros comuns
fator = diametro_novo / DIAMETRO_ORIGINAL ** 2: o**tem precedência maior que/, então só o 20 é elevado ao quadrado e o fator vira0.065. Ponha a divisão entre parênteses.fator = (diametro_novo / DIAMETRO_ORIGINAL) ^ 2: dáTypeError, porque^é o "ou exclusivo" bit a bit, não a potência. Em Python a potência é**.fator = (diametro_novo / DIAMETRO_ORIGINAL) * 2: dobra em vez de elevar ao quadrado, e o fator vira2.6. Use** 2.ovos_inteiros = round(ovos_novos): dá7, porqueround()arredonda para o mais próximo; o enunciado pede a parte inteira, que é6. Use// 1(ouint()).
Outra forma de resolver
A potência também existe como função: pow(diametro_novo / DIAMETRO_ORIGINAL, 2). O ** é mais comum em contas simples; já a parte inteira e a fração podem sair juntas com divmod(ovos_novos, 1), útil quando você precisa das duas ao mesmo tempo.
Saída esperada
Fator de aumento: 1.69
Farinha: 507 g (207 g a mais)
Ovos: 6 inteiros e mais 0.76 de ovo
02Conferência do caixa da papelariaMédio
Uma caneta de R$ 1.10 e uma borracha de R$ 2.20 foram pagas com R$ 3.30 e o cliente digitou o cupom "Volta10", cadastrado como "VOLTA10". Mostre o total calculado e compare pagamento e cupom de duas formas: a direta, que falha, e a correta.
- O pagamento é comparado com
==e depois commath.isclose(). - O cupom é comparado diretamente e depois com
upper()aplicado ao texto digitado. - A última linha usa
>=com o total arredondado para 2 casas porround(). - Cada comparação imprime o
boolresultante, semif.
# caixa.py: conferir uma venda com operadores de comparação.
import math
caneta = 1.10
borracha = 2.20
valor_pago = 3.30
cupom_digitado = "Volta10"
CUPOM_VALIDO = "VOLTA10"
# calcule o total e faça as comparações
Total calculado: 3.3000000000000003
Conferência com ==: False
Conferência com isclose: True
Cupom igual sem tratar: False
Cupom igual com upper(): True
Pagou pelo menos o total? True
Ver dica
Imprima o total antes de comparar: o número que aparece explica por que a igualdade exata não funciona.
Ver resposta
# caixa.py: conferir uma venda com operadores de comparação.
import math
caneta = 1.10
borracha = 2.20
valor_pago = 3.30
cupom_digitado = "Volta10"
CUPOM_VALIDO = "VOLTA10"
total = caneta + borracha
print("Total calculado:", total)
# == compara o float exato; isclose aceita o erro de arredondamento.
print("Conferência com ==:", total == valor_pago)
print("Conferência com isclose:", math.isclose(total, valor_pago))
# O cupom é comparado sem diferenciar maiúsculas de minúsculas.
print("Cupom igual sem tratar:", cupom_digitado == CUPOM_VALIDO)
print("Cupom igual com upper():", cupom_digitado.upper() == CUPOM_VALIDO)
# Comparações de ordem também devolvem bool.
print("Pagou pelo menos o total?", valor_pago >= round(total, 2))
Passo a passo
import mathtraz o módulo da biblioteca padrão que tem a funçãomath.isclose().total = caneta + borrachasoma doisfloat; como 1.10 e 2.20 não têm representação binária exata, o total fica3.3000000000000003.total == valor_pagocompara os valores exatos e dáFalse, mesmo com o cliente tendo pago o valor certo.math.isclose(total, valor_pago)considera iguais dois números com diferença relativa minúscula (por padrão, 1 parte em um bilhão) e devolveTrue.cupom_digitado == CUPOM_VALIDOdiferencia maiúsculas de minúsculas, por isso"Volta10"e"VOLTA10"não são iguais.cupom_digitado.upper() == CUPOM_VALIDOpõe o texto digitado em maiúsculas antes de comparar, e a comparação passa a darTrue.valor_pago >= round(total, 2)arredonda o total para centavos antes de comparar a ordem, evitando que o resíduo de0.0000000000000003atrapalhe.
Por que funciona
Todo operador de comparação devolve um bool, que pode ser impresso, guardado numa variável ou usado num if. O cuidado está no que é comparado: float carrega erro de arredondamento, então a igualdade exata falha em contas com casas decimais, e textos são comparados caractere a caractere, com maiúsculas e minúsculas diferentes.
A solução corrige cada caso na hora de comparar: math.isclose() (ou round()) para números e upper() para textos.
Erros comuns
if total == 3.3:para validar o pagamento: o caixa recusaria uma venda correta. Comparefloatcommath.isclose()ou arredonde antes.total = 3.30: escrever3.30não guarda o zero final nem corrige nada;3.3,3.30e3.300são o mesmofloat.cupom_digitado.upper == CUPOM_VALIDO: sem os parênteses, compara o próprio método com um texto e dá sempreFalse. Chame o método comupper().total = valor_pago: com um só=você atribui em vez de comparar. A comparação usa==.
Outra forma de resolver
Para dinheiro, a forma mais segura é trabalhar com centavos inteiros (110 + 220 == 330 dá True) ou com decimal.Decimal("1.10"), que guarda as casas decimais exatamente. É a escolha certa em sistemas de pagamento; math.isclose() resolve bem em cálculos rápidos.
Saída esperada
Total calculado: 3.3000000000000003
Conferência com ==: False
Conferência com isclose: True
Cupom igual sem tratar: False
Cupom igual com upper(): True
Pagou pelo menos o total? True
03Situação no boletim com operadores lógicosDesafio
Escreva situacao(nome, apelido, soma_notas, qtd_provas, fez_trabalho, fez_feira), que devolve como o aluno é chamado (o apelido, se houver), se foi aprovado (média de pelo menos 7), se vai para a recuperação (fez prova e não passou) e se ganha bônus (fez exatamente uma das duas atividades extras). Um aluno sem provas não pode causar divisão por zero.
- O nome exibido é obtido com
apelido or nome, semif. - A aprovação usa
andem curto-circuito comqtd_provas > 0à esquerda da divisão. - A recuperação usa
not aprovadocombinado comand, e o bônus usa!=entre os doisbool. situacao()devolve o texto comreturne só amain(), chamada sobif __name__ == "__main__":, imprime.
# boletim.py: situação de alunos com and, or, not e !=.
MEDIA_APROVACAO = 7
def situacao(nome, apelido, soma_notas, qtd_provas, fez_trabalho, fez_feira):
# calcule chamado, aprovado, recuperacao e bonus sem usar if
return ""
def main():
print(situacao("Beatriz Souza", "Bia", 24.0, 3, True, False))
print(situacao("Gustavo Reis", "", 15.0, 3, True, True))
print(situacao("Laura Mendes", "", 0.0, 0, False, True))
print(situacao("Pedro Alves", "Pedrinho", 13.5, 2, False, False))
if __name__ == "__main__":
main()
Bia: aprovado=True, recuperação=False, bônus=True
Gustavo Reis: aprovado=False, recuperação=True, bônus=False
Laura Mendes: aprovado=False, recuperação=False, bônus=True
Pedrinho: aprovado=False, recuperação=True, bônus=False
Ver dica
O and só olha o lado direito quando o esquerdo é verdadeiro; e para "um ou outro, mas não os dois", compare os dois booleanos entre si.
Ver resposta
# boletim.py: situação de alunos com and, or, not e !=.
MEDIA_APROVACAO = 7
def situacao(nome, apelido, soma_notas, qtd_provas, fez_trabalho, fez_feira):
# or devolve o apelido quando ele não é vazio; senão, o nome.
chamado = apelido or nome
# and em curto-circuito: sem provas, a divisão nunca é feita.
aprovado = qtd_provas > 0 and soma_notas / qtd_provas >= MEDIA_APROVACAO
# not vem antes de and: "tem provas e NÃO foi aprovado".
recuperacao = qtd_provas > 0 and not aprovado
# != entre dois bool funciona como "ou exclusivo".
bonus = fez_trabalho != fez_feira
return (
f"{chamado}: aprovado={aprovado}, "
f"recuperação={recuperacao}, bônus={bonus}"
)
def main():
print(situacao("Beatriz Souza", "Bia", 24.0, 3, True, False))
print(situacao("Gustavo Reis", "", 15.0, 3, True, True))
print(situacao("Laura Mendes", "", 0.0, 0, False, True))
print(situacao("Pedro Alves", "Pedrinho", 13.5, 2, False, False))
if __name__ == "__main__":
main()
Passo a passo
MEDIA_APROVACAO = 7fica no topo para que a regra apareça num lugar só.chamado = apelido or nome: oordevolve o primeiro operando verdadeiro. Como o texto vazio é falso, quem não tem apelido é chamado pelo nome.qtd_provas > 0 and soma_notas / qtd_provas >= MEDIA_APROVACAO: quando não há provas, a primeira parte éFalsee oandpara ali, sem chegar à divisão por zero.- Na mesma linha,
/é calculado antes de>=, e as comparações antes doand: os aritméticos têm a maior precedência, depois as comparações e por último os lógicos. qtd_provas > 0 and not aprovado: onotse aplica só aaprovado, porque tem precedência maior que oand. Quem não fez prova não vai para a recuperação.fez_trabalho != fez_feiraéTruequando exatamente uma das duas atividades foi feita: é o "ou exclusivo" entre doisbool.- A função devolve o texto com
return, montado com duas f-strings vizinhas que o Python junta numa só, e amain()faz a impressão.
Por que funciona
Os operadores lógicos do Python avaliam da esquerda para a direita e param assim que o resultado está decidido (curto-circuito). Isso permite usar o and como guarda (só divide se houver provas) e o or como valor padrão (apelido ou nome), sem nenhum if.
A ordem de precedência (aritméticos, depois comparações, depois not, and e or) faz cada expressão ser lida como a frase da regra, e o != entre booleanos cobre o caso "um ou outro, mas não os dois", que não tem operador lógico próprio.
Erros comuns
aprovado = soma_notas / qtd_provas >= MEDIA_APROVACAO and qtd_provas > 0: a divisão vem primeiro e Laura provocaZeroDivisionError. A guarda precisa ficar à esquerda doand.bonus = fez_trabalho or fez_feira: Gustavo, que fez as duas atividades, ganharia o bônus. Ooraceita os dois verdadeiros; para "exatamente um", use!=.recuperacao = not qtd_provas > 0 and aprovado: onotpassa a negar a comparação, e a regra muda completamente. Coloque onotjunto do valor que deve ser invertido.chamado = apelido and nome: oanddevolve o segundo operando quando o primeiro é verdadeiro, então Bia seria chamada de "Beatriz Souza". O valor padrão se faz comor.
Outra forma de resolver
Os mesmos resultados podem ser escritos com if/else: chamado = apelido if apelido else nome e um if qtd_provas > 0: antes da divisão. Essa forma é preferível quando a regra tem muitos passos; com uma ou duas condições, o curto-circuito de and e or é idiomático e mais curto.
Saída esperada
Bia: aprovado=True, recuperação=False, bônus=True
Gustavo Reis: aprovado=False, recuperação=True, bônus=False
Laura Mendes: aprovado=False, recuperação=False, bônus=True
Pedrinho: aprovado=False, recuperação=True, bônus=False