4. Exercício Prático – [Cálculo de Impostos]
4.1. O problema
O nosso objetivo é escrever um programa para calcular o imposto de um contribuinte. Consideramos o caso simplificado de um contribuinte que tem apenas o seu salário para declarar:
- calculamos o número de escalões de imposto para o trabalhador nbParts = nbChildren / 2 + 1 se for solteiro, nbChildren / 2 + 2 se for casado, sendo nbChildren o número de filhos;
- calculamos o seu rendimento tributável R = 0,72 * S, onde S é o seu salário anual;
- calculamos o seu coeficiente familiar Q = R/N;
- calculamos o seu imposto I com base nos seguintes dados.
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 tem 3 campos. Para calcular o imposto I, encontre a primeira linha em que QF <= campo1. Por exemplo, se QF = 30 000, a linha encontrada será:
O imposto I é então igual a 0,15*R - 2072,5*nbParts. Se QF for tal que a condição QF<=campo1 nunca for satisfeita, então são utilizados os coeficientes da última linha. Aqui:
o que dá o imposto I = 0,65*R - 49062*nbParts.
| # -*- coding=utf-8 -*-
import math, sys
def cutNewLineChar(ligne):
# delete the end-of-line mark if it exists
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):
# married: yes, no
# children: number of children
# salary: annual salary
# number of shares
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# an additional 1/2 share if at least 3 children
if enfants>=3:
nbParts+=0.5
# taxable income
revenuImposable=0.72*salaire
# family quotient
quotient=revenuImposable/nbParts
# is set at the end of the limit table to stop the following loop
limites[len(limites)-1]=quotient
# tAX CALCULATION
i=0
while(quotient>limites[i]):
i=i+1
# because we've placed quotient at the end of the limit array, the previous loop
# cannot go beyond the limit table
# now we can calculate the tax
return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])
# ------------------------------------------------ main
# definition of constants
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]
# reading data
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# open results file
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# use the current line of the data file
ligne=data.readline()
while(ligne != ''):
# remove any end-of-line marker
ligne=cutNewLineChar(ligne)
# we retrieve the 3 fields married:children:salary which form the line
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# tax calculation
impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
# enter the result
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# a new line is read
ligne=data.readline()
# close files
data.close()
resultats.close()
|
Resultados
O ficheiro de dados data.txt:
oui,2,200000
non,2,200000
oui,3,200000
non,3,200000
oui,5,50000
non,0,3000000
O ficheiro results.txt contendo os resultados:
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 ficheiros de texto
No exemplo anterior, os dados necessários para calcular o imposto foram encontrados em três listas. A partir de agora, serão recuperados de um ficheiro de texto:
| 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
|
| # -*- coding=utf-8 -*-
import math,sys
# --------------------------------------------------------------------------
def getTables(IMPOTS):
# IMPOTS: the name of the file containing data from the limit tables, coeffR, coeffN
# does the IMPOTS file exist?
try:
data=open(IMPOTS,"r")
except:
return ("Le fichier IMPOTS n'existe pas",0,0,0)
# create the 3 lists - assume the lines are syntactically correct
# -- line 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])
# -- line 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])
# -- line 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])
# end
return ("",limites,coeffR,coeffN)
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# delete the end-of-line mark if it exists
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):
# married: yes, no
# children: number of children
# salary: annual salary
# number of shares
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# an additional 1/2 share if at least 3 children
if enfants>=3:
nbParts+=0.5
# taxable income
revenuImposable=0.72*salaire
# family quotient
quotient=revenuImposable/nbParts
# is set at the end of the limit array to stop the loop that follows
limites[len(limites)-1]=quotient
# tAX CALCULATION
i=0
while(quotient>limites[i]):
i=i+1
# because we've placed quotient at the end of the limit array, the previous loop
# cannot exceed the limits of the board
# now we can calculate the tax
return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])
# ------------------------------------------------ main
# definition of constants
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# the data required to calculate the tax has been placed in the IMPOTS file
# one line per table in the form
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)
# was there a mistake?
if(erreur):
print "{0}\n".format(erreur)
sys.exit()
# reading data
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# open results file
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# use the current line of the data file
ligne=data.readline()
while(ligne != ''):
# remove any end-of-line marker
ligne=cutNewLineChar(ligne)
# we retrieve the 3 fields married:children:salary which form the line
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# tax calculation
impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
# enter the result
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# a new line is read
ligne=data.readline()
# close files
data.close()
resultats.close()
|
Resultados
O mesmo que antes.
Programa (taxes_02b)
O método getImpots acima (linhas 6–36) devolve uma tupla (error, limits, coeffR, coeffN), em que error é uma mensagem de erro que pode estar vazia. Poderá querer tratar estes casos de erro utilizando exceções. Nesse caso:
- se ocorrer um erro, o método getImpots lança uma exceção;
- caso contrário, retorna a tupla (limits, coeffR, coeffN).
O código para o método getImpots passa então a ser o seguinte:
| def getTables(IMPOTS):
# IMPOTS: the name of the file containing data from the limit tables, coeffR, coeffN
# does file IMPOTS exist? if not, exception IOError is thrown in this case
data=open(IMPOTS,"r")
# create the 3 lists - assume the lines are syntactically correct
# -- line 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])
# -- line 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])
# -- line 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])
# end
return (limites,coeffR,coeffN)
|
- linha 4: não verificamos se a abertura do ficheiro falha. Se falhar, é lançada uma exceção IOError. Deixamos que esta se propague para o programa de chamada;
- linhas 9–10: lançamos uma exceção para indicar que a primeira linha esperada está em falta. Utilizamos uma exceção RuntimeError predefinida. Também podemos criar as nossas próprias classes de exceção. Faremos isso um pouco mais tarde;
- linhas 16–17 e 23–24: fazemos o mesmo para as linhas 2 e 3.
O código do programa principal fica então assim:
# ------------------------------------------------ main
# définition des constantes
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# les données nécessaires au calcul de l'impôt ont été placées dans le fichier IMPOTS
# à raison d'une ligne par tableau sous la forme
# val1:val2:val3,...
erreur=False
try:
(limites,coeffR,coeffN)=getTables(IMPOTS)
except IOError, message:
erreur=True
except RuntimeError, message:
erreur=True
# y-a-t-il eu une erreur ?
if(erreur):
print "L'erreur suivante s'est produite : {0}\n".format(message)
sys.exit()
# lecture des données
...
- linhas 12-17: chamamos o método getImpots e tratamos as duas exceções que ele pode lançar: IOError e RuntimeError. Nesta tratamento, limitamo-nos a registar que ocorreu um erro;
- linhas 20–22: exibimos a mensagem de erro da exceção interceptada e encerramos o programa.