1.初识继承
什么是继承
继承是面向对象中的一个概念,是描述两个对象的关系,在程序中,继承描述的是类和类之间的关系
例如:a继承了b,a就可以使用b的属性和方法.a称之为子类,b称之为父类,也叫基类
为什么要使用继承
继承的一方可以使用被继承一方已有的东西,这样可以提高代码的重用性
如何使用继承
继承的基本语法
1 class A: 2 # 父类中的方法和属性 3 a = 1 4 def a1(self): 5 print('a1') 6 7 class B(A): 8 # 子类中的方法和属性 9 pass 10 b = B() # 实例化子类B的对象b 11 print(b.a) # 使用父类A中的属性 12 b.a1() # 使用父类A中的方法
2.抽象与继承
抽象即抽取一系列对象或类中相同的部分,从而形成一个新的类
抽象的两个层次:
1.将一系列具有相同特征和行为的对象抽取成类
2.将一系列具有相同属性和方法的类抽取成父类
继承是基于抽象的结果,通过编程语言实现它,先经历抽象的过程,才能通过继承的方法表达抽象的结果
正确的使用继承:
1.先抽象再继承
2.继承一个已经存在的类,扩展或是修改原始的功能
3.属性的查找顺序
属性的查找顺序:对象---->类---->父类---->父类的父类---->.....直到object
1 # 属性的查找顺序 2 class A: # 父类A定义了一个x 3 x = 1 4 5 class B(A): # 子类B也定义了一个x 6 x = 2 7 8 b = B() 9 print(b.x) # 在对象定义之前查找是查找子类B中 10 b.x = 3 # 对象也定义了一个x 11 print(b.x) # 定义x之后,是在对象的属性中查找
先去对象中找,找不到再去类中找,找不到再去父类中找,一直找到object为止,找不到就报错
4.派生与覆盖
什么是派生类
当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类.通常情况下,我们不可能写一个和父类完全一模一样的子类,这样就失去了子类的意义,我们都会加一些代码,所以子类基本都是派生类.
1 # 派生类 2 class Person: 3 def say_hi(self): # 父类中有一个方法 4 print('hi') 5 6 class Student(Person): 7 def say_hello(self): # 子类中也有一个新的方法 8 print('hello') # 此时这个子类就叫做派生类 9 10 s1 = Student() # 实例化一个类的对象 11 s1.say_hi() # 子类的对象可以调用父类的方法 12 s1.say_hello()
覆盖也称重写(overrides),当子类出现了和父类名称完全一致的属性或者方法时就叫做覆盖
1 # 覆盖 2 class Person: 3 x = 1 # 父类中有一个属性 4 def say_hi(self): # 父类中有一个方法 5 print('hi') 6 7 class Student(Person): 8 x = 2 # 子类中有一个同名的属性 9 def say_hi(self): # 子类中也有一个同名的方法 10 print('hello') 11 12 s1 = Student() 13 print(s1.x) # 打印的是子类中的属性 14 s1.say_hi() # 打印的是子类中的方法
5.子类访问父类的内容
访问的语法
语法: ```python 方式1: super(当前类名称,self).你要调的父类的属性或方法 方式2: super().你要调的父类的属性或方法 方式3: 类名称.你要调的父类的属性或方法(self) #方式3与继承无关 ```
子类访问父类的例子
1 # 子类访问父类的内容 2 class Person: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 def eat(self): 8 print('%s会吃东西'%self.name) 9 10 class Student(Person): 11 def __init__(self,name,age,id): 12 # 第一种,父类名直接调用,这种方法什么地方都可以调用,与继承无关,并且需要写上对象本身 13 Person.__init__(self,name,age) 14 # 第二种,super(类名,对象本身),是python2内置的 15 super(Student,self).__init__(name,age) 16 # 第三种,python3做了优化可以内部不传参,推荐使用 17 super().__init__(name,age) 18 self.id = id 19 20 def eat(self): 21 super().eat() # 第三种方法在访问父类的方法 22 super(Student,self).eat() # 第二种方法在访问父类的方法 23 print('%s吃更好的东西'%self.name) 24 25 s1 = Student('sxc',18,188) 26 s1.eat()
初始化方法必须调用super
当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数
需求:person父类中有name,age两个属性,子类student中我需要增加一个id的属性
1 # 继承中初始化方法必须调用super 2 class Person: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 7 class Student(Person): 8 def __init__(self,name,age,id): # 当子类需要增加属性时,需要再初始化一次,这时会覆盖父类的初始化方法 9 super().__init__(name,age) # 子类初始化方法必须加super,这样就能导入父类的初始化方法 10 self.id = id # 增加了id属性,原属性也能访问 11 12 def say(self): 13 print('%s说我真帅'%self.name) # 如果不初始化方法不调用super,self.name就找不到,会报错 14 15 s1 = Student('sxc',18,1414) 16 s1.say()
练习:实现一个可以限制元素类型的容器(字符串,元组,列表,字典,集合)
1 # 实现一个可以限制元素类型的容器 (字典,列表,元组,集合,字符串) 2 class Newlist(list): 3 def __init__(self,kind): 4 super().__init__() # 调用父类的初始化方法 5 self.kind = kind 6 7 def append(self,obj): 8 if type(obj) == self.kind: 9 super().append(obj) # 调用父类的append方法 10 print('加入成功') 11 else: 12 print('类型错误') 13 14 n = Newlist(int) # 定义数据类型为int 15 n.append(123543) 16 print(n)
6.组合
组合是一种关系,描述两个对象之间有什么关系.将一个对象作为另一个对象的属性
组合一般都是在初始化方法时定义另一个对象
组合的目的:为了重用现有代码,提高代码的重用性
组合的例子
1 # 组合:一个类作为另一个类的属性,两者没有关联 2 # 定义一个英雄类 3 class Hero: 4 def __init__(self,name,ad,hp,weapon): 5 self.name = name 6 self.ad = ad 7 self.hp = hp 8 self.weapon = weapon # 英雄类和武器类没有关联,想要英雄类使用武器类,必须在初始化时就增加一个武器的属性 9 # def attack(self,enemy): 10 # enemy.hp -= self.ad 11 # print('%s攻击了%s,掉了%s血,血量还剩%s'%(self.name,enemy.name,self.ad,enemy.hp)) 12 13 # 定义一个武器类 14 class Weapon: 15 def __init__(self,name,att): 16 self.name = name 17 self.att = att 18 19 def weapon(self,hero,enemy): 20 enemy.hp -= self.att 21 print('%s使用了%s攻击了%s,掉了%s血,血量还剩%s' % (hero.name,self.name,enemy.name, self.att, enemy.hp)) 22 23 w1 = Weapon('无尽之刃',100) # 定义一把武器 24 h1 = Hero('亚索',50,500,w1) # 定义一个攻击者 25 h2 = Hero('提莫',20,400,w1) # 定义一个敌人 26 27 h1.weapon.weapon(h1,h2) # 英雄用武器攻击了另一个英雄
什么时候使用继承:一个类是否包含了另一个类,即什么是什么的关系
什么时候使用组合:两个类的关联不大,即什么有什么的关系
7.多继承的问题:菱形继承(了解)
新式类和经典类
新式类:任何直接或者间接的继承于object的类就称之为新式类.(python3中的类的顶部都是object,所以python3中全是新式类)
经典类:不是object的子类,只有python2中有
新式类的继承顺序,先深度遍历,当遇到共同的父类时就广度遍历
新式类的继承顺序还可以通过调用mro方法来查看
1 # 定义类 2 class A: 3 pass 4 5 class B: 6 pass 7 8 class C(A): 9 pass 10 11 class D(A): 12 pass 13 14 class E(B): 15 pass 16 17 class F(C): 18 pass 19 20 class G(D): 21 pass 22 23 class H(E,F,G): 24 pass 25 26 print(H.mro())
新式类的继承顺序:H---->E---->B---->F---->C---->G---->D---->A
经典类的继承顺序,全部都是深度遍历
经典类的继承顺序:H---->E---->B---->F---->C---->A---->G---->D
21