下面是小凰凰的简介,看下吧!
💗人生态度:珍惜时间,渴望学习,热爱音乐,把握命运,享受生活
💗学习技能:网络 -> 云计算运维 -> python全栈( 当前正在学习中)
💗您的点赞、收藏、关注是对博主创作的最大鼓励,在此谢过!
有相关技能问题可以写在下方评论区,我们一起学习,一起进步。
后期会不断更新python全栈学习笔记,秉着质量博文为原则,写好每一篇博文。
文章目录
一、面向对象编程实战演练
1、学校类(关联班级)
# 一、学校---# 校区创建完毕后,可以为每个校区创建班级
# 定义学校类
class School:
school_name = 'OLDBOY'
def __init__(self, nickname, addr):
self.nickname = nickname
self.addr = addr
self.classes = []
# 为【学校】关联【班级】
def related_class(self, class_obj):
# self.classes.append(班级名字)
# self.classes.append(class_name)
self.classes.append(class_obj)
# 函数功能:打印每个校区的每个班级的课程信息
def tell_class(self): # 改
# 打印的校区的名字
print(self.nickname.center(60,'=')) # 格式化打印效果:==========================校区名称===========================
# 打印班级开设的课程信息
for class_obj in self.classes:
class_obj.tell_course()
# 实例化学校类
school_obj1=School('老男孩魔都校区','上海')
school_obj2=School('老男孩帝都校区','北京')
2、班级类(关联课程)
# 二、班级---# 班级创建完毕后,可以为每个班级创建课程
# 定义班级类
class Class:
def __init__(self, name):
self.name = name
self.course = None
# 为【班级】关联【课程】
def related_course(self, course_obj):
# self.course = course_name
self.course = course_obj
def tell_course(self):
print('%s' % self.name,end=" ") # 打印班级信息
self.course.tell_info() # 打印课程的详细信息
# def related_student(self,student_obj):
# self.student = student_obj
def tell_class(self):
print('%s'%self.name)
self.tell_course()
# 实例化班级
# 1、创建班级
class_obj1 = Class('脱产14期')
class_obj2 = Class('脱产15期')
class_obj3 = Class('脱产29期')
# 4、为学校开设班级(为【学校】关联【班级】)
# 上海校区开了:脱产14期,上海校区开了脱产15期
school_obj1.related_class(class_obj1)
school_obj1.related_class(class_obj2)
# 北京校区开了:脱产29期
school_obj2.related_class(class_obj3)
3、课程类
# 三、课程类
# 定义课程类
class Course:
def __init__(self,name,period,price):
self.name=name
self.period=period
self.price=price
def tell_info(self):
print('<课程名:%s 周期:%s 价钱:%s>' %(self.name,self.period,self.price))
# 实例化课程类
# 1、创建课程
course_obj1=Course('python全栈开发','6mons',20000)
course_obj2=Course('linux运维','5mons',18000)
# 3、为【班级】关联【课程对象】
class_obj1.related_course(course_obj1)
class_obj2.related_course(course_obj2)
class_obj3.related_course(course_obj1)
# 【查询一】
# 查看每个校区的每个班级的课程信息(先是为【学校】关联【班级】,再为【班级】关联【课程】)
# tell_class(tell_course())--- tell_course(tell_info())---tell_info(课程信息)
# school_obj1.tell_class()
# school_obj2.tell_class()
# 输出结果:
# ==========================老男孩魔都校区===========================
# 脱产14期 <课程名:python全栈开发 周期:6mons 价钱:20000>
# 脱产15期 <课程名:linux运维 周期:5mons 价钱:18000>
# ==========================老男孩帝都校区===========================
# 脱产29期 <课程名:python全栈开发 周期:6mons 价钱:20000>
4、学生类(关联学校、班级)
# 四、学生类--# 学生创建完毕后,学生可以选择班级
# 定义学生类
class Student:
def __init__(self,s_school,s_name,s_age,s_number,s_gender):
self.stu_school = s_school
self.stu_name = s_name
self.stu_age = s_age
self.stu_number = s_number
self.stu_gender = s_gender
# def choose_class(self,class_obj): # 学生创建完毕后,学生可以选择班级
# self.classes= class_obj
# 为【学生】关联【学校】
def related_school(self,school_obj):
self.stu_school= school_obj
# 为【学生】关联【班级】
def related_class(self,class_obj):
self.stu_class= class_obj
def tell_stu_message(self):
print(('学生 %s 2020年档案表' %(self.stu_name)).center(60, '='))
print('<学校:%s 姓名:%s 年龄:%s 学号:%s 性别:%s>' %(self.stu_school,self.stu_name,self.stu_age,self.stu_number,self.stu_gender))
# self.stu_class.tell_class()
self.stu_class.tell_course()
# 实例化学生类
# 1、创建学生
stu_obj1 = Student('老男孩魔都校区','CC',18,2020001,'female')
stu_obj2 = Student('老男孩帝都校区','MILI',19,2020007,'male')
stu_obj3 = Student('老男孩魔都校区','MELA',20,2020009,'male')
# 2、为【学生】关联【班级】
stu_obj1.related_class(class_obj1)
stu_obj2.related_class(class_obj2)
stu_obj3.related_class(class_obj3)
# 【查询二】查询每个学生的学校,班级,课程信息
stu_obj1.tell_stu_message()
stu_obj2.tell_stu_message()
stu_obj3.tell_stu_message()
# 输出结果:
# =======================学生 CC 2020年档案表=======================
# <学校:老男孩魔都校区 姓名:CC 年龄:18 学号:2020001 性别:female>
# 脱产14期 <课程名:python全栈开发 周期:6mons 价钱:20000>
# ======================学生 MILI 2020年档案表======================
# <学校:老男孩帝都校区 姓名:MILI 年龄:19 学号:2020007 性别:male>
# 脱产15期 <课程名:linux运维 周期:5mons 价钱:18000>
# ======================学生 MELA 2020年档案表======================
# <学校:老男孩魔都校区 姓名:MELA 年龄:20 学号:2020009 性别:male>
# 脱产29期 <课程名:python全栈开发 周期:6mons 价钱:20000>
二、封装
1、引入
面向对象编程有三大特性:封装、继承、多态,其中最重要的一个特性就是封装。封装指的就是把数据与功能都整合到一起
,听起来是不是很熟悉,没错,我们之前所说的”整合“二字其实就是封装的通俗说法。除此之外,针对封装到对象或者类中的属性,我们还可以严格控制对它们的访问,分两步实现:隐藏与开放接口
2、隐藏属性
Python的Class机制采用双下划线开头的方式将属性隐藏起来(设置成私有的)
,但其实这仅仅只是一种变形操作,类中所有双下滑线开头的属性都会在类定义阶段、检测语法时自动变成“_类名__属性名”的形式:
class Foo:
__N=0 # 变形为_Foo__N
def __init__(self): # 定义函数时,会检测函数语法,所以__开头的属性也会变形
self.__x=10 # 变形为self._Foo__x
def __f1(self): # 变形为_Foo__f1
print('__f1 run')
def f2(self): # 定义函数时,会检测函数语法,所以__开头的属性也会变形
self.__f1() #变形为self._Foo__f1()
# -----------此线以上都是Foo类的定义__开头都会变成_类名__属性名----------------
print(Foo.__N) # 报错AttributeError:类Foo没有属性__N
print(Foo._Foo__N) # 正确
obj = Foo()
obj.__x = 2
print(obj._Foo__x) # 对象obj没有属性_Foo__x,注意只在类定义阶段才变形成_Foo__x,上面obj.__x = 2,这已经是执行阶段了,因此不会变形,你只是新增了一个__x数据属性而已
print(obj.__x) # 正确,返回结果:2
# 注意:
1. 函数属性和数据属性的隐藏方法一样,隐藏后的外部调用方法也一样
2. 隐藏对外不对内,对外部需要'_类名__属性名'这样访问,内部'__属性名'这样即可
3. 第2点这是为什么呢?,因为对内的都是在类定义阶段,里面的'__属性名'都会转变成'_类名__属性名',因此访问无碍,外面都是执行阶段,不会变形因此无法访问到。
3、为什么要隐藏属性呢?
定义属性就是为了使用,所以隐藏并不是目的
(1)为什么隐藏数据属性?
将数据隐藏起来就限制了类外部对数据的直接操作
,然后类内应该提供相应的接口来允许类外部间接地操作数据
,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
>>> class Teacher:
... def __init__(self,name,age): #将名字和年纪都隐藏起来
... self.__name=name
... self.__age=age
... def tell_info(self): #对外提供访问老师信息的接口
... print('姓名:%s,年龄:%s' %(self.__name,self.__age))
... def set_info(self,name,age): #对外提供设置老师信息的接口,并附加类型检查的逻辑
... if not isinstance(name,str):
... raise TypeError('姓名必须是字符串类型')
... if not isinstance(age,int):
... raise TypeError('年龄必须是整型')
... self.__name=name
... self.__age=age
...
>>>
>>> t=Teacher('lili',18)
>>> t.set_info(‘LiLi','19') # 年龄不为整型,抛出异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in set_info
TypeError: 年龄必须是整型
>>> t.set_info('LiLi',19) # 名字为字符串类型,年龄为整形,可以正常设置
>>> t.tell_info() # 查看老师的信息
姓名:LiLi,年龄:19
(2)为什么隐藏函数属性?
目的的是为了隔离复杂度
,例如ATM程序的取款功能,该功能有很多其他功能组成,比如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来
>>> class ATM:
... def __card(self): #插卡
... print('插卡')
... def __auth(self): #身份认证
... print('用户认证')
... def __input(self): #输入金额
... print('输入取款金额')
... def __print_bill(self): #打印小票
... print('打印账单')
... def __take_money(self): #取钱
... print('取款')
... def withdraw(self): #取款功能,其他小方法隐藏即可,把取款提供给用户即可,更加简洁,明了
... self.__card()
... self.__auth()
... self.__input()
... self.__print_bill()
... self.__take_money()
...
>>> obj=ATM()
>>> obj.withdraw()
# 注意隐藏了的函数属性,obj,就不会再提示出来,只会提示withdraw这个函数,因此操作简单明了,如果提示一大堆,看着头都晕
总结隐藏属性与开放修改、访问数据属性的接口,本质就是为了明确地区分内外,隐藏数据属性,是为了保护数据,隐藏函数是为了屏蔽复杂的细节。类内部可以修改封装内的东西而不影响外部调用者的代码
;而类外部只需拿到一个接口,只要接口名、参数不变,则无论设计者如何改变内部实现代码,使用者均无需改变代码。这就提供一个良好的合作基础,只要接口这个基础约定不变,则代码的修改不足为虑。
4、property
身高或体重是不断变化的,因而每次想查看BMI值都需要通过计算才能得到,但很明显BMI听起来更像是一个特征而非功能,为此Python专门提供了一个装饰器property,可以将类中的函数“伪装成”对象的数据属性
,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次访问的结果,例如
>>> class People:
... def __init__(self,name,weight,height):
... self.name=name
... self.weight=weight
... self.height=height
... @property
... def bmi(self):
... return self.weight / (self.height**2)
...
>>> obj=People('lili',75,1.85)
>>> obj.bmi #触发方法bmi的执行,将obj自动传给self,执行后返回值作为本次引用的结果
21.913805697589478
使用property有效地保证了属性访问的一致性。另外property还提供设置和删除属性的功能,如下
>>> class Foo:
... def __init__(self,val):
... self.__NAME=val #将属性隐藏起来
... @property
... def name(self):
... return self.__NAME
... @name.setter
... def name(self,value):
... if not isinstance(value,str): #在设定值之前进行类型检查
... raise TypeError('%s must be str' %value)
... self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
... @name.deleter
... def name(self):
... raise PermissionError('Can not delete')
...
>>> f=Foo('lili')
>>> f.name
lili
>>> f.name='LiLi' #触发name.setter装饰器对应的函数name(f,’Egon')
>>> f.name=123 #触发name.setter对应的的函数name(f,123),抛出异常TypeError
>>> del f.name #触发name.deleter对应的函数name(f),抛出异常PermissionError
注意:我们都知道隐藏数据属性,是为了提供接口,更规范的访问,不让用户肆无忌惮的对属性操作
,但是提供函数供用户使用
,使其对属性进行修改、查看、删除,这样不符合用户的使用习惯
,因此,我们需要用上面的方法,使其符合用户的使用习惯,且限制规范了用户对属性的操作