Skip to content

4. Esercizio pratico – [Calcolo delle imposte]

4.1. Il problema

Il nostro obiettivo è scrivere un programma per calcolare l'imposta di un contribuente. Consideriamo il caso semplificato di un contribuente che deve dichiarare solo il proprio stipendio:

  • calcoliamo il numero di scaglioni fiscali per il dipendente nbParts = nbChildren / 2 + 1 se è celibe, nbChildren / 2 + 2 se è coniugato, dove nbChildren è il numero di figli;
  • calcoliamo il suo reddito imponibile R = 0,72 * S, dove S è il suo stipendio annuale;
  • calcoliamo il suo coefficiente familiare Q = R/N;
  • calcoliamo la sua imposta I sulla base dei seguenti dati.
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

Ogni riga ha 3 campi. Per calcolare l'imposta I, trova la prima riga in cui QF <= campo1. Ad esempio, se QF = 30.000, la riga trovata sarà:

24740 0.15 2072.5

L'imposta I è quindi pari a 0,15*R - 2072,5*nbParts. Se QF è tale che la condizione QF<=campo1 non viene mai soddisfatta, vengono utilizzati i coefficienti dell'ultima riga. In questo caso:

0 0.65 49062

il che dà l'imposta I = 0,65*R - 49062*nbParts.

4.2. Versione con elenchi


Programma (impots_01)

#    -*- 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()

Risultati

Il file di dati data.txt:

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

Il file results.txt contenente i risultati:

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. Versione con file di testo

Nell'esempio precedente, i dati necessari per calcolare l'imposta erano contenuti in tre elenchi. D'ora in poi, saranno recuperati da un file di testo:

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

Programma (taxes_02)

#    -*- 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()

Risultati

Come prima.


Programma (taxes_02b)

Il metodo getImpots sopra riportato (righe 6–36) restituisce una tupla (error, limits, coeffR, coeffN), dove error è un messaggio di errore che può essere vuoto. Potrebbe essere opportuno gestire questi casi di errore utilizzando le eccezioni. In tal caso:

  • se si verifica un errore, il metodo getImpots genera un'eccezione;
  • altrimenti, restituisce la tupla (limits, coeffR, coeffN).

Il codice per il metodo getImpots diventa quindi il seguente:

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)
  • riga 4: non controlliamo se l'apertura del file fallisce. Se fallisce, viene generata un'eccezione IOError. Lasciamo che questa si propaghi al programma chiamante;
  • righe 9–10: generiamo un'eccezione per indicare che manca la prima riga prevista. Utilizziamo un'eccezione RuntimeError predefinita. Possiamo anche creare le nostre classi di eccezioni. Lo faremo più avanti;
  • righe 16–17 e 23–24: facciamo la stessa cosa per le righe 2 e 3.

Il codice del programma principale diventa quindi il seguente:


# ------------------------------------------------ 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
...
  • righe 12-17: chiamiamo il metodo getImpots e gestiamo le due eccezioni che potrebbe generare: IOError e RuntimeError. In questa gestione, ci limitiamo a segnalare che si è verificato un errore;
  • righe 20–22: visualizziamo il messaggio di errore dell'eccezione intercettata e terminiamo il programma.