Introduction au langage Python par l'exemple


précédentsommairesuivant

IX. Exercice d'application - [IMPOTS] avec objets

Image non disponible

Nous reprenons ici l'exercice décrit précédemment. Nous partons de la version avec fichiers texte. Pour traiter cet exemple avec des objets, nous allons utiliser une architecture à trois couches :

Image non disponible
  • la couche [DAO] (Data Access Object) s'occupe de l'accès aux données. Dans la suite, ces données seront trouvées d'abord dans un fichier texte, puis dans une base de données MySQL ;
  • la couche [metier] s'occupe des problèmes métier, ici le calcul de l'impôt. Elle ne s'occupe pas des données. Celles-ci peuvent avoir deux provenances :
  • la couche [DAO] pour les données persistantes,
  • la couche [console] pour les données fournies par l'utilisateur ;
  • la couche [console] s'occupe des interactions avec l'utilisateur.

Dans la suite, les couches [DAO] et [metier] seront chacune implémentée à l'aide d'une classe. La couche [console] sera elle implémentée par le programme principal.

On supposera également que toutes les implémentations de la couche [DAO] offrent la méthode getData() qui rend un tuple de trois éléments (limites, coeffR, coeffN) qui sont les trois tableaux de données nécessaires au calcul de l'impôt. Dans d'autres langages, on appelle cela une interface. Une interface définit des méthodes (ici getData) que les classes implémentant cette interface doivent avoir.

La couche [metier] sera implémentée par une classe dont le constructeur aura pour paramètre une référence sur la couche [DAO] ce qui assurera la communication entre les deux couches.

IX-A. La couche [DAO]

Nous allons réunir les différentes classes nécessaires à l'application dans un même fichier impots.py. Les objets de ce fichier seront ensuite importés dans les scripts les nécessitant.

Nous commençons par le cas où les données sont dans un fichier texte.

Image non disponible

Le code de la classe [ImpotsFile] (impots.py) qui implémente la couche [DAO] est le suivant :

[ImpotsFile]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
# -*- coding=utf-8 -*-

import math, sys

# --------------------------------------------------------------------------
# classe d'exceptions propriétaire
class ImpotsError:
    pass

# --------------------------------------------------------------------------
class Utilitaires:
    """classe de fonctions utilitaires"""
    def cutNewLineChar(self,ligne):
        # on supprime la marque de fin de ligne si elle existe
        l=len(ligne)
        while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
            l-=1
        return(ligne[0:l])

# --------------------------------------------------------------------------
class ImpotsFile:
    def __init__(self,IMPOTS):
        # IMPOTS : le nom du fichier contenant les données des tables limites, coeffR, coeffN
        # on ouvre le fichier
        data=open(IMPOTS,"r")
        # un objet Utilitaires
        u=Utilitaires()
        # création des 3 tables - on suppose que les 3 lignes de IMPOTS sont syntaxiquement correctes
        # -- ligne 1
        ligne=data.readline()
        if ligne== '':
            raise ImpotsError("La premiere ligne du fichier {0} est absente".format(IMPOTS))
        limites=u.cutNewLineChar(ligne).split(":")
        for i in range(len(limites)):
            limites[i]=int(limites[i])
        # -- ligne 2
        ligne=data.readline()
        if ligne== '':
            raise ImpotsError("La deuxieme ligne du fichier {0} est absente".format(IMPOTS))
        coeffR=u.cutNewLineChar(ligne).split(":")
        for i in range(len(coeffR)):
            coeffR[i]=float(coeffR[i])
        # -- ligne 3
        ligne=data.readline()
        if ligne== '':
            raise ImpotsError("La troisieme ligne du fichier {0} est absente".format(IMPOTS))
        coeffN=u.cutNewLineChar(ligne).split(":")
        for i in range(len(coeffN)):
            coeffN[i]=float(coeffN[i])
        # fin
        (self.limites,self.coeffR,self.coeffN)=(limites,coeffR,coeffN)

    def getData(self):
        return (self.limites, self.coeffR, self.coeffN)

Notes :

  • lignes 7-8 : on définit une classe ImpotsError dérivée de la classe Exception. Cette classe n'ajoute rien à la classe Exception. On l'utilise seulement pour avoir une classe d'exception propriétaire. Ultérieurement cette classe pourrait être enrichie ;
  • lignes 11-18 : une classe de méthodes utilitaires. Ici la méthode cutNewLineChar supprime l'éventuelle marque de fin de ligne d'une chaîne de caractères ;
  • ligne 25 : l'ouverture du fichier peut lancer l'exception IOError ;
  • lignes 32, 39, 46 : on lance l'exception propriétaire ImpotsError ;
  • le code est analogue à celui étudié précédemment.

IX-B. La couche [metier]

Image non disponible

La classe [ImpotsMetier] (impots.py) qui implémente la couche [metier] est la suivante :

[ImpotsMetier]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
class ImpotsMetier:

    # constructeur
    # on récupère un pointeur sur la couche [dao]
    def __init__(self, dao):
        self.dao=dao

    # calcul de l'impôt
    # --------------------------------------------------------------------------
    def calculer(self,marie,enfants,salaire):
        # marié : oui, non
        # enfants : nombre d'enfants
        # salaire : salaire annuel

        # on demande à la couche [dao] les données nécessaires au calcul
        (limites, coeffR, coeffN)=self.dao.getData()

       # 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*(float)(coeffR[i])-nbParts*(float)(coeffN[i]))

Notes :

  • lignes 5-6 : le constructeur de la classe reçoit comme paramètre une référence sur la couche [DAO] ;
  • ligne 16 : on utilise la méthode getData de la couche [DAO] pour récupérer les données permettant le calcul de l'impôt ;

IX-C. La couche [console]

Image non disponible

Le script implémentant la couche [console] (impots-03) est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
# -*- coding=utf-8 -*-

# import du module des classes Impots*
from impots import *

# ------------------------------------------------ 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...

# instanciation couche [metier]
try:
    metier=ImpotsMetier(ImpotsFile(IMPOTS))
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    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()

# utilitaires
u=Utilitaires()

# 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=u.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=metier.calculer(marie,enfants,salaire)
    # 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()

Notes :

  • ligne 4 : on importe tous les objets du fichier impots.py qui contient les définitions des classes. Ceci fait, on peut utiliser ces objets comme s'ils étaient dans le même fichier que le script ;
  • lignes 17-21 : on instancie à la fois la couche [DAO] et la couche [metier] avec une gestion des exceptions éventuelles ;
  • ligne 18 : on instancie la couche [DAO] puis la couche [metier]. On mémorise la référence sur la couche [metier] ;
  • ligne 19 : on gère les deux exceptions qui peuvent se produire ;

IX-D. Résultats

Ceux déjà obtenus dans les versions avec tableaux et fichiers.

Le fichier des données impots.txt :

impots.txt
Sélectionnez
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

Le fichier des données data.txt :

data.txt
Sélectionnez
1.
2.
3.
4.
5.
6.
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 :

resultats.txt
Sélectionnez
1.
2.
3.
4.
5.
6.
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

précédentsommairesuivant

  

Licence Creative Commons
Le contenu de cet article est rédigé par Serge Tahé et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.