4.8
上节课复习
-
编程思想:一种思想,并不是用class和对象去编程
-
面向过程:核心是过程,过程指的是解决问题的步骤,将程序流程化,简单化。可扩展性差
-
面向对象:核心是对象,对象就是容器,用来盛放数据和功能。将程序进行高度整合。提升程序的解耦和程度,进而增强程序的可扩展性。设计复杂,容易出现过度设计问题
扩展性并不是一个程序好坏的唯一评判标准,面向对象和过程的思想各有用途
-
-
面向对象编程
-
类:对数据和功能的整合,本身也是一个对象。对象具有相同的属性或功能,则可以被归为同一类
区分属性一致是根据属性名,不是属性名
-
对象
-
先定义类,后调用类产生对象,与生活中的思考顺序相反。调用的过程称为实例化
-
-
选课系统
对象
- 学生对象
- 学生的名字,年龄,班级
- 选课功能
- 课程对象
- 课程名,周期
- 学生对象
-
调用类发生的事
- 产生一个空对象
- python自动触发 init,完成初始化
- 产生的对象赋值给对象名
-
init注意
- init必须接收self作为第一个参数
- 必须没有返回值
-
查看对象中的名字
__dict__
得到一个字典,可以操作字典访问或着修改 -
使用对象点,类点修改
-
stu.name = 'deimos' # 若对象中有名字,则修改 stu.xxx = 30 # 若没有,则在对象名称空间新增 stu.school = '111' # 如果在类中有school,对象中没有,则此操作是在对象中新增属性,而不是改了类属性
-
对象使用类里面方法的时候会将自己作为参数传入
-
可以通过类使用类里面的函数和数据,使用函数的时候严格按照函数方法,类里面的方法会自动传参
-
python3中产生的类就是一种数据类型,通过type(对象) 可以看到对象的类名
-
类中定义的变量是类的数据属性,是共享给所有对象用的,指向相同的内存地址
print(id(Student.school)) # 4301108704 print(id(stu1.school)) # 4301108704 print(id(stu2.school)) # 4301108704 print(id(stu3.school)) # 4301108704
-
所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
print(id(Student.choose)) # 4335426280 print(id(stu1.choose)) # 4300433608 print(id(stu2.choose)) # 4300433608 print(id(stu3.choose)) # 4300433608
作业
分析
- 一个课程对应一个班级,一个班级对应一个校区
class School:
# 公共数据:学校名,关联班级
school_name = 'zjnu'
def __init__(self, nickname, addr):
self.addr = addr
self.nickname = nickname
self.classes = [] # 往列表里加值,代表新建班级
def related(self, class_name):
# 关联班级
self.classes.append(class_name)
def show_class(self):
for class_name in self.classes:
print(self.school_name,self.addr,self.nickname,class_name)
school_obj1 = School('数计学院', '20幢')
school_obj2 = School('音乐学院', '18幢')
school_obj1.related('14班')
school_obj2.related('15班')
school_obj1.show_class()
school_obj2.show_class()
class Class:
def __init__(self, name):
self.name = name
self.course = None
def related_course(self, course):
self.course = course
class_obj1 = Class('14期')
class_obj2 = Class('13班')
class_obj1.related_course('python')
class_obj2.related_course('linux')
需求
在建好学校和班级类之后,想要查看学校中班级开设的课程:不操作名字,操作对象,让related方法关联对象
修改:
class School:
def related_class(self,class_obj):
self.classes.append(class_obj)
# 原来传的是班级的名字,现在改传班级对象
def tell_class(self):
print(self.nickname)
for class_obj in self.classes:
class_obj.show_info()
# 得到对象,可以使用对象下的方法
class Class:
def __init__(self, name):
self.name = name
self.course = None
def related_course(self, course):
self.course = course
def show_info(self):
print(self.__dict__)
class Course:
def __init__(self,name,period,price):
self.name = name
self.period = period
self.price = price
def tell_info(self):
print(self.name,self.period,self.price)
course_obj1 = Course('python开发','6mon','2000')
总结
在定义类,在使用关联的数据时,引用对象,而不是引用名字,于是就可以在类中引用关联的对象的类中的方法
只要把对象传过去,就啥都有了
需求:使用pickle,序列化对象存到pkl文件
使文件名唯一,用uuid模块,在init中,每一个对象的初始化的时候给对象一个uuid,作为对象的id
import uuid
uuid.uuid4()
封装
封装是面向对象最核心的一个特性,封装的意思就是整合,可以对类中的属性做进一步处理,将封装的属性进行隐藏操作
封装类中的属性
在类中的属性,数据和函数,前面加 __
,可以隐藏属性
class Foo:
__x = 1
def __f1(self):
print('from f1')
def
Foo.f1
# 报错,被隐藏,不能直接访问到了
print(Foo.__dict__)
# _Foo__f1,_Foo__x
-
这种隐藏属性是一种变形操作,不能直接访问到类里面的属性,但是仍可以通过
_类名__属性名
来访问 -
隐藏对外有效,在类内部可以正常访问
-
用下划线隐藏属性会在类定义检查语法的阶段统一发生变形,在检查语法之后用下划线开头的属性都不会变形
-
在初始化阶段,对init中的属性隐藏
def __init__(self,name,age): self.__name = name self.__age = age
为何要隐藏
定义的目的就是为了使用,隐藏之后外界无法直接用类里面的属性,只让外界通过指定方式使用:提供接口
def __init__(self,name,age):
self.__name = name
self.__age = age
def get_name(self):
print(self.__name)
# get_name就是一个接口,只能通过接口访问到属性,可以在接口内添加其他逻辑,控制使用者对属性的操作
def set_name(self,new_name):
if not isinstance(new_name,str):
print('修改非法')
return
self.__name = new_name
# 提供了一个修改名字的接口,判断