Skip to content

4. Esercizio pratico – [IMPOTS]

4.1. Il problema

Si propone di scrivere un programma che consenta di calcolare l'imposta di un contribuente. Si considera il caso semplificato di un contribuente che abbia da dichiarare solo il proprio stipendio:

  • si calcola il numero di quote del dipendente nbParts=nbEnfants/2 +1 se non è sposato, nbEnfants/2+2 se è sposato, dove nbEnfants è il numero dei suoi figli;
  • si calcola il suo reddito imponibile R=0,72*S, dove S è il suo stipendio annuale;
  • si calcola il suo coefficiente familiare Q = R/N;
  • si calcola la sua imposta I in base ai 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 contiene 3 campi. Per calcolare l'imposta I, si cerca la prima riga in cui QF ≤ campo1. Ad esempio, se QF = 30000, si troverà la riga:

24740 0.15 2072.5

L'imposta I è quindi pari a 0,15*R - 2072,5*nbParts. Se QF è tale che la relazione QF<=campo1 non è mai soddisfatta, allora 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):
    # si rimuove il carattere di fine riga, se presente
    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):
    # stato civile: sì, no
    # figli: numero di figli
    # stipendio: stipendio annuo

    # numero di quote
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # una quota e mezza in più se ci sono almeno 3 figli
    if enfants>=3:
        nbParts+=0.5
    # reddito imponibile
    revenuImposable=0.72*salaire
    # quoziente familiare
    quotient=revenuImposable/nbParts
    # viene inserito alla fine della tabella dei limiti per interrompere il ciclo successivo
    limites[len(limites)-1]=quotient
    # calcolo dell'imposta
    i=0
    while(quotient>limites[i]):
        i=i+1
    # poiché il quoziente è stato inserito alla fine della tabella dei limiti, il ciclo precedente
    # non può andare oltre i limiti della tabella
    # ora è possibile calcolare l’imposta
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# definizione delle costanti
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]

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

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

# si elabora la riga corrente del file dei dati
ligne=data.readline()
while(ligne != ''):
    # si rimuove l'eventuale carattere di fine riga
    ligne=cutNewLineChar(ligne)
    # si recuperano i 3 campi coniugato:figli:stipendio che compongono la riga
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # si calcola l'imposta
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # si inserisce il risultato
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # si legge una nuova riga
    ligne=data.readline()
# si chiudono i file
data.close()
resultats.close()

Risultati

Il file dei dati data.txt:

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

I file resultats.txt dei risultati ottenuti:

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 il calcolo dell'imposta erano stati ricavati da tre elenchi. D'ora in poi saranno ricavati 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 (impots_02)


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

import math,sys

# --------------------------------------------------------------------------
def getTables(IMPOTS):
    # IMPOTS: il nome del file contenente i dati delle tabelle dei limiti, coeffR, coeffN
    # Il file IMPOTS esiste?
    try:
        data=open(IMPOTS,"r")
    except:
        return ("Le fichier IMPOTS n'existe pas",0,0,0)
    # creazione delle 3 liste - si presume che le righe siano sintatticamente corrette
    # -- riga 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])
    # -- riga 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])
    # -- riga 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])
    # fine
    return ("",limites,coeffR,coeffN)

# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
    # si elimina il carattere di fine riga se presente
    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):
    # stato civile: sì, no
    # figli: numero di figli
    # stipendio: stipendio annuo

    # numero di quote
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # una quota e mezza in più se ci sono almeno 3 figli
    if enfants>=3:
        nbParts+=0.5
    # reddito imponibile
    revenuImposable=0.72*salaire
    # quoziente familiare
    quotient=revenuImposable/nbParts
    # viene inserito alla fine della tabella dei limiti per interrompere il ciclo successivo
    limites[len(limites)-1]=quotient
    # calcolo dell'imposta
    i=0
    while(quotient>limites[i]):
        i=i+1
    # poiché il quoziente è stato inserito alla fine della tabella dei limiti, il ciclo precedente
    # non può andare oltre i limiti della tabella
    # ora è possibile calcolare l’imposta
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# definizione delle costanti
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# i dati necessari per il calcolo dell'imposta sono stati inseriti nel file IMPOTS
# con una riga per ogni tabella nella forma
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)

# Si è verificato un errore?
if(erreur):
    print "{0}\n".format(erreur)
    sys.exit()

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

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

# si elabora la riga corrente del file dei dati
ligne=data.readline()
while(ligne != ''):
    # si rimuove l'eventuale carattere di fine riga
    ligne=cutNewLineChar(ligne)
    # si recuperano i 3 campi coniugato:figli:stipendio che compongono la riga
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # si calcola l'imposta
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # si inserisce il risultato
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # si legge una nuova riga
    ligne=data.readline()
# si chiudono i file
data.close()
resultats.close()

Risultati

Gli stessi di prima.


Programma (impots_02b)

Il metodo getImpots sopra riportato (righe 6-36) restituisce una tupla (erreur, limites, coeffR, coeffN) in cui erreur è un messaggio di errore, eventualmente vuoto. Si potrebbe voler gestire questi casi di errore tramite eccezioni. In tal caso:

  • in caso di errore, il metodo getImpots genera un'eccezione;
  • altrimenti restituisce la tupla (limites, coeffR, coeffN).

Il codice del metodo getImpots diventa quindi il seguente:


def getTables(IMPOTS):
    # IMPOTS: il nome del file contenente i dati delle tabelle limite, coeffR, coeffN
    # il file IMPOTS esiste? In caso contrario, viene generata l'eccezione IOError
    data=open(IMPOTS,"r")
  
    # creazione dei 3 elenchi - si presume che le righe siano sintatticamente corrette
    # -- riga 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])
    # -- riga 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])
    # -- riga 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])
    # fine
    return (limites,coeffR,coeffN)
  • riga 4: non si verifica se l’apertura del file fallisce. Se fallisce, viene generata un’eccezione IOError. Si lascia che questa venga propagata al programma chiamante;
  • righe 9-10: si genera un'eccezione per segnalare che la prima riga prevista è assente. Si utilizza un'eccezione predefinita RuntimeError. È anche possibile creare classi di eccezione personalizzate. Lo faremo più avanti;
  • righe 16-17 e 23-24: si ripete la stessa operazione per le righe 2 e 3.

Il codice del programma principale diventa quindi il seguente:


# ------------------------------------------------ main
# definizione delle costanti
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# i dati necessari per il calcolo dell'imposta sono stati inseriti nel file IMPOTS
# con una riga per ogni tabella nella forma
# val1:val2:val3,...

erreur=False
try:
    (limites,coeffR,coeffN)=getTables(IMPOTS)
except IOError, message:
    erreur=True
except RuntimeError, message:
    erreur=True

# Si è verificato un errore?
if(erreur):
    print "L'erreur suivante s'est produite : {0}\n".format(message)
    sys.exit()

# lettura dei dati
...
  • righe 12-17: si chiama il metodo getImpots e si gestiscono le due eccezioni che esso può generare: IOError e RuntimeError. In questa gestione, ci si limita a segnalare che si è verificato un errore;
  • righe 20-22: si visualizza il messaggio di errore dell’eccezione intercettata e si interrompe il programma.