目 录
2w + h、抽象、派生、覆盖、子类访问父类
不同类之间的访问
多继承之菱形继承、新式类和经典类、属性访问顺序
前言:我们首先基于一个目的来讲继承 -- 就是代码的复用性,减少代码的冗余。
基于这个目的我们引出两种方式—— 继承和组合。
一、继承
什么是继承?
继承是一种关系,可以理解为是‘什么是什么的关系’,在程序中就是类与类之间的关系。例如 a 类继承了b类,那么a类就可以调用b类的方法和属性。
为什么要用继承?
因为继承的一方可以直接使用被调用一方的属性和方法,其目的是重用已经有的代码,提高代码的复用性。
怎么用继承?
继承的语法:class 类名称(父类的名称):
class 类名称(父类的名称): 类的内容 #在python中 一个子类可以同时继承多个父类
二、抽象
什么是抽象?
抽象就是将多个类之间相同的地方或有共同特征的属性抽取出来,组成一个新的共同的父类,这个过程就叫做抽象。
抽象的作用:
就是帮助我们更好的使用继承。
如何正确的使用继承呢?
1、先抽象在继承
2、继承一个现存的类,修改或扩展该类的功能
属性的查找顺序?
class A: text = "haha" class B(A): text = "heihei" pass b = B() b.text = "xixi" print(b.text)
对象自己的 - > 所在类中 -> 找父类 - >父类的父类 ->Object
其中Object类中最基本的类,python3 中的类都是由Object 派生出来的,这些类也称为是‘新生类’,后面会介绍!
三、派生
什么是派生?
就是原来的子类发展出于父类不同的功能就叫做‘派生’,这样的类也叫做‘派生类’。
因为对于一个子类来说会生成自己独有的功能,会与父类的代码有区别,所以这类子类统称为‘派生类’,
派生类的实质就是子类,可理解成不同的叫法。
四、覆盖
什么是覆盖?
覆盖也叫做‘重写’,overwrite。
当子类中出现与父类相同的方法或属性时,父类中的这些方法和属性会被覆盖赋值,这种现象叫做‘覆盖’。
覆盖的实质是因为属性的查找顺序决定的:先从子类自身找 ——》父类中找 ——》Object中找,
当父类中的属性和子类名字一样时,子类的属性或方法名字会覆盖父类的,就像是变量的重新赋值一样。
''' 派生与覆盖 ''' class Person: def say_hi(self): print('hello') class Student(Person): def say_hi(self): print('hello word!') # 覆盖:子类有和父类相同的方法名字 say_hi s1 = Student() s1.say_hi() # hello word!
五、子类中访问父类的内容 *****(重点内容)
语法如下:
方式1: super(当前类名称,self).你要调的父类的属性或方法 方式2: super().你要调的父类的属性或方法 方式3: 类名称.你要调的父类的属性或方法(self) #方式3与继承无关
代码示例:
class Person: text = 'from CQUPT' def say_hi(self): print('hello') class Man(Person): def show_info(self): # 方式一:子类访问父类 方法: super(子类名,self对象).父类中的方法 print(super(Man,self).text) super(Man,self).say_hi() m1 = Man() # 对象的创建 m1.show_info() # 调用方法
class Man: res = '老九门串串' def say_some(self): print('欢迎光临!') class Student(Man): def say_no(self): print('请记住下面两句话') # 方式2:子类访问父类 方法: super().父类的属性或方法 print(super().res) super().say_some() stu1 = Student() # 创建对象 stu1.say_no() # 对象调用自身方法
class Women: res = '一直bb' def say_some(self): print('真烦人') class Woman(Women): def say_no(self): # 方式3:子类访问父类 直接用父类调用父类自己的属性和方法 父类名称.方法()或父类名称.属性 print(Women.res) Women.say_some(self) # 注意:父类调用子类的方法要加 self对象,否则报错 print('唉算了') w1 = Woman() #创建对象 w1.say_no()
强调在强调:****** (重点)
当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数
练习:
实现一个可以限制元素类型的容器 (字典,列表,元组,集合,字符串)
""" 需求 实现一个能够限制元素类型的列表类 """ class MyList(list): def __init__(self,element_type): super().__init__() # 调用父类的初始化方法 来完成基本的初始化 self.element_type = element_type def append(self, object): """ :param object: 是要存储的元素 :return: 没有 """ if type(object) == self.element_type: #我们需要在这里访问父类的append函数来完成真正的存储操作 super(MyList,self).append(object) else: print("sorry sir, you element type not is %s" % self.element_type) # 创建是指定要存储的元素类型 m = MyList(int) # 当你有需求,是需要在创建对象时 干点什么事儿 那就该想到初始化方法 m.append(1) print(m[0]) m.append("121212")
此处知识点也可以引用为一个问题:
为什么要在初始化方法中调用父类的初始化方法?
六、组合
组合的引出?
上面提出子类访问父类中内容的方法,那么不是父子关系或者说两个类之间没有什么关联,这两个类要怎么访问(调用)对方的功能呢?
因此提出‘组合’的方法。
什么是组合?
组合也是一种关系,它描述的是两个对象之间‘什么有什么的关系’,
例如:学生有手机—用来打电话,‘学生’、‘手机’是两个不同的对象,‘学生’ 使用‘手机’打电话的功能!
因此我们可以总结说:组合,在程序中就是将一个对象作为另一个对象的属性!
代码示例:
class Phone: def __init__(self,price,kind,color): self.price = price self.kind = kind self.color = color def call(self): print("正在呼叫XXXX;") def send_message(self): print("正在发送短信....") class Student: def __init__(self,name,gender,phone): self.name = name self.gender = gender self.phone = phone def show_info(self): print("name:%s gender:%s" % (self.name,self.gender)) phone = Phone(1000,"apple","red") stu1 = Student("rose","male",phone) stu1.phone.call()
组合的作用?
重用已有的代码,提高代码的复用性
如何区分何时使用继承,什么时候使用组合?
当两个类之间有相同和相似的特征时,或有‘什么是什么的关系’时,考虑用继承!
当两个类之间没有什么关联,只是普通的想要使用对方的功能时,考虑用组合!
另外:组合与继承相比耦合度更低!
七、多继承
在Python中对象明确是可以继承多个类的,即支持多继承!
菱形继承:
引入两个概念:新式类和经典类
Python3 中的类都是直接或间接的继承了Objest类
新式类:显示或隐式的继承自Object类就叫做新式类,Python3 中的类都是新式类
经典类:不是Object 的子类,就是经典类。经典类只在Python2 中出现
菱形继承的查找顺序?
当出现菱形继承时,新式类 先深度,遇到有共同父类时在广度查找;
经典类就是深度优先,深度查找完在广度查找!