Python课程回顾(day23)
类和对象之组合
1.什么是组合
提起组合我们可以想到的大概就是一组集合体,无论它是什么,肯定不会是单个一个个体,那么组合在我们Python中也是一组集合,不过它表达的是在类中某个类产生的对象,该对象拥有的属性值是来自于另外一个类的对象,如下:
class Date: def __init__(self, x, y): self.x = x self.y = y def sum(self): print(self.x + self.y) class Sum: pass sum1 = Sum() sum1.z = Date(1, 2) sum1下的z属性等于Date这个类, Date这个类需要两个参数 sum1.z.sum() 然后调用z属性也就是Date类的sum功能得出结果
2.为何要用组合
根据我们昨天学过的继承,我们解决了在类中如何解决代码冗余的问题,就是将所有对象都需要使用到的变量或函数放到父类中,这样所有对象再使用功能就可以直接使用super方法进行调用(子类的情况下),或者直接使用类名+__init__函数进行调用类里的功能,当然这种方式不是万能的,那么组合也是一种可以解决代码冗余问题的一种方式,在某种情况使用继承不能达到要求的话这个时候就需要用到组合.
3.如何使用组合
使用继承解决代码冗余问题: class OldboyPeople: school = 'Oldboy' def __init__(self, name, age, gender, year, mon, day): self.name = name self.age = age self.gender = gender self.year = year # 在父类定义好各种子类需要用到的参数,子类再使用就无需再进行定义 self.mon = mon self.day = day def tell_birth(self): # 子类可以调用父类的函数功能 print('%s年%s月%s日' % (self.year, self.mon, self.day)) class OldboyTeacher(OldboyPeople): def __init__(self, name, age, gender, level, salary, year, mon, day): super().__init__(name, age, gender, year, mon, day) self.level = level self.salary = salary def change_score(self): print('%s teacher is change score' % self.name) class Students(OldboyPeople): def __init__(self, name, age, gender, course, year, mon, day): super().__init__(name, age, gender, year, mon, day) self.course = course def choose_course(self): print('%s choose course' % self.name) tea = OldboyTeacher('egon', 18, 'male', 9, 10000, 1990, 7, 22) stu1 = Students('klf', 23, 'male', 'linux', 1995, 7, 22) tea.tell_birth() stu1.tell_birth()
使用组合: class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day # 将打印生日这个功能整体定义成一个类. def tell_birth(self): print('%s年%s月%s日' % (self.year, self.mon, self.day)) class OldboyPeople: school = 'Oldboy' def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender class OldboyTeacher(OldboyPeople): def __init__(self, name, age, gender, level, salary): super().__init__(name, age, gender) self.level = level self.salary = salary def change_score(self): print('%s teacher is change score' % self.name) class Students(OldboyPeople): def __init__(self, name, age, gender, course): super().__init__(name, age, gender) self.course = course def choose_course(self): print('%s choose course' % self.name) tea = OldboyTeacher('egon', 18, 'male', 9, 10000) # 传入参数产生对象 stu1 = Students('klf', 23, 'male', 'linux') # 传入参数产生对象 tea.birth = Date(1990, 1, 1) # 为对象添加新属性,该属性的值 = Date类产生的对象 tea.birth.tell_birth() # 使用该属性的值可以直接调用Date类的其他函数功能 stu1.birth = Date(1995, 7, 22) # 但是该对象依然是Teacher类的对象,所以tea此时可以调用两个类的功能 stu1.birth.tell_birth()
组合练习之为老师添加上课程:
class Course: def __init__(self, course_name, course_price, course_period): self.course_name = course_name self.course_price = course_price self.course_period = course_period # 定义一个课程信息的类 def course_info(self): print('课程信息表: 课程:%s 金额:%s 周期:%s' % ( self.course_name, self.course_price, self.course_period)) class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def tell_birth(self): print('%s年%s月%s日' % (self.year, self.mon, self.day)) class OldboyPeople: school = 'Oldboy' def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender class OldboyTeacher(OldboyPeople): def __init__(self, name, age, gender, level, salary): super().__init__(name, age, gender) self.level = level self.salary = salary self.courses = [] # 定义一个空列表用于存放老师所教授的课程信息 def change_score(self): print('%s teacher is change score' % self.name) def tell_course_info(self): print(('%s老师教授的课程如下' % self.name).center(50, '=')) # 打印格式 for line in self.courses: # 使用for循环打印老师的课程信息列表 line.course_info() # 此时列表里存的是Course的对象(步骤见最下方),所以可以直接调用Course类的course_info函数打印 class Students(OldboyPeople): def __init__(self, name, age, gender, course): super().__init__(name, age, gender) self.courses = [] tea = OldboyTeacher('egon', 18, 'male', 9, 10000) stu1 = Students('klf', 23, 'male', 'linux') python = Course('Python', 17800, '5.5个月') # 为Course类产生对象 Linux = Course('Linux', 16800, '4个月') Go = Course('Go', 15800, '3个月') tea.courses.append(python) # 为tea对象里的courses列表添加Course所产生的对象 tea.courses.append(Linux) tea.courses.append(Go) tea.tell_course_info() # 调用tea对象所属类的tell_course_info打印
类和对象之封装
1.什么是封装
封装的字面意思是将使用某一种事物将另外一种事情装起来,比如我们经常接触的快递,物流等等,都属于封装.在我们Python中封装的意思就是将一堆属性隐藏起来的意思,而封装的基本含义就是对外隐藏,对内开放,即别人找不到,只有自己内部的对象或属性看得到.
2.为何要使用封装
其实我们的快递物流等等有些情况是完全不需要进行封装的,需要进行封装的无非两种情况,首先是保护,避免我们的物品在运输途中损坏,其次是用户的个人隐私,那我们在程序中使用封装就是要使用封装的第二种特性,隐私,可以理解为在我们不希望用户对我们程序内部属性进行增删改的时,就可以使用封装来解决,但封装最重要的特点是对内开放,而不是单纯意义上的隐藏
3.如何使用封装
封装之对外隐藏:
关键字:__ (两个下划线)
在进行封装时,为要封装的属性名之前加上__开头(注意不能再以__结尾),会在定义类的阶段内将属性名进行变形:_类名__属性名
若再想访问到属性则需要按照变形之后的属性名进行访问
class Foo: __x = 111 # 变形为------> _Foo__x def __init__(self,y): # 只针对__开头,不针对__结尾
self.__y = y # 要封装对象内的属性值则要在定义时在self后加__ 变形为---> self._Foo__y
def __f1(self): # 变形为---->def_Foo__f1 print('Foo.f1') obj = Foo(222) # 给obj对象的属性y传值 obj.x # 直接访问x与__x都会报错 obj.__x
obj.__y # 此时直接访问obj对象内的y也会报错
我们可以使用__dict__来查看Foo内已封装的属性是否已经变形:
输出结果为:
{'__module__': '__main__',
'_Foo__x': 111,
'f1': <function Foo.f1 at 0x00000000025FF730>,
'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
封装之对内开放:
1.基于__语法的变形,要告诉大家的是,这种语法意义上的变形只在定义类的阶段变形执行一次,类定义之后,新增的__开头的属性都不会有变形的效果.
class Foo: __x = 1 def __f1(self): pass obj = Foo() Foo.__y = 2 obj.__y = 3 print(Foo.__dict__) 输出结果为: {'__y':2} 不会变形为 _Foo__y print(obj.__dict__) 输出结果为: {'__y':3}
2.如果父类不允许子类覆盖掉父类的方法时,可以在不允许覆盖的属性前加__
class Foo: def __f1(self): # 变形为 _Foo__f1(self): print('Foo.f1') def f2(self): # 未变形可以调用 print('Foo.f2') self.__f1() # 变形为 _Foo__f1,会执行Foo内的f1函数,不会执行当前对象内的f1,所以不会覆盖掉父类的属性 class Bar(Foo): def f1(self): print('Bar.f1') obj = Bar() obj.f2() # 当前类里没有f2去父类找
3.封装的具体应用:
要知道,我们要封装的属性无非两种属性,一种是数据属性,另一种则是函数属性,而不同的属性封装则是为了达到不同的目的
封装数据属性的目的:
class People:
def __init__(self, name, age):
self.__name = name # 将数据属性进行封装
self.__age = age
def tell_info(self): # 提供接口给用户使用
user = input('>>:')
pwd = input('>>:') # 增加控制逻辑
if user == 'klf' and pwd == '123':
print(self.__name, self.__age)
def set_info(self, name, age):
if type(name) is not str: # 提供接口给用户更改用户信息,并增加控制逻辑
print('用户名必须为字符串')
return
elif type(age) is not int:
print('密码必须是整形')
return
self.__name = name # 更改变形后的属性值
self.__age = age
obj = People('kkk', 18)
obj.tell_info()
obj.set_info('KLF', 23)
obj.tell_info()
总结:我们将数据属性封装起来,然后还需要开辟接口以供类外部的用户使用,这样麻烦的做是为了什么呢? 因为我们要在供用户使用我们的程序时,要按照我们程序设定的控制逻辑使用,不能随意的对我们的属性进行随意操作
封装函数属性的目的:
还记得我们之前写过的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()
封装之property 01:
BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解) 成人的BMI数值: 过轻:低于18.5 正常:18.5-23.9 过重:24-27 肥胖:28-32 非常肥胖, 高于32 体质指数(BMI)=体重(kg)÷身高^2(m) EX:70kg÷(1.75×1.75)=22.86 class People: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height @property # 不加property之前我们调用bmi功能时需要加上括号,因为bmi本身就是一个函数不加括号是不会执行的 def bmi(self): # 而property装饰器的作用就是将功能伪装成一个具体的数据以便直接调用 return self.weight / (self.height ** 2) obj = People('yp', 95, 1.73) print(obj.bmi)
obj.bmi = 123
封装之property 02:
class People:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter # 被property装饰过的对象会生成各种方式,此方式会在对函数进行设置类操作时执行
def name(self, new_name):
if type(new_name) is not str:
print('用户名必须为str类型')
return
self.name = new_name
@name.deleter # 此方式会在对函数进行删除类操作时执行
def name(self):
del self.__name
obj = People('yp')
print(obj.name)
obj.name = 'KLF' # 自动执行name.setter下的函数功能
del obj.name # 自动执行name.deleter下的函数功能
多态与多态性
1.什么是多态? 什么是多态性?
多态指的就是同一种事物的多种形态,比如动物的多个形态可以是人,狗,猪,猫等等,植物的多种形态可以是树,草,花等等.在程序中的应用就好比我们的继承
而多态性指的则是在基类首先定义好类的基本属性,且子类必须按照基类的基本属性进行实例化操作,这样子类就可以不用考虑具体类型的前提下而直接使用对象下的方法
2.为什么要使用多态
我们使用基类创建一套统一的规则,强制子类去遵循(使用抽象类实现),这样便可以在不用考虑对象具体类型的前提下而直接使用对象的方法
3.如何使用多态
import abc @abc.abstractmethod 导入abc模块则会强制子类按照基类的规则去实例化每一个功能 class Animals(metaclass=abc.ABCMeta): def eat(self): pass def drink(self): pass def run(self): pass def bark(self): pass class Cat(Animals): def jiao(self): print('喵喵喵') # 没有遵循基类的规则则会报错 class Dog(Animals): def eat(self): print('嘎吱嘎吱') def drink(self): print('舔舔舔') def run(self): print('跑跑跑') def bark(self): print('汪汪汪')
c = Cat
c.jiao() # 报错
d = Dog
d.bark
鸭子类型
在我们python中其实是不推崇上面我们所学到的多态性的,因为现在我们所编写的程序都是尽量的去避免程序的结偶合性的,而多态性则是在一定条件下限制了避免结偶合的这种特性,在python中我们更推崇以一种约定好的方式来进行实例化,比如说在Linux中的文件操作,假设将一切程序的具体操作变为对文件的操作模式的话,那么只需要记住对文件的操作(读写)就可以了,这就是鸭子类型.所以我们在以后的编程中更多的应该去使用这种特性