第六章 面向对象编程
6.1 类和对象的概念
6.1.1 类和对象
例 6.1 定义一个 Person 类,能说出姓名和职业,比如:“大家好,我叫张三,职业是教师”
class Person:
def say(self, name, occupation):
print("大家好,我叫"+name)
print("职业是"+occupation)
teacher = Person()
doctor = Person()
teacher.say("张三", "教师")
doctor.say("李四", "医生")
知识点:
- 类和对象的概念:对象指具体的事物,类是指事物的属性
- 类和对象的关系:具有共性的若干事物可以抽象成一个类,一个类可以实例化出若干对象
6.1.2 对象属性和方法
例 6.2 定义一个类 Person,该类包含三个对象属性:姓名,性别,年龄和职业;三个对象方法,一个方法的功能是输出姓名和年龄,另外两个方法的功能分别是判断给定职业或年龄是否与已有相同
class Person(object):
def __init__(self, name, age, occupation):
self.name = name
self.age = age
self.occupation = occupation
def say(self):
print("大家好,我叫"+self.name+",今年"+str(self.age) + ",职业是:"+self.occupation)
def judge_occupation(self, work):
if self.occupation == work.strip(" "):
print("职业相同")
else:
print("职业不同")
def judge_age(self, age):
if self.age == age:
print("年龄相同")
else:
print("年龄不同")
Person1 = Person("张三", 20, "教师")
Person2 = Person("李四", 30, "医生")
Person1.say()
Person2.say()
Person1.judge_occupation("律师")
Person2.judge_occupation("医生")
Person2.judge_age(21)
知识点:
-
什么是对象属性
类的定义包括两个方面,数据成员和成员函数,数据成员用于描述对象的共性特征,也叫对象属性
-
什么是对象方法
对象方法也叫成员函数,用来描述对象的行为,用函数方式实现
-
对象属性的定义
对象属性是指定义在构造方法init()里面的属性,对象属性由两部分组成,前面是“self.”,后面是任意合法标识符,其中 self 表示对象本身。
-
对象方法的定义
对象方法定义形式如下
def 方法名(self,[参数 1,参数 2,.....]):
代码块 -
对象方法的调用
对象名.方法名([参数列表])
6.1.3 构造方法和非构造方法
例 6.3 系统登录时,需要验证用户名和密码的合法性,如果户名和密码正确,则输出“登录成功!”,结束程序。否则,输出“用户名或密码错误!”,重新输人,直到输入正确,结束程序。所有用户名和密码信息保存在“账号密码.txt”中,该文件中的每行对应一个人的账号和密码(逗号分隔),假设用户名和密码都是长度在 3 ~ 16 之间的字符串。定义登录验证的 Login 类,并通过实例化对象验证类的正确性。
class Login(object):
def __init__(self):
self.userlist = self.setvalue()
def setvalue(self):
userlist = []
with open('账号密码.txt', "r", encoding="utf-8")as f:
for line in f.readlines():
info = line[:-1].split(",")
userlist.append(info)
return userlist
def Check(self, name, password):
flag = 0
if self.isLegalUser(name, password):
for line in self.userlist:
if line[0] == name and line[1] == password:
print("登录成功")
flag = 1
return flag
if flag == 0:
print("用户名或密码错误")
else:
print("账号或密码长度非法!")
def isLegalUser(self, name, password):
if len(name) >= 3 and len(name) < 16:
if len(password) >= 3 and len(password) <= 16:
return True
return False
if __name__ == "__main__":
login = Login()
while(True):
str_name = input("请输入用户名:")
str_pwd = input("请输入密码:")
if login.Check(str_name, str_pwd):
break
知识点:
- 构造方法
类中的两个特殊方法:一个是构造方法init():,一个是析构方法def()。一般会自动调用
-
构造方法的定义
def init(self,[参数 1,参数 2]):
代码块
第一个参数是对象本身 -
对象方法的调用
- 构造方法可以调用除本身之外任何方法
- 其他对象方法之间可以互相调用
- 其他对象方法不可以调用构造方法
- 调用对象方法形式:
self.方法名([参数 1,参数 2,.....])
参数位置没有 self
-
if name == "main":
程序文件的两种使用方法:- 作为脚本直接执行
- 作为模块导入其他文件执行
if name == "main":后面的代码只在第一种情况下才执行
6.1.4 类的属性和方法
例 6.4 创建一个学生选课的类,对象属性是姓名和成绩,类属性是课程名称和学分。除构造方法外,有 3 个对象方法,其功能分别是显示课程信息,修改课程学分和修改课程名称。
class CourseSelect(object):
credit = 3
cname = "计算机"
def __init__(self, name, score):
self.name = name
self.score = score
# 显示课程信息
def CourseInfo(self):
print("课程名:"+CourseSelect.cname)
print("学分为:"+str(CourseSelect.credit))
# 修改课程学分
def set_credit(self, n):
CourseSelect.credit = n
# 修改课程名称
@classmethod
def set_name(cls, c):
cls.cname = c
if __name__ == "__main__":
stu1 = CourseSelect("张三", 90)
stu2 = CourseSelect("李四", 100)
stu1.CourseInfo()
stu2.CourseInfo()
stu2.set_credit(5)
stu1.CourseInfo()
stu2.CourseInfo()
CourseSelect.set_name("英语")
stu1.CourseInfo()
stu2.CourseInfo()
知识点:
- 类属性的定义
类属性是在所有对象方法(包括构造方法)外定义 - 类属性的访问
类属性对于整个类都是课件的,在类内部和外部都可以调用 - 类方法的定义
类中定义的方法有 3 种:对象方法,类方法和静态方法- 类方法
@classmethod
def 方法名(cls,[可选参数列表])
代码块
第一个参数代表类
- 类方法
- 类方法的调用
类方法不需要实例化,直接调用
类名.类方法(参数列表)
注意:
对象方法只能由实例化的对象调用,类方法两种都可以调用
巩固与拓展:
(1)定义一个学生类,包含 2 个对象属性:姓名和学号;1 个对象方法:output()输出学生的姓名和学号。创建 2 个学生对象,验证类的正确性。
class Student():
def __init__(self, name, number):
self.name = name
self.number = number
def output(self):
print("姓名:"+self.name)
print("学号:"+str(self.number))
Student1 = Student("张三", 1)
Student2 = Student("李四", 2)
Student1.output()
Student2.output()
(2)定义一个圆类,包含 1 个对象属性:半径;2 个对象方法:area()计算圆的面积,circum()计算圆的周长。创建两个不同半径的对象,测试类的正确性。
import math
class circular(object):
def __init__(self, radius):
self.radius = radius
def area(self):
print("圆的面积是:%.2f" % (math.pi*pow(self.radius, 2)))
def circum(self):
print("圆的周长是:%.2f" % (2*math.pi*self.radius))
radius1 = circular(5)
radius1.area()
radius1.circum()
(3)定义 Compute 类,包含 2 个对象属性:两个整数;4 个对象方法:Add()、Sub()、Mult()和 Div() 分别实现加、减、乘、除运算。创建一个对象,验证类的正确性。
class compute(object):
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
def Add(self):
print("a+b=%d" % (self.num1+self.num2))
def Sub(self):
print("a-b=%d" % (self.num1-self.num2))
def Mult(self):
print("a*b=%d" % (self.num1*self.num2))
def Div(self):
print("a/b=%.2f" % (self.num1/self.num2))
compute1 = compute(8, 4)
compute1.Add()
compute1.Sub()
compute1.Mult()
compute1.Div()
(4)设计一个系统管理员类,包含 2 个类属性:Acount 和 password,初始值分别设为“admin'和“2020”。2 个类方法:Change() 修改管理员的账号和密码,Prnt()输出管理员账号和密码。分别使用对象调用和类名直接调用验证类方法的正确性。
class manager(object):
Acount = "admin"
password = "2020"
def __init__(self, Acount, password):
self.Acount = Acount
self.password = password
@classmethod
def Change(cls, a, p):
cls.Acount = a
cls.password = p
def pnf(self):
print("账号:"+manager.Acount + " 密码:"+str(manager.password))
manager1 = manager("admin", "2020")
manager1.pnf()
manager.Change("xiaoqiyan", "2001")
manager1.pnf()
6.2 面向对象编程的三大特性
6.2.1 封装
例 6.5 定义管理员类。包括 1 个类属性:user_list,是用来存储多个普通用户的账户和密码列表。5 个对象方法,其功能分别是:检测给定的管理员账户是否合法、检测给定的管理员密码是否合法、添加用户、删除用户和修改密码。有 1 个类方法,其功能是输出所有用户的账户及其密码信息。要求:将管理员的账号和密码设为私有属性,初始值分别是“admin”和“admin2010”。创建一个管理员对象,验证类的正确性。
class Admin(object):
userlist = []
def __init__(self, name, password):
self.__name = "admin"
self.__password = "admin2010"
self.IsNameOk(name) # 检测用户名是否合法
self.IsPasswordOk(password) # 检测密码是否合法
""" 检测账户是否合法 """
def IsNameOk(self, name):
if name.strip(" ") != self.__name:
raise Exception("用户名错误!")
def IsPasswordOk(self, password):
if password.strip(" ") != self.__password:
raise Exception("密码错误!")
def AddUser(self, user_name, user_pwd):
Admin.userlist.append([user_name, user_pwd])
def DeleteUser(self, user_name):
flag = 1
for line in Admin.userlist:
if line[0] == user_name:
Admin.userlist.remove(user_name)
flag = 0
break
if flag:
print("该用户名不存在!")
def ChangePsd(self, user_name, user_password):
flag = 1
for line in Admin.userlist:
if line[0] == user_name:
Admin.userlist.remove(line)
flag = 0
break
if flag:
print("该用户不存在!")
else:
Admin.userlist.append([user_name, user_password])
@classmethod
def PrintUserlist(cls):
print("用户名\t密码")
for item in cls.userlist:
print(item[0], item[1], sep='\t')
if __name__ == "__main__":
a1 = Admin("admin", "admin2010")
a1.AddUser("xiaoqiyan", "666888")
a1.AddUser("zhangsan", "123456")
a1.ChangePsd("zhangsan", "456")
a1.PrintUserlist()
知识点:
- 什么是封装
比如在例子中将管理员的账号密码以及管理员所能进行的操作都封装在 Admin 类中,其实按照我的理解就是将这一堆类似的功能装到一个模块里 - 封装的优点
- 使类内部和外部的代码相对独立,只提供给用户接口
- 隐藏代码的细节
- 提高了代码的重用性
- 私有成员和公有成员
- 只能在类的内部使用
- 私有成员写法:self.__name
- 如何访问私有成员呢?a1._Admin__name
6.2.2 继承
例 6.6 定义 Person 类,包括 2 个对象属性:姓名和年龄,1 个对象方法,功能是输出姓名和年龄。定义 Student 类,继承 Person 类所有属性和方法,另有 1 个对象属性:学号,1 个对象方法,功能是输出选修课的程名及其成绩
import random
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def say1(self):
print("我叫{},今年{}岁".format(self.name, self.age))
class Student(Person):
def __init__(self, name, age, snum, course):
Person.__init__(self, name, age)
self.snum = snum
self.course = course
def say2(self):
print(self.course+"课的分数为: "+str(random.randint(80, 100)))
stu1 = Person("张三", 18)
stu2 = Student("李四", 20, "202101", "数学")
stu1.say1()
stu2.say1()
stu2.say2()
知识点:
- 什么是继承
继承就是不需要编写与父类相同的代码就可以获取父类的属性和方法,object 处于父子关系顶端class 子类名(父类名):
- 继承的好处
代码重用 - 子类重写构造方法
父类名.init(self[参数 1,参数 2.....])
6.2.3 多态
例 6.7 创建 Person 类,包含 1 个对象属性:姓名,1 个对象方法 say(),功能是输出姓名。创建继承 Person 类的子类 Student,包含 2 个对象属性:年龄和身高,1 个对象方法 say(),功能是输出姓名、年龄和身高。在两个类的外面,定义函数 Introduce(),形参是没有类型的对象,在函数体中,由形参调用 say()方法。
class Person(object):
def __init__(self, name):
self.name = name
def say(self):
print("我叫"+self.name)
class Student(Person):
def __init__(self, name, age, height):
Person.__init__(self, name)
self.age = str(age)
self.height = str(height)
def say(self):
print("我叫{},今年{}岁,身高{}cm.".format(self.name, self.age, self.height))
def introduce(obj):
obj.say()
p1 = Person("张三")
p2 = Student("李四", 20, 180)
introduce(p1)
introduce(p2)
知识点:
- 什么是多态
同一操作作用于不同对象产生不同的效果
巩固与扩展
(1)设计一个三维向量类,包含 3 个对象属性:x、y 和 z,5 个对象方法:Add()和 Sub()分别实现 2 个三维向量的加和减操作;Mul()方法和 Div()方法分别实现三维向量乘上和除以整数的操作;PrstVector()方法输出向量的值。
class Vector3(object):
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
def Add(self, v):
v1 = Vector3()
v1.x = self.x+v.x
v1.y = self.y+v.y
v1.z = self.z+v.z
return v1
def Sub(self, v):
v1 = Vector3()
v1.x = self.x-v.x
v1.y = self.y-v.y
v1.z = self.z-v.z
return v1
def Mul(self, n):
v1 = Vector3()
v1.x = self.x*n
v1.y = self.y*n
v1.z = self.z*n
return v1
def Div(self, n):
v1 = Vector3()
v1.x = self.x/n
v1.y = self.y/n
v1.z = self.z/n
return v1
def show(self):
print((self.x, self.y, self.z))
v1 = Vector3(1, 2, 3)
v2 = Vector3(4, 5, 6)
v3 = v2.Add(v1)
v3.show()
v4 = v2.Sub(v1)
v4.show()
v5 = v2.Mul(2)
v5.show()
v6 = v2.Div(2)
v6.show()
(2)定义 Person 类,包含 3 个对象属性:姓名、身高和体重,2 个对象方法:Compute()方法根据身高计算标准体重,OverWeight()方法用来判断是否超重(超过合理体重的 10%算超重)。定义 2 个 Person 的子类:Men 和 Women,有 1 个对象属性:性别。两个子类都有与父类 Person 重名的方法 Compute(),该方法在父类中的计算公式是:标准体重(kg)=身高(cm)-105,在 Women 类中的计算方法是:[身高(cm)-70]×60%,在 Man 类中的计算方法是:[身高(cm)-80]×70%。在类外部,定义函数 test(),其形参是没有类型的对象,函数体中由形参调用 Compute()方法,使用 Person、Women 和 Man 三种不类型的对象分别调用 Compute 函数,验证类的正确性
class Person(object):
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
def compute(self):
Standard_weight = self.height-105
return Standard_weight
def Overweight(self):
if self.weight > (self.compute()*1.1):
print("{},您超重了!".format(self.name))
else:
print("{},您没有超重!".format(self.name))
class Women(Person):
def __init__(self, name, height, weight):
Person.__init__(self, name, height, weight)
def compute(self):
Standard_weight = (self.height-70)*0.6
return Standard_weight
class Men(Person):
def __init__(self, name, height, weight):
Person.__init__(self, name, height, weight)
def compute(self):
Standard_weight = (self.height-80)*0.7
return Standard_weight
def test(obj):
obj.Overweight()
p1 = Person("zhangsan", 175, 60)
p2 = Men("lisi", 175, 58)
p3 = Women("feng", 152, 40
test(p1)
test(p2)
test(p3)
6.3 应用实例
设计一个简单的学生选课系统,包括 2 个类:学生类和课程类。其中,学生类包括 4 个对象属性:姓名、学号、课程名列表及成绩字典,3 个对象方法:choice()模拟选课、exam()模拟考试和 OutputScore()显示成绩。课程类包括 1 个类属性 cour_info,用于存放所有的课程名,4 个类方法分别完成:打乱课程顺序、添加课程、删除课程和输出所有的课程名称。
import random
class Student(object):
def __init__(self, name, snum):
self.name = name
self.snum = snum
self.course = []
self.score = {}
def choice(self, course):
for item in course:
self.course.append(item)
def exam(self):
for item in self.course:
self.score[item] = random.randint(30, 100)
def OutputScore(self):
print("课程类\t成绩")
for key, value in self.score.items():
print(key, "\t", value)
class course(object):
cour_info = ["英语", "数学", "计算机", "政治", "哲学", "品德"]
@classmethod
def ShuffleCourse(cls):
random.shuffle(cls.cour_info)
@classmethod
def AddCourse(cls, new_course):
cls.cour_info.append(new_course)
@classmethod
def DelCourse(cls, old_course):
cls.cour_info.pop(old_course)
@classmethod
def OutputCourse(cls):
print("课程名: ", end=' ')
for item in cls.cour_info:
print(item, end=' ')
print("\n")
course.ShuffleCourse()
course.OutputCourse()
s1 = Student("张三", "2021")
n = int(input("请输入选课的门数:"))
if n <= len(course.cour_info):
s1.choice(random.sample(course.cour_info, n))
s1.exam()
s1.OutputScore()
else:
print("选课门数超过已有课程!")