建立在多进程的基础之上,使用模块进行优化
介绍
优化上一个挑战中完成的计算器,完善下述需求:

  • 使用 getopt 模块处理命令行参数
  • 使用 Python3 中的 configparser 模块读取配置文件
  • 使用 datetime模块写入工资单生成时间

计算器执行中包含下面的参数:

  • -h 或 --help,打印当前计算器的使用方法,内容为:
Usage: calculator.py -C cityname -c configfile -d userdata -o resultdata
  • -C 城市名称 指定使用某个城市的社保配置信息,如果没有使用该参数,则使用配置文件中 [DEFAULT] 栏目中的数据,城市名称不区分大小写,比如配置文件中写的是 [CHENGDU],这里参数可以写 -C Chengdu,仍然可以匹配

  • -c 配置文件 配置文件,由于各地的社保比例稍有不同,我们将多个城市的不同配置信息写入一个配置文件

  • -d 员工工资数据文件 指定员工工资数据文件,文件中包含两列内容,分别为员工工号和工资金额

  • -o 员工工资单数据文件 输出内容,将员工缴纳的社保、税前、税后工资等详细信息输出到文件中

配置文件格式如下,数字不一定非常准确,仅供参考:

[DEFAULT]
JiShuL = 2193.00
JiShuH = 16446.00
YangLao = 0.08
YiLiao = 0.02
ShiYe = 0.005
GongShang = 0
ShengYu = 0
GongJiJin = 0.06[CHENGDU]
JiShuL = 2193.00
JiShuH = 16446.00
YangLao = 0.08
YiLiao = 0.02
ShiYe = 0.005
GongShang = 0
ShengYu = 0
GongJiJin = 0.06[BEIJING]
JiShuL = 4251.00
JiShuH = 21258.00
YangLao = 0.08
YiLiao = 0.02
ShiYe = 0.002
GongShang = 0
ShengYu = 0
GongJiJin = 0.12

员工工资数据文件格式每行为 工号,税前工资,举例如下:

101,5000
203,6500
309,15000

输出的员工工资单数据文件每行格式为 工号,税前工资,社保金额,个税金额,税后工资,计算时间如下:

101,5000,825.00,0.00,4175.00,2019-02-01 12:09:32
203,6500,1072.50,12.82,5414.68,2019-02-01 12:09:32
309,15000,2475.00,542.50,11982.50,2019-02-01 12:09:32

计算时间为上一挑战中实现的多进程代码中的进程2计算的时间,格式为 年-月-日 小时:分钟:秒。

程序的执行过程如下,注意配置文件和输入的员工数据文件需要你自己创建并填入数据,可以参考上述的内容示例:

$ ./calculator.py -C Chengdu -c test.cfg -d user.csv -o gongzi.csv

执行成功不需要输出信息到屏幕,执行失败或有异常出现则将错误信息输出到屏幕
系统检测说明
后台有多个脚本对程序文件的路径及运行结果进行检测,如果严格按照实验楼楼赛的标准只给出是否准确的反馈则非常不利于新手排错调试,这里将后台使用的部分测试用例提供出来,大家可以在遇到错误的时候先自行进行排错,提供的部分测试用例仅供参考,如果有任何疑问可以在 QQ 讨论组里与同学和助教进行交流。

如果 /home/shiyanlou/calculator.py 已经完成,点击 提交结果 后遇到报错,那么测试用例的临时文件都会被保留,可以进入到测试文件夹中进行排错。

首先,测试脚本会将 /home/shiyanlou/calculator.py 拷贝到 /tmp/test.py,然后会下载以下测试需要的文件:

/tmp/c5.cfg 配置文件
/tmp/c5user.csv 员工工资数据文件
执行的测试命令:

$ python3 /tmp/test.py -C chengdu -c /tmp/c5.cfg -d /tmp/c5user.csv -o /tmp/c5gongzi.csv

排错的时候可以进入到 /tmp 目录,先检查下输出的文件 /tmp/c5gongzi.csv(员工工资单数据文件)是否存在,如果存在,重点检查下里面的工号为 207 的员工税后工资是否为 14467.77,这个地方是最容易出错的地方,通常都是由于社保基数计算的问题导致的,可以确认下。

另外一个容易出错的地方就是 -C chengdu 这个参数,需要注意以下几点:

chengdu 大小写都应该支持
可以准确的找到并从配置文件中加载 [CHENGDU] 这一个 section 的配置信息
如果挑战 PASS 了,那么 /tmp 目录下的测试文件都会被全部删除

