Skip to content

4. Ejercicio práctico – [IMPOTS]

4.1. El problema

Nos proponemos escribir un programa que permita calcular el impuesto de un contribuyente. Nos situamos en el caso simplificado de un contribuyente que solo tiene que declarar su salario:

  • se calcula el número de participaciones 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 renta imponible R = 0,72 * S, donde S es su salario anual;
  • se calcula su coeficiente familiar Q = R/N;
  • se calcula su impuesto I a partir de 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 <= campo1. Por ejemplo, si QF = 30000, se encontrará la línea:

24740 0.15 2072.5

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:

0 0.65 49062

lo que da el impuesto I=0,65*R - 49062*nbParts.

4.2. Version con listas


Programa (impots_01)


# -*- 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 más si hay 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
    # dado que se ha colocado el coeficiente al final de la tabla de límites, el bucle anterior
    # no puede sobrepasar la tabla de límites
    # 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 forman 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 introduce 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()

Resultados

El archivo de datos data.txt:

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

Los archivos resultats.txt de 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. Version con archivos de texto

En el ejemplo anterior, los datos necesarios para el cálculo del impuesto se encontraban en tres listas. A partir de ahora, se buscarán en un archivo 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: el nombre del archivo que contiene los datos de las tablas límite, 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 más si hay 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
    # dado que se ha colocado el coeficiente al final de la tabla de límites, el bucle anterior
    # no puede sobrepasar la tabla de límites
    # 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)

# ¿Se ha producido 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 forman 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 introduce 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()

Resultados

Los mismos que anteriormente.


Programa (impots_02b)

El método getImpots anterior (líneas 6-36) devuelve una tupla (error, límites, coeffR, coeffN) donde error es un mensaje de error que puede estar vacío. Es posible que se desee gestionar 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 (límites, 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 límite, 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 deja que esta se transmita al programa que la invoca;
  • 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 lo mismo para las líneas 2 y 3.

El código del programa principal queda entonces así:


# ------------------------------------------------ main
# definición de 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

# ¿Se ha producido 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: llamamos al método getImpots y gestionamos las dos excepciones que puede lanzar: IOError y RuntimeError. En esta gestión, nos limitamos a señalar que se ha producido un error;
  • líneas 20-22: se muestra el mensaje de error de la excepción interceptada y se detiene el programa.