學(xué)習(xí)了一些Python的基礎(chǔ)知識(shí)之后,就開始來(lái)實(shí)踐吧。
上手非常簡(jiǎn)單,有過(guò)一定編程經(jīng)驗(yàn)的人大約可以在幾個(gè)小時(shí)之內(nèi)掌握基本的Python編程方法。
正好在學(xué)習(xí)設(shè)計(jì)模式課程,會(huì)有幾次課程作業(yè),就直接拿過(guò)來(lái)權(quán)當(dāng)Python的一次編程練習(xí)了
第一次任務(wù)簡(jiǎn)介:
實(shí)現(xiàn)一個(gè)管理雇員薪水的工具,可以打印出員工的薪水信息。
雇員分為普通和經(jīng)理,各自有不同的薪水計(jì)算方法。
Ok,Let's go!
首先復(fù)習(xí)一下Python里的面向?qū)ο笙嚓P(guān)知識(shí),定義一個(gè)雇員的基類EmployeeBase
并且在__init__方法里定義了三個(gè)成員name, base_salary和overtime_days
需要注意的就是所有的類成員方法在聲明的時(shí)候都需要有一個(gè)默認(rèn)的參數(shù)self,這個(gè)相當(dāng)于this指針的東西必須要寫上,而且訪問(wèn)類成員或者方法也都要加上這個(gè)前綴,不然會(huì)出錯(cuò)。
1?class?EmployeeBase():
2?????"""Base?class?of?all?staffs"""
3?????def?__init__(self,?name?=?"",?basesalary?=?0,?overtime?=?0):
4?????????self.name?=?name
5?????????self.base_salary?=?basesalary
6?????????self.overtime_days?=?overtime
7?????def?GetName(self):
8?????????return?self.name
接下來(lái)就是普通雇員類Employee,繼承的語(yǔ)法:
Employee(EmployeeBase)
以及類的__doc__,養(yǎng)成寫注釋的習(xí)慣。
Line 6:
__init__并不是構(gòu)造函數(shù),并且Python里也沒(méi)有構(gòu)造函數(shù)這個(gè)概念。所以要手動(dòng)的調(diào)用基類的__init__方法GetSalary中計(jì)算薪水,普通雇員的加班工資是雙倍計(jì)算的。
?1?class?Employee(EmployeeBase):
?2?????"""Common?employee?class?derived?from?EmployeeBase.
?3?????A?employee?can?has?overtime?salary,?every?overtime?work's?salary?will?be?doubled
?4?????"""
?5?????def?__init__(self,?name="",?basesalary=0,?overtime=0):
?6???????? EmployeeBase.__init__(self,name,basesalary,overtime)
?7?????def?GetStaffType(self):
?8?????????return?"Employee"
?9?????def?GetSalary(self):
10?????????return?self.base_salary?+?self.base_salary/30*2*self.overtime_days
11?????def?GetSalaryInfo(self):
12?????????return?self.name?+?"?:?"?+?self.GetStaffType()+?"?:?"?+?str(self.GetSalary())
到這里,雇員類已經(jīng)完成,下面就是——
單元測(cè)試。一般我以前寫程序都很少寫單元測(cè)試的,因?yàn)楹苜M(fèi)時(shí),要寫很多測(cè)試的代碼。
但是這里我想特別嘗試一下,以展現(xiàn)Python里的各種特性和工具。
新版本的Python里有一個(gè)專門用于測(cè)試的unittest模塊,導(dǎo)入就可以使用了,下面新建一個(gè)Testsalary.py進(jìn)行單元測(cè)試。
首先unittest模塊里有一個(gè)測(cè)試用例的基類
TestCase,我們只要在TestCase的派生類里定義自己的測(cè)試用例方法就可以使用了,這些測(cè)試用命方法甚至都不需要自己調(diào)用,只要方法
名字以test開頭,TestCase就會(huì)自動(dòng)的調(diào)用它們。
在下面的測(cè)試代碼中,我定義了testNoneEmployee等幾個(gè)測(cè)試方法,主要是使用了TestCase里的
assertEqual方法。
"""Unit?test?for?salary.py"""
import?unittest
from salary importEmployee
class?TestEmployee(unittest.TestCase):
????"""Unit?test?for?clas?Employee"""
????def?testNoneEmployee(self):
????????e?=?Employee()
????????self.assertEqual("",e.GetName())
????????self.assertEqual(0,e.GetSalary())
????????self.assertEqual("Employee",e.GetStaffType())
????def?testBaseSalary(self):
????????e?=?Employee("emp",?300)
????????self.assertEqual("emp",e.GetName())
????????self.assertEqual(300,e.GetSalary())
????????self.assertEqual("Employee",e.GetStaffType())
????def?testNoOvertime(self):
????????e?=?Employee("emp",?300,?0)
????????self.assertEqual(300,e.GetSalary())
????????self.assertEqual("emp?:?Employee?:?300",?e.GetSalaryInfo())
????def?testOvertime1(self):
????????e?=?Employee("emp",?300,?1)
????????self.assertEqual(320,e.GetSalary())
????????self.assertEqual("emp?:?Employee?:?320",?e.GetSalaryInfo())
????def?testOvertime2(self):
????????e?=?Employee("emp",?300,?5)
????????self.assertEqual(400,e.GetSalary())
????????self.assertEqual("emp?:?Employee?:?400",?e.GetSalaryInfo())
????????
if?__name__?==?"__main__":
????unittest.main()
運(yùn)行之,出現(xiàn)結(jié)果:
----------------------------------------------------------------------
Ran 5 tests?in?0.007s
OK
全部通過(guò),太沒(méi)意思了,想著要加點(diǎn)什么好呢。下面我對(duì)構(gòu)造Employee的時(shí)候加上參數(shù)的檢查,不允許不正確的參數(shù)輸入,例如名字太長(zhǎng),工資和加班天數(shù)為負(fù)數(shù)等。我希望在試圖用這些不正確的參數(shù)進(jìn)行創(chuàng)建Employee對(duì)象的時(shí)候出現(xiàn)異常。
在測(cè)試代碼中加入另兩個(gè)方法,使用了TestCase里的另一個(gè)方法
assertRaises,這個(gè)方法可以檢測(cè)被測(cè)對(duì)象是否按拋出了預(yù)期的異常。這個(gè)方法接受一個(gè)異常類型,和一個(gè)會(huì)產(chǎn)生這種異常的“對(duì)象”,通常是一個(gè)函數(shù),以及它們的參數(shù)
????def?testInvalidName(self):
????????self.assertRaises(InvalidName,?Employee,?"name?too?long")
????def?testInvalidInput1(self):
????????self.assertRaises(InvalidInput,?Employee,?"emp",?-300)
????def?testInvalidInput2(self):
????????self.assertRaises(InvalidInput,?Employee,?"emp",?300,?-5)
兩種異常類的名字分別叫InvalidName和InvalidInput,這下測(cè)試通不過(guò)了。下面就需要修改Employee類,首先提一下Python里的異常處理,使用try...except...處理異常,raise拋出異常,finally可以用來(lái)做一些善后處理工作。
下面先要定義上面用到的這兩種異常,InvalidName和InvalidInput,這里簡(jiǎn)單的繼承一下Python里的Exception類就可以了,然后修改Employee的__init__方法,進(jìn)行參數(shù)的查檢,修正后如下:
#new?type?of?Expceptions
class?InvalidName(Exception):pass
class?InvalidInput(Exception):pass
#----------------------
????def?__init__(self,?name="",?basesalary=0,?overtime=0):
????????#raise?exceptions?if?input?is?invalidate
????????if?len(name)?>?8:
????????????raise?InvalidName,?"Error:?Name?too?long!"
????????if?basesalary?<?0?or?basesalary?>?300000:
????????????raise?InvalidInput,?"Error:?Base?salary?should?between?0~300000!"
????????#use?int(overtime)?!=?overtime?to?check?if?the?input?is?int
????????if?overtime?<?0?or?overtime??>?31?or?int(overtime)?!=?overtime:
????????????raise?InvalidInput,?"Error:?Overtime?should?between?0~31!"
????????EmployeeBase.__init__(self,name,basesalary,overtime)
ps:異常類型后面的文字是為了給,異常處理提供信息用的。
OK,第一步順利完成,下面就是經(jīng)理類。經(jīng)理的工資由加班工資和績(jī)效組成,加班工資只跟經(jīng)理的等級(jí)有關(guān),它與績(jī)效工資都是一個(gè)固定值,基本上沒(méi)有什么難度。