# -*- coding: utf-8 -*-
import sys
import csv
import configparser
from getopt import getopt, GetoptError
from datetime import datetime
from collections import namedtuple
import queue
from multiprocessing import Queue, Process# 税率表条目类,该类由 namedtuple 动态创建,代表一个命名元组
IncomeTaxQuickLookupItem = namedtuple('IncomeTaxQuickLookupItem',['start_point', 'tax_rate', 'quick_subtractor']
)# 起征点常量
INCOME_TAX_START_POINT = 5000# 税率表,里面的元素类型为前面创建的 IncomeTaxQuickLookupItem
INCOME_TAX_QUICK_LOOKUP_TABLE = [IncomeTaxQuickLookupItem(80000, 0.45, 15160),IncomeTaxQuickLookupItem(55000, 0.35, 7160),IncomeTaxQuickLookupItem(35000, 0.30, 4410),IncomeTaxQuickLookupItem(25000, 0.25, 2660),IncomeTaxQuickLookupItem(12000, 0.2, 1410),IncomeTaxQuickLookupItem(3000, 0.1, 210),IncomeTaxQuickLookupItem(0, 0.03, 0)
]class Args(object):"""命令行参数处理类"""def __init__(self):# 解析命令行选项self.options = self._options()def _options(self):"""内部函数,用来解析命令行选项,返回保存了所有选项及其取值的字典"""try:# 解析命令行选项和参数,本程序只支持选项,因此忽略返回结果里的参数列表opts, _ = getopt(sys.argv[1:], 'hC:c:d:o:', ['help'])except GetoptError:print('Parameter Error')exit()options = dict(opts)# 处理 -h 或 --help 选项if len(options) == 1 and ('-h' in options or '--help' in options):print('Usage: calculator.py -C cityname -c configfile -d userdata -o resultdata')exit()return optionsdef _value_after_option(self, option):"""内部函数,用来获取跟在选项后面的值"""value = self.options.get(option)# 城市参数可选,其它参数必须提供if value is None and option != '-C':print('Parameter Error')exit()return value@propertydef city(self):"""城市"""return self._value_after_option('-C')@propertydef config_path(self):"""配置文件路径"""return self._value_after_option('-c')@propertydef userdata_path(self):"""用户工资文件路径"""return self._value_after_option('-d')@propertydef export_path(self):"""税后工资文件路径"""return self._value_after_option('-o')# 创建一个全局参数类对象供后续使用
args = Args()class Config(object):"""配置文件处理类"""def __init__(self):# 读取配置文件self.config = self._read_config()def _read_config(self):"""内部函数,用来读取配置文件中指定城市的配置"""config = configparser.ConfigParser()config.read(args.config_path)# 如果指定了城市并且该城市在配置文件中,返回该城市的配置,否则返回默认的配置if args.city and args.city.upper() in config.sections():return config[args.city.upper()]else:return config['DEFAULT']def _get_config(self, key):"""内部函数,用来获得配置项的值"""try:return float(self.config[key])except (ValueError, KeyError):print('Parameter Error')exit()@propertydef social_insurance_baseline_low(self):"""获取社保基数下限"""return self._get_config('JiShuL')@propertydef social_insurance_baseline_high(self):"""获取社保基数上限"""return self._get_config('JiShuH')@propertydef social_insurance_total_rate(self):"""获取社保总费率"""return sum([self._get_config('YangLao'),self._get_config('YiLiao'),self._get_config('ShiYe'),self._get_config('GongShang'),self._get_config('ShengYu'),self._get_config('GongJiJin')])# 创建一个全局的配置文件处理对象供后续使用
config = Config()class UserData(Process):"""用户工资文件处理进程"""def __init__(self, userdata_queue):super().__init__()# 用户数据队列self.userdata_queue = userdata_queuedef _read_users_data(self):"""内部函数,用来读取用户工资文件"""userdata = []with open(args.userdata_path) as f:# 依次读取用户工资文件中的每一行并解析得到用户 ID 和工资for line in f.readlines():employee_id, income_string = line.strip().split(',')try:income = int(income_string)except ValueError:print('Parameter Error')exit()userdata.append((employee_id, income))return userdatadef run(self):"""进程入口方法"""# 从用户数据文件依次读取每条用户数据并写入到队列for item in self._read_users_data():self.userdata_queue.put(item)class IncomeTaxCalculator(Process):"""税后工资计算进程"""def __init__(self, userdata_queue, export_queue):super().__init__()# 用户数据队列self.userdata_queue = userdata_queue# 导出数据队列self.export_queue = export_queue@staticmethoddef calc_social_insurance_money(income):"""计算应纳税额"""if income < config.social_insurance_baseline_low:return config.social_insurance_baseline_low * \config.social_insurance_total_rateelif income > config.social_insurance_baseline_high:return config.social_insurance_baseline_high * \config.social_insurance_total_rateelse:return income * config.social_insurance_total_rate@classmethoddef calc_income_tax_and_remain(cls, income):"""计算税后工资"""# 计算社保金额social_insurance_money = cls.calc_social_insurance_money(income)# 计算应纳税额real_income = income - social_insurance_moneytaxable_part = real_income - INCOME_TAX_START_POINT# 从高到低判断落入的税率区间,如果找到则用该区间的参数计算纳税额并返回结果for item in INCOME_TAX_QUICK_LOOKUP_TABLE:if taxable_part > item.start_point:tax = taxable_part * item.tax_rate - item.quick_subtractorreturn '{:.2f}'.format(tax), '{:.2f}'.format(real_income - tax)# 如果没有落入任何区间,则返回 0return '0.00', '{:.2f}'.format(real_income)def calculate(self, employee_id, income):"""计算单个用户的税后工资"""# 计算社保金额social_insurance_money = '{:.2f}'.format(self.calc_social_insurance_money(income))# 计算税后工资tax, remain = self.calc_income_tax_and_remain(income)return [employee_id, income, social_insurance_money, tax, remain,datetime.now().strftime('%Y-%m-%d %H:%M:%S')]def run(self):"""进程入口方法"""# 从用户数据队列读取用户数据,计算用户税后工资,然后写入到导出数据队列while True:# 获取下一个用户数据try:# 超时时间为 1 秒,如果超时则认为没有需要处理的数据,退出进程employee_id, income = self.userdata_queue.get(timeout=1)except queue.Empty:return# 计算税后工资result = self.calculate(employee_id, income)# 将结果写入到导出数据队列self.export_queue.put(result)class IncomeTaxExporter(Process):"""税后工资导出进程"""def __init__(self, export_queue):super().__init__()# 导出数据队列self.export_queue = export_queue# 创建 CSV 写入器self.file = open(args.export_path, 'w', newline='')self.writer = csv.writer(self.file)def run(self):"""进程入口方法"""# 从导出数据队列读取导出数据,写入到导出文件中while True:# 获取下一个导出数据try:# 超时时间为 1 秒,如果超时则认为没有需要处理的数据,退出进程item = self.export_queue.get(timeout=1)except queue.Empty:# 退出时关闭文件self.file.close()return# 写入到导出文件self.writer.writerow(item)if __name__ == '__main__':# 创建进程之间通信的队列userdata_queue = Queue()export_queue = Queue()# 用户数据进程userdata = UserData(userdata_queue)# 税后工资计算进程calculator = IncomeTaxCalculator(userdata_queue, export_queue)# 税后工资导出进程exporter = IncomeTaxExporter(export_queue)# 启动进程userdata.start()calculator.start()exporter.start()# 等待所有进程结束userdata.join()calculator.join()exporter.join()

