Skip to content

4. 应用练习 – [IMPOTS]

4.1. 问题

我们计划编写一个程序,用于计算纳税人的税款。在此,我们考虑一种简化情况,即纳税人仅需申报工资收入:

  • 计算员工nbParts的份额数:若未婚,则为nbEnfants/2 +1; 已婚则为 nbEnfants/2+2,其中 nbEnfants 为其子女数;
  • 计算其应税收入 R=0.72*S,其中 S 为其年薪;
  • 计算其家庭系数 Q=R/N;
  • 根据以下数据计算其应纳税额 I。
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

每行有 3 个字段。要计算税款 I,需查找满足 QF<=字段1 的第一行。例如,若 QF=30000,则会找到以下行:

24740 0.15 2072.5

此时税额 I 等于 0.15*R - 2072.5*nbParts。 如果 QF 使得关系 QF<=field1 从未成立,则使用最后一行中的系数。此处:

0 0.65 49062

由此得出的税额 I=0.65*R - 49062*nbParts。

4.2. 带列表的版本


程序 (impots_01)


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

import math, sys

def cutNewLineChar(ligne):
    # 如果存在,则删除行尾标记
    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):
    # 已婚:是,否
    # 子女:子女数量
    # 薪资:年薪

    # 份额数
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # 若子女至少3名,则增加1/2份额
    if enfants>=3:
        nbParts+=0.5
    # 应税收入
    revenuImposable=0.72*salaire
    # 家庭系数
    quotient=revenuImposable/nbParts
    # 置于限额表末尾以终止后续循环
    limites[len(limites)-1]=quotient
    # 计算税额
    i=0
    while(quotient>limites[i]):
        i=i+1
    # 由于将家庭系数放置在限额表末尾,因此前面的循环
    # 不会超出限值表范围
    # 现在可以计算税款
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ 主函数
# 常量定义
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]

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

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

# 处理数据文件的当前行
ligne=data.readline()
while(ligne != ''):
    # 移除可能存在的换行符
    ligne=cutNewLineChar(ligne)
    # 提取构成该行的 3 个字段:已婚:子女:工资
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # 计算税额
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # 写入计算结果
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # 读取新行
    ligne=data.readline()
# 关闭文件
data.close()
resultats.close()

结果

数据文件 data.txt

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

所得结果的 resultats.txt 文件:

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. 带文本文件的版本

在上一个示例中,计算税款所需的数据来自三个列表。现在,这些数据将从一个文本文件中获取:

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

程序 (impots_02)


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

import math,sys

# --------------------------------------------------------------------------
def getTables(IMPOTS):
    # IMPOTS:包含限额表数据的文件名,coeffR,coeffN
    # 文件 IMPOTS 是否存在?
    try:
        data=open(IMPOTS,"r")
    except:
        return ("Le fichier IMPOTS n'existe pas",0,0,0)
    # 创建 3 个列表——假设行语法正确
    # -- 第 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])
    # -- 第 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])
    # -- 第 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])
    # 结束
    return ("",limites,coeffR,coeffN)

# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
    # 如果存在行尾标记,则将其删除
    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):
    # 已婚:是,否
    # 子女:子女人数
    # 工资:年薪

    # 份额数
    marie=marie.lower()
    if(marie=="oui"):
        nbParts=float(enfants)/2+2
    else:
        nbParts=float(enfants)/2+1
    # 若子女至少3名,则额外增加1/2份额
    if enfants>=3:
        nbParts+=0.5
    # 应税收入
    revenuImposable=0.72*salaire
    # 家庭系数
    quotient=revenuImposable/nbParts
    # 置于限额表末尾以终止后续循环
    limites[len(limites)-1]=quotient
    # 计算税额
    i=0
    while(quotient>limites[i]):
        i=i+1
    # 由于将家庭系数放置在限额表末尾,因此前面的循环
    # 不会超出限值表范围
    # 现在可以计算税额
    return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])


# ------------------------------------------------ 主函数
# 常量定义
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# 计算税款所需的数据已存入文件 IMPOTS
# 每张表格占一行,格式为
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)

# 是否出现错误?
if(erreur):
    print "{0}\n".format(erreur)
    sys.exit()

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

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

# 处理数据文件的当前行
ligne=data.readline()
while(ligne != ''):
    # 移除可能存在的换行符
    ligne=cutNewLineChar(ligne)
    # 提取构成该行的 3 个字段:已婚:子女:工资
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # 计算税额
    impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
    # 写入计算结果
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # 读取新行
    ligne=data.readline()
# 关闭文件
data.close()
resultats.close()

结果

与之前相同。


程序 (impots_02b)

上面的方法 getImpots(第 6-36 行)返回一个元组(erreurlimitescoeffR, coeffN),其中 erreur 是一个可能为空的错误消息。可能需要通过异常来处理这些错误情况。在这种情况下:

  • 若发生错误,方法 getImpots 将抛出异常;
  • 否则,它将返回元组 (limites, coeffR, coeffN)。

因此,方法 getImpots 的代码如下:


def getTables(IMPOTS):
    # IMPOTS:包含限额表数据的文件名,coeffR,coeffN
    # 文件 IMPOTS 是否存在?如果不存在,则在此情况下抛出异常 IOError
    data=open(IMPOTS,"r")
  
    # 创建 3 个列表——假设行在语法上正确
    # -- 第 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])
    # -- 第 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])
    # -- 第 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])
    # 结束
    return (limites,coeffR,coeffN)
  • 第 4 行:不检查文件打开是否失败。如果打开失败,将抛出 IOError 异常。该异常将向上传递给调用程序;
  • 第9-10行:抛出一个异常,以指示预期中的第一行缺失。这里使用了一个预定义的异常RuntimeError。也可以创建自己的异常类。我们稍后会这样做;
  • 第16-17行和第23-24行:对第2行和第3行重复上述操作。

主程序的代码如下:


# ------------------------------------------------ main
# 常量定义
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# 计算税款所需的数据已放入文件 IMPOTS
# 每张表格占一行,格式为
# val1:val2:val3,...

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

# 是否出现错误?
if(erreur):
    print "L'erreur suivante s'est produite : {0}\n".format(message)
    sys.exit()

# 读取数据
...
  • 第12-17行:调用方法getImpots,并处理该方法可能抛出的两个异常:IOError和RuntimeError。在此处理中,我们仅记录发生了错误;
  • 第20-22行:显示被拦截异常的错误信息,并终止程序。