4. Ejercicio práctico – [IMPOTS]
![]() |
4.1. El problema
Nos proponemos escribir un programa que permita calcular el impuesto de un contribuyente. Consideramos el caso simplificado de un contribuyente que solo tiene que declarar su salario:
- se calcula el número de cuotas del empleado nbParts = nbEnfants/2 + 1 si no está casado, nbEnfants/2+2 si está casado, donde nbEnfants es su número de hijos;
- se calcula su ingreso imponible R = 0,72 * S, donde S es su salario anual;
- se calcula su coeficiente familiar Q = R/N;
- se calcula su impuesto I según los siguientes datos.
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 línea tiene 3 campos. Para calcular el impuesto I, se busca la primera línea en la que QF sea menor o igual que el campo 1. Por ejemplo, si QF = 30000, se encontrará la línea:
El impuesto I es entonces igual a 0,15*R - 2072,5*nbParts. Si QF es tal que la relación QF <= campo1 nunca se cumple, entonces se utilizan los coeficientes de la última línea. En este caso:
lo que da como resultado el impuesto I = 0,65 * R - 49062 * nbParts.
4.2. Versión con listas
# -*- coding=utf-8 -*-
import math, sys
def cutNewLineChar(ligne):
# se elimina el marcador de fin de línea si existe
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: sí, no
# Hijos: número de hijos
# salario: salario anual
# número de partes
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# media participación adicional si tiene al menos 3 hijos
if enfants>=3:
nbParts+=0.5
# ingresos imponibles
revenuImposable=0.72*salaire
# coeficiente familiar
quotient=revenuImposable/nbParts
# se coloca al final de la tabla de límites para detener el bucle siguiente
limites[len(limites)-1]=quotient
# cálculo del impuesto
i=0
while(quotient>limites[i]):
i=i+1
# debido a que se colocó el coeficiente familiar al final de la tabla de límites, el bucle anterior
# no puede sobrepasar los límites de la tabla
# ahora se puede calcular el impuesto
return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])
# ------------------------------------------------ main
# definición de las 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]
# lectura de datos
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# apertura del archivo de resultados
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# se procesa la línea actual del archivo de datos
ligne=data.readline()
while(ligne != ''):
# se elimina el posible carácter de fin de línea
ligne=cutNewLineChar(ligne)
# se recuperan los 3 campos «casado:hijos:salario» que conforman la línea
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# se calcula el impuesto
impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
# se registra el resultado
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# se lee una nueva línea
ligne=data.readline()
# se cierran los archivos
data.close()
resultats.close()
El archivo de datos data.txt:
Los archivos resultats.txt con los resultados obtenidos:
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. Versión con archivos de texto
En el ejemplo anterior, los datos necesarios para calcular el impuesto se obtuvieron de tres listas. A partir de ahora, se buscarán en un archivo de texto:
# -*- coding=utf-8 -*-
import math,sys
# --------------------------------------------------------------------------
def getTables(IMPOTS):
# IMPOTS: el nombre del archivo que contiene los datos de las tablas de límites, coeffR, coeffN
# ¿Existe el archivo IMPOTS?
try:
data=open(IMPOTS,"r")
except:
return ("Le fichier IMPOTS n'existe pas",0,0,0)
# creación de las 3 listas; se supone que las líneas son sintácticamente correctas
# -- línea 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])
# -- línea 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])
# -- línea 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])
# fin
return ("",limites,coeffR,coeffN)
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# se elimina el marcador de fin de línea si existe
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: sí, no
# hijos: número de hijos
# salario: salario anual
# número de partes
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# media participación adicional si tiene al menos 3 hijos
if enfants>=3:
nbParts+=0.5
# ingresos imponibles
revenuImposable=0.72*salaire
# coeficiente familiar
quotient=revenuImposable/nbParts
# se coloca al final de la tabla de límites para detener el bucle siguiente
limites[len(limites)-1]=quotient
# cálculo del impuesto
i=0
while(quotient>limites[i]):
i=i+1
# debido a que se colocó el cociente familiar al final de la tabla de límites, el bucle anterior
# no puede sobrepasar los límites de la tabla
# ahora se puede calcular el impuesto
return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])
# ------------------------------------------------ main
# definición de las constantes
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# los datos necesarios para el cálculo del impuesto se han colocado en el archivo IMPOTS
# a razón de una línea por tabla en el formato
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)
# ¿Ha ocurrido algún error?
if(erreur):
print "{0}\n".format(erreur)
sys.exit()
# lectura de datos
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# apertura del archivo de resultados
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# se procesa la línea actual del archivo de datos
ligne=data.readline()
while(ligne != ''):
# se elimina el posible carácter de fin de línea
ligne=cutNewLineChar(ligne)
# se recuperan los 3 campos «casado:hijos:salario» que conforman la línea
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# se calcula el impuesto
impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
# se registra el resultado
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# se lee una nueva línea
ligne=data.readline()
# se cierran los archivos
data.close()
resultats.close()
Los mismos que antes.
El método getImpots anterior (líneas 6-36) devuelve una tupla (erreur, limites, coeffR, coeffN), donde erreur es un mensaje de error que puede estar vacío. Es posible que se desee manejar estos casos de error mediante excepciones. En ese caso:
- en caso de error, el método getImpots lanza una excepción;
- de lo contrario, devuelve la tupla (limites, coeffR, coeffN).
El código del método getImpots queda entonces así:
def getTables(IMPOTS):
# IMPOTS: el nombre del archivo que contiene los datos de las tablas de límites, coeffR, coeffN
# ¿Existe el archivo IMPOTS? Si no es así, se lanza la excepción IOError en este caso
data=open(IMPOTS,"r")
# creación de las 3 listas: se supone que las líneas son sintácticamente correctas
# -- línea 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])
# -- línea 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])
# -- línea 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])
# fin
return (limites,coeffR,coeffN)
- línea 4: no se comprueba si falla la apertura del archivo. Si falla, se lanza una excepción IOError. Se permite que esta se propague al programa que la invocó;
- líneas 9-10: se lanza una excepción para indicar que falta la primera línea esperada. Se utiliza una excepción predefinida RuntimeError. También se pueden crear clases de excepción propias. Lo haremos un poco más adelante;
- líneas 16-17 y 23-24: se repite el mismo proceso para las líneas 2 y 3.
El código del programa principal queda entonces así:
# ------------------------------------------------ main
# definición de las constantes
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# los datos necesarios para el cálculo del impuesto se han colocado en el archivo IMPOTS
# a razón de una línea por tabla en el formato
# val1:val2:val3,...
erreur=False
try:
(limites,coeffR,coeffN)=getTables(IMPOTS)
except IOError, message:
erreur=True
except RuntimeError, message:
erreur=True
# ¿Ha ocurrido algún error?
if(erreur):
print "L'erreur suivante s'est produite : {0}\n".format(message)
sys.exit()
# lectura de datos
...
- líneas 12-17: se llama al método getImpots y se manejan las dos excepciones que puede lanzar: IOError y RuntimeError. En este manejo, simplemente se registra que se produjo un error;
- líneas 20-22: se muestra el mensaje de error de la excepción interceptada y se detiene el programa.