2.getopt介绍
getopt这个函数 就是用来抽取 sys.argv 获得的用户输入来确定执行步骤。

getopt是个模块,而这个模块里面又有getopt 函数,所以getopt需要这样这样用。

getopt.getopt( [命令行参数列表], "短选项", [长选项列表] )

该函数返回两个值. opts 和args

opts 是一个存有所有选项及其输入值的元组.当输入确定后,这个值不能被修改了.

args 是去除有用的输入以后剩余的部分.

import getopt,sys
shortargs = 'f:t' #短选项
longargs = ['directory-prefix=', 'format', '--f_long='] #长选项
opts,args= getopt.getopt( sys.argv[1:], shortargs, longargs)
print 'opts=',opts
print 'args=',args

getopt函数的格式是getopt.getopt ( [命令行参数列表], “短选项”, [长选项列表] )
短选项名后的冒号(:)表示该选项必须有附加的参数。
长选项名后的等号(=)表示该选项必须有附加的参数。

try:  opts, args = getopt.getopt(sys.argv[1:], "ho:", ["help", "output="])
except getopt.GetoptError:  # print help information and exit:

解释如下:

  1. 处理所使用的函数叫getopt() ,因为是直接使用import 导入的getopt 模块,所以要加上限定getopt 才可以。
  2. 使用sys.argv[1:] 过滤掉第一个参数(它是执行脚本的名字,不应算作参数的一部分)。
  3. 使用短格式分析串"ho:" 。当一个选项只是表示开关状态时,即后面不带附加参数时,在分析串中写入选项字符。当选项后面是带一个附加参数时,在分析串中写入选项字符同时后面加一 个":" 号 。所以"ho:" 就表示"h" 是一个开关选项;“o:” 则表示后面应该带一个参数。
  4. 使用长格式分析串列表:[“help”, “output=”] 。长格式串也可以有开关状态,即后面不跟"=" 号。如果跟一个等号则表示后面还应有一个参数 。这个长格式表示"help" 是一个开关选项;“output=” 则表示后面应该带一个参数。
  5. 调用getopt 函数。函数返回两个列表:opts 和args 。opts 为分析出的格式信息。args 为不属于格式信息的剩余的命令行参数。opts 是一个两元组的列表。每个元素为:( 选项串, 附加参数) 。如果没有附加参数则为空串’’ 。
  6. 整个过程使用异常来包含,这样当分析出错时,就可以打印出使用信息来通知用户如何使用这个程序。

