日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用模块优化工资计算器

發(fā)布時間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用模块优化工资计算器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

建立在多進程的基礎之上,使用模塊進行優(yōu)化
介紹
優(yōu)化上一個挑戰(zhàn)中完成的計算器,完善下述需求:

  • 使用 getopt 模塊處理命令行參數(shù)
  • 使用 Python3 中的 configparser 模塊讀取配置文件
  • 使用 datetime模塊寫入工資單生成時間

計算器執(zhí)行中包含下面的參數(shù):

  • -h 或 --help,打印當前計算器的使用方法,內(nèi)容為:
Usage: calculator.py -C cityname -c configfile -d userdata -o resultdata
  • -C 城市名稱 指定使用某個城市的社保配置信息,如果沒有使用該參數(shù),則使用配置文件中 [DEFAULT] 欄目中的數(shù)據(jù),城市名稱不區(qū)分大小寫,比如配置文件中寫的是 [CHENGDU],這里參數(shù)可以寫 -C Chengdu,仍然可以匹配

  • -c 配置文件 配置文件,由于各地的社保比例稍有不同,我們將多個城市的不同配置信息寫入一個配置文件

  • -d 員工工資數(shù)據(jù)文件 指定員工工資數(shù)據(jù)文件,文件中包含兩列內(nèi)容,分別為員工工號和工資金額

  • -o 員工工資單數(shù)據(jù)文件 輸出內(nèi)容,將員工繳納的社保、稅前、稅后工資等詳細信息輸出到文件中

配置文件格式如下,數(shù)字不一定非常準確,僅供參考:

[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

員工工資數(shù)據(jù)文件格式每行為 工號,稅前工資,舉例如下:

101,5000 203,6500 309,15000

輸出的員工工資單數(shù)據(jù)文件每行格式為 工號,稅前工資,社保金額,個稅金額,稅后工資,計算時間如下:

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

計算時間為上一挑戰(zhàn)中實現(xiàn)的多進程代碼中的進程2計算的時間,格式為 年-月-日 小時:分鐘:秒。

程序的執(zhí)行過程如下,注意配置文件和輸入的員工數(shù)據(jù)文件需要你自己創(chuàng)建并填入數(shù)據(jù),可以參考上述的內(nèi)容示例:

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

執(zhí)行成功不需要輸出信息到屏幕,執(zhí)行失敗或有異常出現(xiàn)則將錯誤信息輸出到屏幕
系統(tǒng)檢測說明
后臺有多個腳本對程序文件的路徑及運行結(jié)果進行檢測,如果嚴格按照實驗樓樓賽的標準只給出是否準確的反饋則非常不利于新手排錯調(diào)試,這里將后臺使用的部分測試用例提供出來,大家可以在遇到錯誤的時候先自行進行排錯,提供的部分測試用例僅供參考,如果有任何疑問可以在 QQ 討論組里與同學和助教進行交流。

如果 /home/shiyanlou/calculator.py 已經(jīng)完成,點擊 提交結(jié)果 后遇到報錯,那么測試用例的臨時文件都會被保留,可以進入到測試文件夾中進行排錯。

首先,測試腳本會將 /home/shiyanlou/calculator.py 拷貝到 /tmp/test.py,然后會下載以下測試需要的文件:

/tmp/c5.cfg 配置文件
/tmp/c5user.csv 員工工資數(shù)據(jù)文件
執(zhí)行的測試命令:

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

排錯的時候可以進入到 /tmp 目錄,先檢查下輸出的文件 /tmp/c5gongzi.csv(員工工資單數(shù)據(jù)文件)是否存在,如果存在,重點檢查下里面的工號為 207 的員工稅后工資是否為 14467.77,這個地方是最容易出錯的地方,通常都是由于社保基數(shù)計算的問題導致的,可以確認下。

另外一個容易出錯的地方就是 -C chengdu 這個參數(shù),需要注意以下幾點:

chengdu 大小寫都應該支持
可以準確的找到并從配置文件中加載 [CHENGDU] 這一個 section 的配置信息
如果挑戰(zhàn) 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 動態(tài)創(chuàng)建,代表一個命名元組 IncomeTaxQuickLookupItem = namedtuple('IncomeTaxQuickLookupItem',['start_point', 'tax_rate', 'quick_subtractor'] )# 起征點常量 INCOME_TAX_START_POINT = 5000# 稅率表,里面的元素類型為前面創(chuàng)建的 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):"""命令行參數(shù)處理類"""def __init__(self):# 解析命令行選項self.options = self._options()def _options(self):"""內(nèi)部函數(shù),用來解析命令行選項,返回保存了所有選項及其取值的字典"""try:# 解析命令行選項和參數(shù),本程序只支持選項,因此忽略返回結(jié)果里的參數(shù)列表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):"""內(nèi)部函數(shù),用來獲取跟在選項后面的值"""value = self.options.get(option)# 城市參數(shù)可選,其它參數(shù)必須提供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')# 創(chuàng)建一個全局參數(shù)類對象供后續(xù)使用 args = Args()class Config(object):"""配置文件處理類"""def __init__(self):# 讀取配置文件self.config = self._read_config()def _read_config(self):"""內(nèi)部函數(shù),用來讀取配置文件中指定城市的配置"""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):"""內(nèi)部函數(shù),用來獲得配置項的值"""try:return float(self.config[key])except (ValueError, KeyError):print('Parameter Error')exit()@propertydef social_insurance_baseline_low(self):"""獲取社保基數(shù)下限"""return self._get_config('JiShuL')@propertydef social_insurance_baseline_high(self):"""獲取社保基數(shù)上限"""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')])# 創(chuàng)建一個全局的配置文件處理對象供后續(xù)使用 config = Config()class UserData(Process):"""用戶工資文件處理進程"""def __init__(self, userdata_queue):super().__init__()# 用戶數(shù)據(jù)隊列self.userdata_queue = userdata_queuedef _read_users_data(self):"""內(nèi)部函數(shù),用來讀取用戶工資文件"""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):"""進程入口方法"""# 從用戶數(shù)據(jù)文件依次讀取每條用戶數(shù)據(jù)并寫入到隊列for item in self._read_users_data():self.userdata_queue.put(item)class IncomeTaxCalculator(Process):"""稅后工資計算進程"""def __init__(self, userdata_queue, export_queue):super().__init__()# 用戶數(shù)據(jù)隊列self.userdata_queue = userdata_queue# 導出數(shù)據(jù)隊列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# 從高到低判斷落入的稅率區(qū)間,如果找到則用該區(qū)間的參數(shù)計算納稅額并返回結(jié)果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)# 如果沒有落入任何區(qū)間,則返回 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):"""進程入口方法"""# 從用戶數(shù)據(jù)隊列讀取用戶數(shù)據(jù),計算用戶稅后工資,然后寫入到導出數(shù)據(jù)隊列while True:# 獲取下一個用戶數(shù)據(jù)try:# 超時時間為 1 秒,如果超時則認為沒有需要處理的數(shù)據(jù),退出進程employee_id, income = self.userdata_queue.get(timeout=1)except queue.Empty:return# 計算稅后工資result = self.calculate(employee_id, income)# 將結(jié)果寫入到導出數(shù)據(jù)隊列self.export_queue.put(result)class IncomeTaxExporter(Process):"""稅后工資導出進程"""def __init__(self, export_queue):super().__init__()# 導出數(shù)據(jù)隊列self.export_queue = export_queue# 創(chuàng)建 CSV 寫入器self.file = open(args.export_path, 'w', newline='')self.writer = csv.writer(self.file)def run(self):"""進程入口方法"""# 從導出數(shù)據(jù)隊列讀取導出數(shù)據(jù),寫入到導出文件中while True:# 獲取下一個導出數(shù)據(jù)try:# 超時時間為 1 秒,如果超時則認為沒有需要處理的數(shù)據(jù),退出進程item = self.export_queue.get(timeout=1)except queue.Empty:# 退出時關閉文件self.file.close()return# 寫入到導出文件self.writer.writerow(item)if __name__ == '__main__':# 創(chuàng)建進程之間通信的隊列userdata_queue = Queue()export_queue = Queue()# 用戶數(shù)據(jù)進程userdata = UserData(userdata_queue)# 稅后工資計算進程calculator = IncomeTaxCalculator(userdata_queue, export_queue)# 稅后工資導出進程exporter = IncomeTaxExporter(export_queue)# 啟動進程userdata.start()calculator.start()exporter.start()# 等待所有進程結(jié)束userdata.join()calculator.join()exporter.join()

2.getopt介紹
getopt這個函數(shù) 就是用來抽取 sys.argv 獲得的用戶輸入來確定執(zhí)行步驟。

getopt是個模塊,而這個模塊里面又有getopt 函數(shù),所以getopt需要這樣這樣用。

getopt.getopt( [命令行參數(shù)列表], "短選項", [長選項列表] )

該函數(shù)返回兩個值. 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函數(shù)的格式是getopt.getopt ( [命令行參數(shù)列表], “短選項”, [長選項列表] )
短選項名后的冒號(:)表示該選項必須有附加的參數(shù)。
長選項名后的等號(=)表示該選項必須有附加的參數(shù)。

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

解釋如下:

  • 處理所使用的函數(shù)叫getopt() ,因為是直接使用import 導入的getopt 模塊,所以要加上限定getopt 才可以。
  • 使用sys.argv[1:] 過濾掉第一個參數(shù)(它是執(zhí)行腳本的名字,不應算作參數(shù)的一部分)。
  • 使用短格式分析串"ho:" 。當一個選項只是表示開關狀態(tài)時,即后面不帶附加參數(shù)時,在分析串中寫入選項字符。當選項后面是帶一個附加參數(shù)時,在分析串中寫入選項字符同時后面加一 個":" 號 。所以"ho:" 就表示"h" 是一個開關選項;“o:” 則表示后面應該帶一個參數(shù)。
  • 使用長格式分析串列表:[“help”, “output=”] 。長格式串也可以有開關狀態(tài),即后面不跟"=" 號。如果跟一個等號則表示后面還應有一個參數(shù) 。這個長格式表示"help" 是一個開關選項;“output=” 則表示后面應該帶一個參數(shù)。
  • 調(diào)用getopt 函數(shù)。函數(shù)返回兩個列表:opts 和args 。opts 為分析出的格式信息。args 為不屬于格式信息的剩余的命令行參數(shù)。opts 是一個兩元組的列表。每個元素為:( 選項串, 附加參數(shù)) 。如果沒有附加參數(shù)則為空串’’ 。
  • 整個過程使用異常來包含,這樣當分析出錯時,就可以打印出使用信息來通知用戶如何使用這個程序。
  • 如上面解釋的一個命令行例子為:
    ‘-h -o file --help --output=out file1 file2’
    在分析完成后,opts 應該是:
    [(’-h’, ‘’), (’-o’, ‘file’), (’–help’, ‘’), (’–output’, ‘out’)]
    而args 則為:
    [‘file1’, ‘file2’]
    轉(zhuǎn)載:https://www.cnblogs.com/chushiyaoyue/p/5380022.html

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

    configparser模塊簡介
    該模塊適用于配置文件的格式與windows ini文件類似,可以包含一個或多個節(jié)(section),每個節(jié)可以有多個參數(shù)(鍵=值)與java原先的配置文件相同的格式

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

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

    現(xiàn)在看一下類似上方的配置文件是如何生成的

    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的段的內(nèi)容的創(chuàng)建類似于創(chuàng)建字典。類似與字典當然還有別的操作方式啦! config['bitbucket.org'] = {'User':'Atlan'} #類似與最經(jīng)典的字典操作方式

    和字典的操作方式相比,configparser模塊的操作方式,無非是在實例化的對象后面,跟一個section,在緊跟著設置section的屬性(類似字典的形式)

    讀文件內(nèi)容 import configparserconfig = configparser.ConfigParser()#---------------------------查找文件內(nèi)容,基于字典的形式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循環(huán),找到'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)

    總結(jié)

    以上是生活随笔為你收集整理的使用模块优化工资计算器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。