python基础(二十三):面向对象编程精髓演练(选课系统)、封装

下面是小凰凰的简介,看下吧!
💗人生态度:珍惜时间,渴望学习,热爱音乐,把握命运,享受生活
💗学习技能:网络 -> 云计算运维 -> 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

注意:我们都知道隐藏数据属性,是为了提供接口,更规范的访问,不让用户肆无忌惮的对属性操作,但是提供函数供用户使用,使其对属性进行修改、查看、删除,这样不符合用户的使用习惯,因此,我们需要用上面的方法,使其符合用户的使用习惯,且限制规范了用户对属性的操作

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凤求凰的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值