如上面解释的一个命令行例子为:
‘-h -o file --help --output=out file1 file2’
在分析完成后,opts 应该是:
[(’-h’, ‘’), (’-o’, ‘file’), (’–help’, ‘’), (’–output’, ‘out’)]
而args 则为:
[‘file1’, ‘file2’]
转载:https://www.cnblogs.com/chushiyaoyue/p/5380022.html

3.Python3中的configparser模块
https://blog.csdn.net/atlansi/article/details/83243478

configparser模块简介
该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)与java原先的配置文件相同的格式

看一下configparser生成的配置文件的格式

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes[bitbucket.org]
User = Atlan[topsecret.server.com]
Port = 50022
ForwardX11 = no

现在看一下类似上方的配置文件是如何生成的

import configparser #引入模块config = configparser.ConfigParser()    #类中一个方法 #实例化一个对象config["DEFAULT"] = {'ServerAliveInterval': '45','Compression': 'yes','CompressionLevel': '9','ForwardX11':'yes'}   #类似于操作字典的形式config['bitbucket.org'] = {'User':'Atlan'} #类似于操作字典的形式config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'}with open('example.ini', 'w') as configfile:config.write(configfile) #将对象写入文件

解释一下,操作方式

config["DEFAULT"] = {'ServerAliveInterval': '45','Compression': 'yes','CompressionLevel': '9','ForwardX11':'yes'} #类似于操作字典的形式
#config后面跟的是一个section的名字,section的段的内容的创建类似于创建字典。类似与字典当然还有别的操作方式啦!
config['bitbucket.org'] = {'User':'Atlan'}  #类似与最经典的字典操作方式

和字典的操作方式相比,configparser模块的操作方式,无非是在实例化的对象后面,跟一个section,在紧跟着设置section的属性(类似字典的形式)

读文件内容
import configparserconfig = configparser.ConfigParser()#---------------------------查找文件内容,基于字典的形式print(config.sections())        #  []config.read('example.ini')print(config.sections())        #   ['bitbucket.org', 'topsecret.server.com']print('bytebong.com' in config) # False
print('bitbucket.org' in config) # Trueprint(config['bitbucket.org']["user"])  # Atlanprint(config['DEFAULT']['Compression']) #yesprint(config['topsecret.server.com']['ForwardX11'])  #noprint(config['bitbucket.org'])          #<Section: bitbucket.org>for key in config['bitbucket.org']:     # 注意,有default会默认default的键print(key)print(config.options('bitbucket.org'))  # 同for循环,找到'bitbucket.org'下所有键print(config.items('bitbucket.org'))    #找到'bitbucket.org'下所有键值对print(config.get('bitbucket.org','compression')) # yes       get方法Section下的key对应的value

修改

import configparserconfig = configparser.ConfigParser()config.read('example.ini')  #读文件config.add_section('yuan')  #添加sectionconfig.remove_section('bitbucket.org') #删除section
config.remove_option('topsecret.server.com',"forwardx11") #删除一个配置想config.set('topsecret.server.com','k1','11111')
config.set('yuan','k2','22222')
with open('new2.ini','w') as f:config.write(f)

