Skip to content

4. Ćwiczenie praktyczne – [IMPOTS]

4.1. Zadanie

Zadaniem jest napisanie programu umożliwiającego obliczenie podatku podatnika. Rozważamy uproszczony przypadek podatnika, który ma do zadeklarowania wyłącznie swoje wynagrodzenie:

  • liczy się liczbę udziałów pracownika według wzoru nbParts = nbEnfants/2 + 1, jeśli nie jest żonaty, nbEnfants/2 + 2, jeśli jest żonaty, gdzie nbEnfants to liczba jego dzieci;
  • oblicza się jego dochód podlegający opodatkowaniu R = 0,72 * S, gdzie S to jego roczne wynagrodzenie;
  • oblicza się jego współczynnik rodzinny Q = R/N;
  • oblicza się jego podatek I na podstawie poniższych danych.
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

Każdy wiersz zawiera 3 pola. Aby obliczyć podatek I, należy znaleźć pierwszy wiersz, w którym QF <= pole1. Na przykład, jeśli QF = 30000, znajdziemy wiersz:

24740 0.15 2072.5

Podatek I wynosi wówczas 0,15*R – 2072,5*nbParts. Jeśli QF jest takie, że relacja QF <= pole1 nigdy nie jest spełniona, wówczas stosuje się współczynniki z ostatniego wiersza. W tym przypadku:

0 0.65 49062

co daje podatek I = 0,65*R – 49062*nbParts.

4.2. Wersja z listami


Program (impots_01)


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

import math, sys

def cutNewLineChar(ligne):
    # usuwanie znacznika końca wiersza, jeśli istnieje
    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):
    # stan cywilny: tak, nie
    # dzieci: liczba dzieci
    # wynagrodzenie: roczne wynagrodzenie

    # liczba udziałów
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # o 1/2 udziału więcej, jeśli ma co najmniej 3 dzieci
    if enfants>=3:
        nbParts+=0.5
    # dochód podlegający opodatkowaniu
    revenuImposable=0.72*salaire
    # współczynnik rodzinny
    quotient=revenuImposable/nbParts
    # umieszczony na końcu tabeli limitów w celu zakończenia następującej pętli
    limites[len(limites)-1]=quotient
    # obliczenie podatku
    i=0
    while(quotient>limites[i]):
        i=i+1
    # ponieważ iloraz rodzinny umieszczono na końcu tablicy limitów, poprzednia pętla
    # nie może wykraczać poza tablicę limitów
    # teraz można obliczyć podatek
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# definicja stałych
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]

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

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

# przetwarzamy bieżący wiersz pliku danych
ligne=data.readline()
while(ligne != ''):
    # usuwanie ewentualnego znaku końca wiersza
    ligne=cutNewLineChar(ligne)
    # pobieranie trzech pól: „żonaty:dzieci:wynagrodzenie”, które tworzą wiersz
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # obliczamy podatek
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # zapisuje się wynik
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # odczytuje się nowy wiersz
    ligne=data.readline()
# zamykamy pliki
data.close()
resultats.close()

Wyniki

Plik danych data.txt:

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

Pliki resultats.txt zawierające uzyskane wyniki:

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. Wersja z plikami tekstowymi

W poprzednim przykładzie dane niezbędne do obliczenia podatku pochodziły z trzech list. Teraz będą one pobierane z pliku tekstowego:

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

Program (impots_02)


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

import math,sys

# --------------------------------------------------------------------------
def getTables(IMPOTS):
    # IMPOTS: nazwa pliku zawierającego dane z tabel limitów, coeffR, coeffN
    # czy plik IMPOTS istnieje?
    try:
        data=open(IMPOTS,"r")
    except:
        return ("Le fichier IMPOTS n'existe pas",0,0,0)
    # utworzenie trzech list – zakłada się, że wiersze są poprawne pod względem składniowym
    # — wiersz 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])
    # — wiersz 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])
    # — wiersz 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])
    # koniec
    return ("",limites,coeffR,coeffN)

# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
    # usuwamy znacznik końca wiersza, jeśli występuje
    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):
    # stan cywilny: tak, nie
    # dzieci: liczba dzieci
    # wynagrodzenie: roczne wynagrodzenie

    # liczba udziałów
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # o 1/2 udziału więcej, jeśli ma co najmniej 3 dzieci
    if enfants>=3:
        nbParts+=0.5
    # dochód podlegający opodatkowaniu
    revenuImposable=0.72*salaire
    # współczynnik rodzinny
    quotient=revenuImposable/nbParts
    # umieszczony na końcu tabeli limitów w celu zakończenia następującej pętli
    limites[len(limites)-1]=quotient
    # obliczenie podatku
    i=0
    while(quotient>limites[i]):
        i=i+1
    # ponieważ iloraz rodzinny umieszczono na końcu tablicy limitów, poprzednia pętla
    # nie może wykraczać poza tablicę limitów
    # teraz można obliczyć podatek
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ main
# definicja stałych
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# dane niezbędne do obliczenia podatku zostały umieszczone w pliku IMPOTS
# w postaci jednego wiersza na tabelę w formacie
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)

# czy wystąpił błąd?
if(erreur):
    print "{0}\n".format(erreur)
    sys.exit()

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

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

# przetwarzanie bieżącego wiersza pliku danych
ligne=data.readline()
while(ligne != ''):
    # usuwamy ewentualny znacznik końca wiersza
    ligne=cutNewLineChar(ligne)
    # pobieranie trzech pól: „żonaty:dzieci:wynagrodzenie”, które tworzą wiersz
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # obliczamy podatek
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # zapisuje się wynik
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # odczytuje się nowy wiersz
    ligne=data.readline()
# zamykamy pliki
data.close()
resultats.close()

Wyniki

Takie same jak poprzednio.


Program (impots_02b)

Powyższa metoda getImpots (wiersze 6–36) zwraca krotkę (erreur, limites, coeffR, coeffN), gdzie erreur to komunikat o błędzie, który może być pusty. Można rozważyć obsługę takich przypadków błędów za pomocą wyjątków. W takim przypadku:

  • w przypadku błędu metoda getImpots zgłasza wyjątek;
  • w przeciwnym razie zwraca krotkę (limites, coeffR, coeffN).

Kod metody getImpots wygląda wówczas następująco:


def getTables(IMPOTS):
    # IMPOTS: nazwa pliku zawierającego dane z tabel limitów, coeffR, coeffN
    # czy plik IMPOTS istnieje? Jeśli nie, to w tym przypadku zgłaszany jest wyjątek IOError
    data=open(IMPOTS,"r")
  
    # utworzenie trzech list – zakłada się, że wiersze są poprawne pod względem składniowym
    # — wiersz 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])
    # — wiersz 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])
    # — wiersz 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])
    # koniec
    return (limites,coeffR,coeffN)
  • wiersz 4: nie sprawdzamy, czy otwarcie pliku zakończyło się niepowodzeniem. Jeśli tak się stanie, zgłaszany jest wyjątek IOError. Pozwalamy, aby wyjątek ten został przekazany do programu wywołującego;
  • wiersze 9–10: generujemy wyjątek, aby zasygnalizować brak pierwszego oczekiwanego wiersza. Wykorzystujemy predefiniowany wyjątek RuntimeError. Można również tworzyć własne klasy wyjątków. Zrobimy to nieco później;
  • wiersze 16–17 i 23–24: powtarzamy tę samą procedurę dla wierszy 2 i 3.

Kod programu głównego wygląda wówczas następująco:


# ------------------------------------------------ main
# definicja stałych
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# dane niezbędne do obliczenia podatku zostały umieszczone w pliku IMPOTS
# w ilości jednego wiersza na tabelę w postaci
# val1:val2:val3,...

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

# czy wystąpił błąd?
if(erreur):
    print "L'erreur suivante s'est produite : {0}\n".format(message)
    sys.exit()

# odczyt danych
...
  • wiersze 12–17: wywołujemy metodę getImpots i obsługujemy dwa wyjątki, które może ona wygenerować: IOError i RuntimeError. W ramach tej obsługi ograniczamy się do odnotowania, że wystąpił błąd;
  • wiersze 20–22: wyświetla się komunikat o błędzie związany z przechwyconym wyjątkiem i kończy się działanie programu.