Skip to content

4. Exercice d'application – [IMPOTS]

4.1. Le problème

On se propose d'écrire un programme permettant de calculer l'impôt d'un contribuable. On se place dans le cas simplifié d'un contribuable n'ayant que son seul salaire à déclarer :

  • on calcule le nombre de parts du salarié nbParts=nbEnfants/2 +1 s'il n'est pas marié, nbEnfants/2+2 s'il est marié, où nbEnfants est son nombre d'enfants ;
  • on calcule son revenu imposable R=0.72*S où S est son salaire annuel ;
  • on calcule son coefficient familial Q=R/N ;
  • on calcule son impôt I d'après les données suivantes.
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

Chaque ligne a 3 champs. Pour calculer l'impôt I, on recherche la première ligne où QF<=champ1. Par exemple, si QF=30000 on trouvera la ligne :

24740 0.15 2072.5

L'impôt I est alors égal à 0.15*R - 2072.5*nbParts. Si QF est tel que la relation QF<=champ1 n'est jamais vérifiée, alors ce sont les coefficients de la dernière ligne qui sont utilisés. Ici :

0 0.65 49062

ce qui donne l'impôt I=0.65*R - 49062*nbParts.

4.2. Version avec listes


Programme (impots_01)


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

import math, sys

def cutNewLineChar(ligne):
    # on supprime la marque de fin de ligne de ligne si elle 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):
    # marié : oui, non
    # enfants : nombre d'enfants
    # salaire : salaire annuel

    # nombre de parts
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # une 1/2 part de plus si au moins 3 enfants
    if enfants>=3:
        nbParts+=0.5
    # revenu imposable
    revenuImposable=0.72*salaire
    # quotient familial
    quotient=revenuImposable/nbParts
    # est mis à la fin du tableau limites pour arrêter la boucle qui suit
    limites[len(limites)-1]=quotient
    # calcul de l'impôt
    i=0
    while(quotient>limites[i]):
        i=i+1
    # du fait qu'on a placé quotient à la fin du tableau limites, la boucle précédente
    # ne peut déborder du tableau limites
    # maintenant on peut calculer l'impôt
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# définition des 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]

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

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

# on exploite la ligne courante du fichier des données
ligne=data.readline()
while(ligne != ''):
    # on enlève l'éventuelle marque de fin de ligne
    ligne=cutNewLineChar(ligne)
    # on récupère les 3 champs marié:enfants:salaire qui forment la ligne
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # on calcule l'impôt
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # on inscrit le résultat
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # on lit une nouvelle ligne
    ligne=data.readline()
# on ferme les fichiers
data.close()
resultats.close()

Résultats

Le fichier des données data.txt :

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

Les fichier resultats.txt des résultats obtenus :

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 avec fichiers texte

Dans l'exemple précédent, les données nécessaires au calcul de l'impôt avaient été trouvées dans trois listes. Elle seront désormais cherchées dans un fichier texte :

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

Programme (impots_02)


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

import math,sys

# --------------------------------------------------------------------------
def getTables(IMPOTS):
    # IMPOTS : le nom du fichier contenant les données des tables limites, coeffR, coeffN
    # le fichier IMPOTS existe-t-il ?
    try:
        data=open(IMPOTS,"r")
    except:
        return ("Le fichier IMPOTS n'existe pas",0,0,0)
    # création des 3 listes - on suppose que les lignes sont syntaxiquement correctes
    # -- ligne 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])
    # -- ligne 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])
    # -- ligne 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):
    # on supprime la marque de fin de ligne de ligne si elle 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):
    # marié : oui, non
    # enfants : nombre d'enfants
    # salaire : salaire annuel

    # nombre de parts
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # une 1/2 part de plus si au moins 3 enfants
    if enfants>=3:
        nbParts+=0.5
    # revenu imposable
    revenuImposable=0.72*salaire
    # quotient familial
    quotient=revenuImposable/nbParts
    # est mis à la fin du tableau limites pour arrêter la boucle qui suit
    limites[len(limites)-1]=quotient
    # calcul de l'impôt
    i=0
    while(quotient>limites[i]):
        i=i+1
    # du fait qu'on a placé quotient à la fin du tableau limites, la boucle précédente
    # ne peut déborder du tableau limites
    # maintenant on peut calculer l'impôt
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ 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,limites,coeffR,coeffN)=getTables(IMPOTS)

# y-a-t-il eu une erreur ?
if(erreur):
    print "{0}\n".format(erreur)
    sys.exit()

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

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

# on exploite la ligne courante du fichier des données
ligne=data.readline()
while(ligne != ''):
    # on enlève l'éventuelle marque de fin de ligne
    ligne=cutNewLineChar(ligne)
    # on récupère les 3 champs marié:enfants:salaire qui forment la ligne
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # on calcule l'impôt
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # on inscrit le résultat
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # on lit une nouvelle ligne
    ligne=data.readline()
# on ferme les fichiers
data.close()
resultats.close()

Résultats

Les mêmes que précédemment.


Programme (impots_02b)

La méthode getImpots ci-dessus (lignes 6-36) rend un tuple (erreur, limites, coeffR, coeffN) où erreur est un message d'erreur éventuellement vide. On peut vouloir gérer ces cas d'erreurs avec des exceptions. Dans ce cas :

  • en cas d'erreur, la méthode getImpots rend une exception ;
  • sinon elle rend le tuple (limites, coeffR, coeffN).

Le code de la méthode getImpots devient alors le suivant :


def getTables(IMPOTS):
    # IMPOTS : le nom du fichier contenant les données des tables limites, coeffR, coeffN
    # le fichier IMPOTS existe-t-il ? si non alors l'exception IOError est lancée dans ce cas
    data=open(IMPOTS,"r")
  
    # création des 3 listes - on suppose que les lignes sont syntaxiquement correctes
    # -- ligne 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])
    # -- ligne 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])
    # -- ligne 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)
  • ligne 4 : on ne teste pas si l'ouverture du fichier échoue. Si elle échoue, une exception IOError est lancée. On laisse celle-ci remonter au programme appelant ;
  • lignes 9-10 : on lance une exception pour indiquer que la première ligne attendue est absente. On utilise une exception prédéfinie RuntimeError. On peut également créer ses propres classes d'exception. Nous le ferons un peu plus loin ;
  • lignes 16-17 et 23-24 : on refait la même chose pour les lignes 2 et 3.

Le code du programme principal devient alors le suivant :


# ------------------------------------------------ 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
...
  • lignes 12-17 : on appelle la méthode getImpots et on gère les deux exceptions qu'elle peut lancer : IOError et RuntimeError. Dans cette gestion, on se contente de noter qu'il y a eu erreur ;
  • lignes 20-22 : on affiche le message d'erreur de l'exception interceptée et on arrête le programme.