使用模块优化工资计算器相关推荐

  1. 一个基于angularJS的工资计算器

    先看界面: 其实在ng中最让人印象深刻的就是数据的双向绑定,在html中就完成了很多操作.大概用到的就是控制器视图服务等,没有分模块写控制器,代码如下: <html ng-app = " ...

  2. 工资计算器非常的好用

    自学后第一次做小模块:package jisuanqi;import java.awt.*; import java.text.DecimalFormat; import java.util.Date ...

  3. python制作工资计算器-Python计算个人所得税

    不知道大家有没有细心观察自己工资条上个人所得税呢?很多人都觉得有专门的个人所得税计算器,不用再去复查,但是有的黑心的公司专门利用这种心理,偶尔增加你的扣税额,为了方便大家核查自己的个人所得税,我用Py ...

  4. python制作工资计算器-Python制作个税计算器

    不知道大家有没有细心观察自己工资条上个人所得税呢?很多人都觉得有专门的个人所得税计算器,不用再去复查,但是有的黑心的公司专门利用这种心理,偶尔增加你的扣税额,为了方便大家核查自己的个人所得税,我用Py ...

  5. python制作工资计算器-Python实现扣除个人税后的工资计算器示例

    本文实例讲述了Python实现扣除个人税后的工资计算器.分享给大家供大家参考,具体如下: 正好处于找工作期间避免不了会跟单位谈论薪资的情况,当然所有人跟你谈的都是税前收入,税后应该实际收入有多少呢?今 ...

  6. python计算每月工资-Python实现扣除个人税后的工资计算器示例

    本文实例讲述了Python实现扣除个人税后的工资计算器.分享给大家供大家参考,具体如下: 正好处于找工作期间避免不了会跟单位谈论薪资的情况,当然所有人跟你谈的都是税前收入,税后应该实际收入有多少呢?今 ...

  7. python计算工资编程-Python实现扣除个人税后的工资计算器示例

    本文实例讲述了Python实现扣除个人税后的工资计算器.分享给大家供大家参考,具体如下: 正好处于找工作期间避免不了会跟单位谈论薪资的情况,当然所有人跟你谈的都是税前收入,税后应该实际收入有多少呢?今 ...

  8. python实验楼工资计算器_挑战:完善工资计算器

    完善工资计算器 项目需求改变: 输出税后工资 计算过程需要扣除社会保险费用 支持多人同时计算工资 打印税后工资列表 个税计算公式: 应纳税所得额 = 工资金额 - 各项社会保险费 - 起征点(3500 ...

  9. Android计步模块优化(今日步数)

    简书地址:https://www.jianshu.com/p/cfc2a200e46d github地址:https://github.com/jiahongfei/TodayStepCounter ...

  10. 新版个人所得税计算python_2019年新税率工资计算器(python脚本) | linux系统运维...

    #!/usr/bin/evn python # coding=utf-8 # 2019年新税率个人所得税计算器,本脚本仅供学习 # blog: http://www.linuxyw.com # aut ...

最新文章

  1. 第十七课.Pytorch-geometric入门(二)
  2. 在docker的Linux容器搭建前端开发环境
  3. matlab--离散(discrete)数据绘图
  4. 需要某个字体包才能正确显示此页面_如何做设计,才能做好设计
  5. Codeforces Round #448 (Div. 2)
  6. android 解压zip报错_Android 使用 Batterystats 和 Battery Historian 分析电池用量
  7. 一加7 Pro在京东开启预约:人数超34万
  8. CS 231n 学习笔记 03——课程3.1 损失函数
  9. 代码阅读工具-global
  10. 邓侃:中国首个全过程智能诊疗系统,全方位披露技术核心和商业模式
  11. lua 函数 默认值_简明lua教程[转]
  12. 【科普】如何判断论文是不是SCI/EI?
  13. java图标中设置背景、文本框设置大小、标签中字体的格式
  14. 史上最恐怖的10篇超短篇鬼故事(转…
  15. 软件测试质量度量,软件测试过程质量的度量
  16. VSCode快捷键配置复制一行
  17. 网络猫只有计算机和机顶盒两个口,电信的猫上有两个网口,但是只能用网口1,这是为什么。360......
  18. 程序员必备神器,脑洞不是一般大!
  19. 也谈般若波罗密多心经与计算机世界
  20. 基于Android的外卖App系统设计

热门文章

  1. VMware8安装教程
  2. GPS数据格式的分析与处理
  3. C语言编程题如何提交源程序,c语言课后习题源程序.doc
  4. 常见积分求导公式表--便于记忆
  5. OpenCore黑苹果引导配置说明-基于OpenCore-0.7.1-07-06正式版
  6. php smarty配置,PHP中使用Smarty模板目录结构配置
  7. csrss32.exe
  8. 炒股高手必备抓大牛主图筹码箱体分析指标
  9. linux系统下制作启动u盘,在Linux系统下如何制作创建win10启动盘U盘启动?
  10. 微软windows10易升_Win10 如何摆脱易升这个流氓软件,如何拒绝每半年一次的大更...