一、编写类 RegularPolygon,表示正 n 边形。类包括:
私有成员 n,要求为整型,代表正 n 边形边的数量,注意n> 3;
私有成员 side,代表正 n 边形每条边的长度;
私有成员 x,代表正 n 边形中心的坐标在 x 轴上的数值;
私有成员 y,代表正 n 边形中心的坐标在 y 轴上的数值;
构造函数,输入参数为 n(默认为 3),side(默认为 1),x(默认为 0), y(默认为 0);
方法 getPerimeter,返回正 n 边形的周长;
方法 getArea,返回正 n 边形的面积;
方法 distanceToPolygon,输入参数为另外一个正 n 边形,返回两个正 n 边形中心的距离。
自行设计测试函数验证 RegularPolygon 类代码的正确性。
(1)大致思路:
定义一个RegularPolygon类,并定义四个私有变量,并赋初始值。然后定义每个方法,首先,获取周长方法只需直接返回边长与边数的积即可。获取面积方法则利用Python中的math库以及正多边形面积公式直接进行计算即可。获取多边形中心距离方法则直接获取当前中心点坐标以及传入参数的中心点坐标,并利用公式直接计算即可。
(2)编程实现:
# 引入math库进行三角函数计算以获得多边形面积 import math # 定义RegularPolygon类 class RegularPolygon: # 初始化构造函数,并声明各私有变量 def __init__(self, n=3, side=1, x=0, y=0): self._n = n self._side = side self._x = x self._y = y # 重载__str__函数便于输出 def __str__(self): return 'This is a regularPolygon:' + '\n' + 'Number of side: ' + str(self._n) + '\n' + 'Length of side: ' + str( self._side) + '\n' + 'Position of centre: (' + str(self._x) + ' , ' + str(self._y) + ')' + '\n' # 定义获取周长函数 def getPerimeter(self): return self._n * self._side # 定义获取面积函数 def getArea(self): return 0.25 * self._n * self._side * self._side / math.tan(math.pi / self._n) # 定义计算到另一多边形中心点距离函数 def distanceToPolygon(self, p): return ((self._x - p._x) ** 2 + (self._y - p._y) ** 2) ** 0.5
首先引入math库以便后面调用pai以及三角函数对正多边形面积进行求解。
定义RegularPolygon类,定义四个私有变量,并重载__str__函数从而方便对RegularPolygon进行输出测试。接下来实现几个方法,获取周长方法只需直接返回边长与边数的积即可;获取面积方法则利用Python中的math库以及正多边形面积公式直接进行计算即可;获取多边形中心距离方法则直接获取当前中心点坐标以及传入参数的中心点坐标,并利用公式直接计算即可。
(3)运行并测试:
运行如下代码对程序进行测试:
# 进行测试 # 测试缺省构造函数 polygon1 = RegularPolygon() print(polygon1) # 测试含参构造函数 polygon2 = RegularPolygon(4, 2, 1, 1) print(polygon2) #测试几个成员函数 print('Perimeter of polygon1: ' + str(polygon1.getPerimeter())) print('Area of polygon2: ' + str(polygon2.getArea())) print('Centre Distance : ' + str(polygon1.distanceToPolygon(polygon2)))
在如上的代码中,测试了缺省参数构造函数,含参构造函数,以及对应几个成员函数。运行并输出结果如下:
通过手动计算,可以验证,各个方法以及类的实现以及输出结果完全正确。
二、自定义数据结构栈。栈是一种后进先出(Last-In-First-Out)的数据结构。编写类 Stack,实现入栈、出栈、判断栈是否为空,是否满栈、以及改变栈容量等操作。
Stack 类包括:
私有成员 content,为一个列表,代表栈里的数据;
私有成员 size,要求为整型,代表栈的容量;
私有成员 current,要求为整型,代表栈当前数据的个数;
方法 isempty,判断栈是否为空,返回 True/False;
方法 empty,置空栈;
方法 setSize,输入参数为新的栈的容量。注意新的栈容量可能小于原有的栈容量,统一将后进的元素删除;
方法 isFull,判断栈是否为满,返回 True/False;
方法 push,入栈,输入参数为新的元素;
方法 pop,出栈;
方法 show,打印当前栈的数据。
自行设计测试函数验证 Stack 类代码的正确性。
(1)大致思路:
对于栈,首先定义栈Stack类。初始化栈时只需初始化栈的大小即可,其他数据不需进行初始化赋值。各个方法具体实现如下:
①栈判空函数:只需比较当前栈中元素数是否为零即可
②栈清空函数:只需清空栈中用来存元素的列表并将栈当前元素个数变量设置为0
③定义栈改变大小函数:首先对输入进来的新大小进行判断,如果大小值小于等于零,即认为是非法输入,给出提示并返回,对于合法输入,修改栈大小后,如果栈的现有元素比栈大小大,则从栈中列表的头部依次删除元素直至栈中元素个数合法
④定义判栈满函数:只需比较栈中元素个数与栈大小是否相等即可
⑤定义压栈函数:首先进行栈满判断,如果栈满,则给出错误提示并返回。如果栈不满,则在列表中加入待压栈元素并将栈中现有元素数加一。
⑥定义弾栈函数:首先进行栈空判断,如果栈空,则给出错误提示并返回。如果栈不空,则进行依次弾栈直至大小合法
⑦定义栈的展示函数:输出当前栈中现有元素个数以及栈的大小。并输出栈中列表的每个元素。
(2)编程实现:
# 定义Stack类 class Stack: # 初始化构造函数,并声明各私有变量 def __init__(self, size): self._content = [] self._size = size self._current = 0 # 定义栈判空函数 def isEmpty(self): return self._current == 0 # 定义栈清空函数 def empty(self): self._content.clear() self._current = 0 # 定义栈改变大小函数 def setSize(self, newSize): # 对非法数据进行特判 if newSize <= 0: print('Invalid size!') return # 合法数据正常进行处理 self._size = newSize while self._size < self._current: self.pop() # 定义栈判满函数 def isFull(self): return self._size == self._current # 定义压栈函数 def push(self, item): # 判栈满 如果栈满则无法进栈 if self.isFull(): print('Failed! Cannot insert item into a full stack!') return else: self._current += 1 self._content.append(item) # 定义弹栈函数 def pop(self): # 判栈空 如果栈空则无法弹栈 if self.isEmpty(): print('Failed! Cannot pop item from an empty stack!') return else: self._current -= 1 res = self._content[self._current] del self._content[self._current] return res # 定义栈的展示函数 def show(self): print('Stack status:' + str(self._current) + '/' + str(self._size)) print(self._content)
(3)运行并测试:
①测试压栈,判栈满函数以及展示函数:
将栈的大小设置为5,并压入7个数据,栈将自动压入前5个数据并在栈满时给出错误提示。
可以看到前五次都顺利压入数据,后两次由于栈已满,无法压入数据并给出错误提示。
②测试压栈,判栈空函数以及展示函数:
在①的基础上进行7次弾栈,栈将自动弹出5个数据并在栈空时给出错误提示。
③测试栈改变大小函数及栈的展示函数:
首先初始化一大小为10的栈并压满数据后,将栈的大小调整至8,输出栈。
可以看到,栈成功的调整了大小并丢弃了多余的元素。
④测试清空栈函数及栈的展示函数:
首先初始化一大小为10的栈并压满数据后,清空栈,输出栈。
可以看到,栈中元素成功清空。
三、时间类:设计一个名为 Time 的类。该类包含:
表示时间的私有成员 hour、minute 和 second。
构造 Time 对象的构造函数,使用当前时间 time.time()初始化小时、分钟和秒。
hour、minute 和 second 的 get 方法。
方法 setTime(elapseTime),设置经过了 elapseTime(以秒为单位)后的新时间。
(1)大致思路:
直接调用time.time()函数并直接访问格式化后的元组对各个变量进行赋值初始化即可。对于setTime()方法,首先将秒数加到秒上,分钟进位数则为秒数除以60并取整的值,小时数进位为分钟数除以60并取整的值。进位计算完毕后,分别对时分秒进行对24,60,60的取模运算即为最终结果。
(2)编程实现:
import time # 定义Time类 class Time: # 初始化构造函数,并声明各私有变量 def __init__(self): self._hour = time.localtime(time.time())[3] self._minute = time.localtime(time.time())[4] self._second = time.localtime(time.time())[4] # 重载str函数便于输出 def __str__(self): return str(self._hour) + ':' + str(self._minute) + ':' + str(self._second) + '\n' # 定义获取小时函数 def getHour(self): return self._hour # 定义获取分钟函数 def getMinute(self): return self._minute # 定义获取秒函数 def getSecond(self): return self._second # 定义设置秒数函数 def setTime(self, elapseTime): # 首先计算秒数 self._second += elapseTime # 获取分钟与小时数进位数进位 self._minute += (int)(self._second / 60) self._hour += (int)(self._minute / 60) # 格式化分钟数和秒数 self._second %= 60 self._minute %= 60 self._hour %= 24
首先调用time对时间进行获取并直接访问格式化后的元组对各个变量进行赋值初始化即可。
对于setTime()方法,首先将秒数加到秒上,分钟进位数则为秒数除以60并取整的值,小时数进位为分钟数除以60并取整的值。进位计算完毕后,分别对时分秒进行对24,60,60的取模运算即为最终结果。
(3)运行并测试:
运行代码计算10s后,100s后,1000s后,10000s后,100000s后的时间:
①10s后
②100s后
③1000s后
④10000s后
四、继承 1:补充代码 lab7_4.py,使得代码输出如下。注:不允许在类中添加新的方法。
if name ==' main ': zhangsan = Person('Zhang San', 19, 'man') zhangsan.show()
#Name: Zhang San
#Age: 19
#Sex: man
lisi = Teacher('Li Xi',32, 'man', 'Math') lisi.show() #Name: Li Xi
#Age: 32
#Sex: man
#Department: Math
lisi.setAge(40) lisi.setName("Li Si") lisi.show()
#Name: Li Si
#Age: 40
#Sex: man
#Department: Math
(1)大致思路:
对于第一个代码块,即使用构造方法对Person进行构造,并使用Show()方法进行输出。只需在代码中补全构造方法并按照输出补全Show()方法即可。
对于第二个代码块,只需补全继承的构造函数中对Department的初始化并按照输出补全Show()方法即可。
对于第三代码块,即使用set函数对类中的变量值进行了修改,因此只需补全set函数即可
(2)编程实现:
class Person(object): def __init__(self, name='', age=20, sex='man'): self._name = name self._age = age self._sex = sex def setName(self, name): self._name = name def setAge(self, age): self._age = age def setSex(self, sex): self._sex = sex def show(self): print('Name: ' + self._name) print('Age: ' + str(self._age)) print('Sex: ' + self._sex) class Teacher(Person): def __init__(self, name='', age=30, sex='man', department='Computer'): Person.__init__(self, name, age, sex) self.setDepartment(department) def setDepartment(self, department): self.department = department def show(self): Person.show(self) print('Department: ' + self.department) if __name__ == '__main__': zhangsan = Person('Zhang San', 19, 'man') zhangsan.show() lisi = Teacher('Li Xi', 32, 'man', 'Math') lisi.show() lisi.setAge(40) lisi.setName("Li Si") lisi.show()
对于第一个代码块,即使用构造方法对Person进行构造,并使用Show()方法进行输出。只需在代码中补全构造方法并按照输出补全Show()方法即可。
对于第二个代码块,只需补全继承的构造函数中对Department的初始化并按照输出补全Show()方法即可。
对于第三代码块,即使用set函数对类中的变量值进行了修改,因此只需补全set函数即可
(3)运行并测试:
①代码块一:
②代码块二:
③代码块三:
五、继承 2:研究以下代码,思考代码的输出,并解释。
class China: def __init__(self, given, family): self.given = given self.family = family def __str__(self): return self.given + ' ' + self.family + '\n' + self.get_description() def get_description(self): return 'From China' def execute(self): print(self.family) class Guangdong(China): def __init__(self): China.__init__(self, 'Ming', 'Li') class England(China): def __init__(self): China.__init__(self, 'David', 'Beckham') def get_description(self): return 'From England' def test_person(person): print(person) ming = Guangdong() ming.execute() test_person(ming) test_person(England())
(1)测试并输出:
(2)分析输出结果:
通过代码,可以看到Guangdong和England是继承于China的两个类。主函数中第一行通过构造函数创建Guangdong类,创建类时调用了构造函数并为对应参数分别赋值‘Ming’和‘Li’。函数第二行调用了execute()函数,因此输出了Guangdong类中参数family的内容:Li。
代码第三行使用test_person函数并使用ming作为参数。即调用__str__函数,输出他的名字,姓氏并换行后输出他的国籍。
代码第四行同理第三行输出他的名字,姓氏并换行后输出他的国籍。
实验结论:
通过本次实验,我学会了使用面向对象编程的方式进行编程。学会了使用类,初始化构造函数,方法以及继承完成代码的编写。与C++类似,Python中的面向对象编程也通过各个类进行实现。
此外在编程过程中,我也遇到了一些问题。例如第二题中,在栈进行弾栈与压栈时必须进行判空与判满,否则将造成错误。如果栈空时仍然进行弾栈,将造成栈中元素个数为负值并且将造成列表的下标报错。这些都告诉我们在编程过程中需要对一些特殊情况进行特判考虑。在本题中,重设栈大小时也需要对输入数据进行判断,非法数据将造成错误。