Day21 面向对象三大特性之继承

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

猜你喜欢

转载自www.cnblogs.com/sxchen/p/11247659.html
今日推荐