学习了一些Python的基础知识之后,就开始来实践吧。
上手非常简单,有过一定编程经验的人大约可以在几个小时之内掌握基本的Python编程方法。
正好在学习设计模式课程,会有几次课程作业,就直接拿过来权当Python的一次编程练习了
第一次任务简介:
实现一个管理雇员薪水的工具,可以打印出员工的薪水信息。
雇员分为普通和经理,各自有不同的薪水计算方法。
Ok,Let's go!
首先复习一下Python里的面向对象相关知识,定义一个雇员的基类EmployeeBase
并且在__init__方法里定义了三个成员name, base_salary和overtime_days
需要注意的就是所有的类成员方法在声明的时候都需要有一个默认的参数self,这个相当于this指针的东西必须要写上,而且访问类成员或者方法也都要加上这个前缀,不然会出错。
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
接下来就是普通雇员类Employee,继承的语法:
Employee(EmployeeBase)
以及类的__doc__,养成写注释的习惯。
Line 6:
__init__并不是构造函数,并且Python里也没有构造函数这个概念。所以要手动的调用基类的__init__方法GetSalary中计算薪水,普通雇员的加班工资是双倍计算的。
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())
到这里,雇员类已经完成,下面就是——
单元测试。一般我以前写程序都很少写单元测试的,因为很费时,要写很多测试的代码。
但是这里我想特别尝试一下,以展现Python里的各种特性和工具。
新版本的Python里有一个专门用于测试的unittest模块,导入就可以使用了,下面新建一个Testsalary.py进行单元测试。
首先unittest模块里有一个测试用例的基类
TestCase,我们只要在TestCase的派生类里定义自己的测试用例方法就可以使用了,这些测试用命方法甚至都不需要自己调用,只要方法
名字以test开头,TestCase就会自动的调用它们。
在下面的测试代码中,我定义了testNoneEmployee等几个测试方法,主要是使用了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()
运行之,出现结果:
----------------------------------------------------------------------
Ran 5 tests in 0.007s
OK
全部通过,太没意思了,想着要加点什么好呢。下面我对构造Employee的时候加上参数的检查,不允许不正确的参数输入,例如名字太长,工资和加班天数为负数等。我希望在试图用这些不正确的参数进行创建Employee对象的时候出现异常。
在测试代码中加入另两个方法,使用了TestCase里的另一个方法
assertRaises,这个方法可以检测被测对象是否按抛出了预期的异常。这个方法接受一个异常类型,和一个会产生这种异常的“对象”,通常是一个函数,以及它们的参数
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,这下测试通不过了。下面就需要修改Employee类,首先提一下Python里的异常处理,使用try...except...处理异常,raise抛出异常,finally可以用来做一些善后处理工作。
下面先要定义上面用到的这两种异常,InvalidName和InvalidInput,这里简单的继承一下Python里的Exception类就可以了,然后修改Employee的__init__方法,进行参数的查检,修正后如下:
#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,第一步顺利完成,下面就是经理类。经理的工资由加班工资和绩效组成,加班工资只跟经理的等级有关,它与绩效工资都是一个固定值,基本上没有什么难度。