今日内容:
OOP的三大特征之一:
封装,继承,多态
继承:
什么是继承?
- 继承是一种关系,是描述两个对象之间,什么是什么的关系
- 在程序中,继承描述的是类和类之间的关系
例如:
- a继承了b,a就能直接使用b已有的方法和属性;
- a称之为子类,b称之为父类或者基类
为什么要使用继承?
- 继承的一方可以直接使用被继承一方的已有的东西
- 其目的是为了重用已经有的代码,提高重用性
如何使用继承?
继承的基本语法:
class 类名称(父类的名称): # 在python中,一个子类可以同时继承多个父类 class Base: desc = '这是一个基类' def show_info(self): print(self.desc) def make_money(self): print('一天赚一个亿...') # 如果不继承Base类 class SubClass: def make_money(self): print('一天赚一百...') obj = SubClass() obj.make_money() # 一天赚一百 # 如果继承了Base类 class SubClass(Base): def make_money(self): print('一天赚一百...') obj = SubClass() obj.make_money() # 一天赚一个亿 print(obj.desc) # 这是一个基类 # 指定父类Base: class Subclass() pass obj = SubClass() # 即使类中什么也没有,也可以使用类中的方法 obj.make_money() print(obj.desc)
抽象:
表现形式:不具体,不清晰,很模糊,看不懂
定义:
- 将多个子类中相同的部分进行抽取,形成一个新的类,这个过程就是抽象的过程
正确的使用继承:
- 1.先抽象,再继承
- 2.继承一个已经现有的类,扩展或是修改原始的功能
继承与抽象:
class Teacher: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def say_hi(self): print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age)) def teaching(self): print('老师教学生,写代码') t1 = Teacher("jack","male",20) t1.say_hi() # 如果不继承Teacher,有冗余代码 class Student: def __init__(self,name,gender,age,number): self.name = name self.age = age self.gender = gender self.numuber = number def say_hi(self): print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age)) stu1 = Student("rose","female",18,"xxx01") stu1.say_hi() # 如果继承Teacher class Student(Teacher): pass stu1 = Student("rose","female",18) stu1.say_hi() # 开始抽象 # 抽取老师和学生中相同的类 class Person: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def say_hi(self): print("name:%s,gender:%s,age:%s" % (self.name,self.gender,self.age)) class Teacher(Person): def teaching(self): print('老师教学生,写代码') t1 = Teacher("jack","male",20) t1.say_hi() class Student(Person): pass stu1 = Student("rose","female",18) stu1.say_hi()
属性查找顺序:
先找对象本身的>>>所在类中>>>找父类>>>父类的父类>>>object
# 正常继承 class A: text = 'hahaha' class B(A): text = "heihei" b = B() print(b.text) # heihei # 继承后,B类中pass class A: text = 'hahaha' class B(A): pass b = B() print(b.text) # hahaha # 继承后,b给本身赋了值 class A: text = 'hahaha' class B(A): text = "heihei" b = B() b.text = "xixi" print(b.text) # xixi
派生:
定义:
- 当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类
- 通常子类都会写一些新的代码,不可能与父类完全一样,即都是派生类
总结:派生类指的就是子类
覆盖:
定义:
- 它也称之为重写overrides
# 当子类出现了与父类名称完全一致的属性或方法 class Person: def say_hi(self): print('hello') class Student(Person): def say_hi(self): print('hello world!') stu = Student() stu.say_hi() # 'hello world' 因为hello被覆盖了
练习:实现一个可以限制元素类型的容器(列表,字典,元祖,集合,字符串)
涉及知识点:我们需要访问父类的append函数来完成真正的存储操作
class MyList(list): def __init__(self, element_type): # 初始化 super().__init__(element_type) self.element_type = element_type def append(self, object): if type(object) == self.element_type: # 这里我们需要访问父类的append函数来完成真正的存储操作 super(MyList,self).append(object) else: print('你的element_type not is %s' % self.element_type) # 创建时指定要存储的元素类型 m = MyList(int) # 当你有需求,是需要在创建对象时,干点什么事儿,那就应该在初始化方法时操作 m.append(l) print(m[0]) m.append('121212')
子类中访问父类的内容:
语法:
# 方式1: super(当前类名称,self).你要调用的父类的属性或方法 # 方式2: super().你要调用的父类的属性或者方法 # 方式3:其实这种方式与继承无关 类名称.你要调用的父类的属性或者方法
注意点:
- 当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需要的参数
实例:
class Parent: text = 'abc' def say_something(self): print('anything') class Sub(Parent): def show_info(self): # 访问方式1 print(super(Sub,self).text) super(Sub,self).say_something() # 最常用的!访问方式2 py3中的新语法(推荐使用) print(super().text) super().say_something() # 访问方式3 直接指定类型调用 print(Parent.text) Parent.say_something(self) sub = Sub() sub.show_info()
初始化方法中必须调用super.py:
class Person: def __init__(self,name,gender,age): self.name = name self.gender = gender self.age = age def say_hi(self): print('name:%s, gender:%s, age:%s' % (self.name, self.gender, self.age))
class Student(Person): def __init__(self,name,gender,age,number): super().__init__(name,gender,age) self.number = number def say_hi(self): super().say_hi() print('number:%s' % self.number)
stu = Student('rose', 'male', 20, '0001') stu.say_hi()
组合:
定义:
- 它也是一种关系,描述两个对象之间,是什么有什么的关系
- 将一个对象作为另一个对象的属性(即什么有什么)
组合的目的:
- 它也是为了重用现有代码
什么时候使用继承:
- 分析两个类的关系,到底是不是----什么是什么的关系
什么时候使用组合:
- 如果两个类之间,没有什么太大的关系,完全不属于同类
补充知识点:
- 组合相比于继承,耦合度更低
例如:学生有手机,游戏中角色拥有的某些装备
class Phone: def __init__(self,price,kind,color): self.price = price self.kind = kind self.color = color def call(self): print('正在呼叫XXX:') def send_msg(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() # 正在呼叫XXX:
了解知识点:继承的原理
菱形继承与mro列表:
首先要了解python支持多继承
# 讨论一些python支持多继承可能会出现的问题 class A: pass class B: pass class B: pass class Test(A,B,C): pass print(Test.mro())
# [ <class '__main__.Test'>,<class '__main__.A'>,
<class '__main__.B'>,<class '__main__.C'>,object ]
补充: 新式类与经典类
- python3中任何类都是直接或间接的继承了Object
- 新式类,任何显式或者隐式地继承自object的类就称之为新式类,python3中全都是新式类
- 经典类,既不是object的子类,仅仅在python2中出现
新式类与经典类在菱形继承中的体现:
class A: j = 1 pass class B(A): j = 2 pass class C(A): j = 3 pass class D(A): j = 4 pass d = D() print(d,j) # 输出4,如果D中j = 2注掉,则输出3;如果C中j = 2注掉,则输出2,依次往上输入
总结:
当出现了菱形继承时:
- 新式类,先深度,当遇到了共同父类时就广度
- 经典类,单纯的深度优先