Skip to content

4. Exercício prático – [IMPOTS]

4.1. O problema

Propõe-se escrever um programa que permita calcular o imposto de um contribuinte. Consideramos o caso simplificado de um contribuinte que tenha apenas seu salário para declarar:

  • calcula-se o número de cotas do empregado nbParts = nbEnfants/2 + 1 se ele for solteiro, nbEnfants/2 + 2 se for casado, onde nbEnfants é o número de filhos;
  • calcula-se sua renda tributável R = 0,72 * S, onde S é seu salário anual;
  • calcula-se seu coeficiente familiar Q = R/N;
  • calcula-se seu imposto I com base nos dados a seguir.
12620.0 0 0
13190 0.05 631
15640 0.1 1290.5
24740 0.15 2072.5
31810 0.2 3309.5
39970 0.25 4900
48360 0.3 6898.5
55790 0.35 9316.5
92970 0.4 12106
127860 0.45 16754.5
151250 0.50 23147.5
172040 0.55 30710
195000 0.60 39312
0 0.65 49062

Cada linha possui 3 campos. Para calcular o imposto I, procura-se a primeira linha em que QF ≤ campo1. Por exemplo, se QF = 30000, encontrar-se-á a linha:

24740 0.15 2072.5

O imposto I é, então, igual a 0,15*R - 2072,5*nbParts. Se QF for tal que a relação QF <= campo1 nunca for verificada, então serão utilizados os coeficientes da última linha. Aqui:

0 0.65 49062

o que resulta no imposto I = 0,65 * R - 49.062 * nbParts.

4.2. Versão com listas


Programa (impots_01)


# -*- coding=utf-8 -*-

import math, sys

def cutNewLineChar(ligne):
    # remove-se o marcador de fim de linha, se houver
    l=len(ligne);
    while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
        l-=1
    return(ligne[0:l]);

  # --------------------------------------------------------------------------
def calculImpots(marie,enfants,salaire,limites,coeffR,coeffN):
    # casado: sim, não
    # filhos: número de filhos
    # salário: salário anual

    # número de cotas
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # mais 1/2 parte se houver pelo menos 3 filhos
    if enfants>=3:
        nbParts+=0.5
    # renda tributável
    revenuImposable=0.72*salaire
    # quociente familiar
    quotient=revenuImposable/nbParts
    # é colocado no final da tabela de limites para encerrar o ciclo a seguir
    limites[len(limites)-1]=quotient
    # cálculo do imposto
    i=0
    while(quotient>limites[i]):
        i=i+1
    # como o quociente familiar foi colocado no final da tabela de limites, o loop anterior
    # não pode ultrapassar a tabela de limites
    # agora é possível calcular o imposto
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# definição das constantes
DATA="data.txt"
RESULTATS="resultats.txt"
limites=[12620,13190,15640,24740,31810,39970,48360,55790,92970,127860,151250,172040,195000,0]
coeffR=[0,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.65]
coeffN=[0,631,1290.5,2072.5,3309.5,4900,6898.5,9316.5,12106,16754.5,23147.5,30710,39312,49062]

# leitura dos dados
try:
    data=open(DATA,"r")
except:
    print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
    sys.exit()

# abertura do arquivo de resultados
try:
    resultats=open(RESULTATS,"w")
except:  
    print "Impossible de creer le fichier des résultats [RESULTATS]"
    sys.exit()

# processamos a linha atual do arquivo de dados
ligne=data.readline()
while(ligne != ''):
    # remove-se o eventual caractere de fim de linha
    ligne=cutNewLineChar(ligne)
    # recuperam-se os três campos “casado:filhos:salário” que compõem a linha
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # calcula-se o imposto
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # registra-se o resultado
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # lê-se uma nova linha
    ligne=data.readline()
# fecha-se os arquivos
data.close()
resultats.close()

Resultados

O arquivo de dados data.txt:

oui,2,200000
non,2,200000
oui,3,200000
non,3,200000
oui,5,50000
non,0,3000000

Os arquivos resultats.txt dos resultados obtidos:

oui:2:200000:22504.0
non:2:200000:33388.0
oui:3:200000:16400.0
non:3:200000:22504.0
oui:5:50000:0.0
non:0:3000000:1354938.0

4.3. Versão com arquivos de texto

No exemplo anterior, os dados necessários para o cálculo do imposto foram encontrados em três listas. A partir de agora, eles serão buscados em um arquivo de texto:

1
2
3
12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:2072.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

Programa (impots_02)


# -*- coding=utf-8 -*-

import math,sys

# --------------------------------------------------------------------------
def getTables(IMPOTS):
    # IMPOTS: o nome do arquivo que contém os dados das tabelas de limites, coeffR, coeffN
    # O arquivo IMPOTS existe?
    try:
        data=open(IMPOTS,"r")
    except:
        return ("Le fichier IMPOTS n'existe pas",0,0,0)
    # criação das 3 listas — supõe-se que as linhas estejam sintaticamente corretas
    # -- linha 1
    ligne=data.readline()
    if ligne== '':
        return ("La premiere ligne du fichier {0} est absente".format(IMPOTS),0,0,0)
    limites=cutNewLineChar(ligne).split(":")
    for i in range(len(limites)):
        limites[i]=int(limites[i])
    # -- linha 2
    ligne=data.readline()
    if ligne== '':
        return ("La deuxieme ligne du fichier {0} est absente".format(IMPOTS),0,0,0)
    coeffR=cutNewLineChar(ligne).split(":")
    for i in range(len(coeffR)):
        coeffR[i]=float(coeffR[i])
    # — linha 3
    ligne=data.readline()
    if ligne== '':
        return ("La troisieme ligne du fichier {0} est absente".format(IMPOTS),0,0,0)
    coeffN=cutNewLineChar(ligne).split(":")
    for i in range(len(coeffN)):
        coeffN[i]=float(coeffN[i])
    # fim
    return ("",limites,coeffR,coeffN)

# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
    # remove-se o marcador de fim de linha, caso exista
    l=len(ligne);
    while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
        l-=1
    return(ligne[0:l]);

  # --------------------------------------------------------------------------
def calculImpots(marie,enfants,salaire,limites,coeffR,coeffN):
    # casado: sim, não
    # filhos: número de filhos
    # salário: salário anual

    # número de cotas
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # mais 1/2 parte se houver pelo menos 3 filhos
    if enfants>=3:
        nbParts+=0.5
    # renda tributável
    revenuImposable=0.72*salaire
    # quociente familiar
    quotient=revenuImposable/nbParts
    # é colocado no final da tabela de limites para encerrar o ciclo a seguir
    limites[len(limites)-1]=quotient
    # cálculo do imposto
    i=0
    while(quotient>limites[i]):
        i=i+1
    # como o quociente familiar foi colocado no final da tabela de limites, o loop anterior
    # não pode ultrapassar os limites da tabela
    # agora é possível calcular o imposto
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# definição das constantes
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# os dados necessários para o cálculo do imposto foram inseridos no arquivo IMPOTS
# com uma linha por tabela, no formato
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)

# houve algum erro?
if(erreur):
    print "{0}\n".format(erreur)
    sys.exit()

# leitura dos dados
try:
    data=open(DATA,"r")
except:
    print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
    sys.exit()

# abertura do arquivo de resultados
try:
    resultats=open(RESULTATS,"w")
except:  
    print "Impossible de creer le fichier des résultats [RESULTATS]"
    sys.exit()

# processando a linha atual do arquivo de dados
ligne=data.readline()
while(ligne != ''):
    # removendo o eventual caractere de fim de linha
    ligne=cutNewLineChar(ligne)
    # recuperam-se os três campos casado:filhos:salário que compõem a linha
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # calcula-se o imposto
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # insere-se o resultado
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # lê-se uma nova linha
    ligne=data.readline()
# fecha-se os arquivos
data.close()
resultats.close()

Resultados

Os mesmos de antes.


Programa (impots_02b)

O método getImpots acima (linhas 6-36) retorna uma tupla (erreur, limites, coeffR, coeffN), em que erreur é uma mensagem de erro que pode estar vazia. Pode ser necessário lidar com esses casos de erro por meio de exceções. Nesse caso:

  • em caso de erro, o método getImpots lança uma exceção;
  • caso contrário, ela retorna a tupla (limites, coeffR, coeffN).

O código do método getImpots passa a ser o seguinte:


def getTables(IMPOTS):
    # IMPOTS: o nome do arquivo que contém os dados das tabelas de limites, coeffR, coeffN
    # o arquivo IMPOTS existe? Se não, então a exceção IOError é lançada nesse caso
    data=open(IMPOTS,"r")
  
    # criação das 3 listas — supõe-se que as linhas estejam sintaticamente corretas
    # -- linha 1
    ligne=data.readline()
    if ligne== '':
        raise RuntimeError ("La premiere ligne du fichier {0} est absente".format(IMPOTS))
    limites=cutNewLineChar(ligne).split(":")
    for i in range(len(limites)):
        limites[i]=int(limites[i])
    # — linha 2
    ligne=data.readline()
    if ligne== '':
        raise RuntimeError ("La deuxieme ligne du fichier {0} est absente".format(IMPOTS))
    coeffR=cutNewLineChar(ligne).split(":")
    for i in range(len(coeffR)):
        coeffR[i]=float(coeffR[i])
    # — linha 3
    ligne=data.readline()
    if ligne== '':
        raise RuntimeError ("La troisieme ligne du fichier {0} est absente".format(IMPOTS))
    coeffN=cutNewLineChar(ligne).split(":")
    for i in range(len(coeffN)):
        coeffN[i]=float(coeffN[i])
    # fim
    return (limites,coeffR,coeffN)
  • linha 4: não se verifica se a abertura do arquivo falha. Se falhar, é lançada uma exceção IOError. Deixa-se que essa exceção seja propagada para o programa chamador;
  • linhas 9-10: lança-se uma exceção para indicar que a primeira linha esperada está ausente. Utiliza-se uma exceção predefinida RuntimeError. Também é possível criar suas próprias classes de exceção. Faremos isso um pouco mais adiante;
  • linhas 16-17 e 23-24: repetimos o mesmo procedimento para as linhas 2 e 3.

O código do programa principal fica então assim:


# ------------------------------------------------ main
# definição das constantes
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# os dados necessários para o cálculo do imposto foram inseridos no arquivo IMPOTS
# na proporção de uma linha por tabela, no formato
# val1:val2:val3,...

erreur=False
try:
    (limites,coeffR,coeffN)=getTables(IMPOTS)
except IOError, message:
    erreur=True
except RuntimeError, message:
    erreur=True

# houve algum erro?
if(erreur):
    print "L'erreur suivante s'est produite : {0}\n".format(message)
    sys.exit()

# leitura dos dados
...
  • linhas 12-17: chamamos o método getImpots e tratamos as duas exceções que ele pode lançar: IOError e RuntimeError. Nesse tratamento, limitamo-nos a registrar que ocorreu um erro;
  • linhas 20-22: exibe-se a mensagem de erro da exceção interceptada e encerra-se o programa.