python-面向对象进阶
Posted Gulibali
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python-面向对象进阶相关的知识,希望对你有一定的参考价值。
python-面向对象进阶
三大特性:继承,多态,封装
1,初识继承
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类。
1 # 父类/基类/超生类 2 # 子类/派生类(继承父类) 3 # _bases_则是查看所有继承的父类
代码示例如下:
1 class ParentClass1: 2 pass 3 class ParentClass2: 4 pass 5 class SubClass1(ParentClass1): 6 pass 7 class SubClass2(ParentClass1,ParentClass2): 8 pass 9 10 print(SubClass1.__bases__) 11 print(SubClass2.__bases__) 12 13 ### 14 (<class \'__main__.ParentClass1\'>,) 15 (<class \'__main__.ParentClass1\'>, <class \'__main__.ParentClass2\'>)
2,继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
3, 继承与重用性
继承与重用性
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
1 class Hero: 2 def __init__(self, nickname, life_value, aggressivity): 3 self.nickname = nickname 4 self.life_value = life_value 5 self.aggressivity = aggressivity 6 def attack(self, enemy): 7 enemy.life_value -= self.aggressivity 8 9 class Gailen(Hero): 10 pass 11 12 class Riven(Hero): 13 pass 14 15 gailen = Gailen(\'草丛伦\', 120, 40) 16 print(gailen.nickname, gailen.life_value, gailen.aggressivity) # 这里能实现,说明了它是继承了Hero的属性
提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大节省了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.
再说属性查找
查找顺序:对象自己 → 对象自己的类 → 父类
1 # 查找顺序:对象自己 → 对象自己的类 → 父类 2 class Foo: 3 def f1(self): 4 print(\'Foo.f1\') 5 6 def f2(self): 7 print(\'Foo.f2\') 8 self.f1() # b.f1() 9 10 class Bar(Foo): 11 def f1(self): 12 print(\'Bar.f1\') 13 14 b = Bar() 15 b.f2() 16 ###Foo.f2 17 ###Bar.f1
4,派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
1 class Hero: 2 def __init__(self, nickname, life_value, aggressivity): 3 self.nickname = nickname 4 self.life_value = life_value 5 self.aggressivity = aggressivity 6 def attack(self, enemy): 7 enemy.life_value -= self.aggressivity 8 9 class Riven(Hero): 10 camp=\'Noxus\' 11 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 12 print(\'from riven\') 13 def fly(self): #在自己这里定义新的 14 print(\'%s is flying\' % self.nickname)
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要其传值。
1 class Riven(Hero): 2 camp=\'Noxus\' 3 def __init__(self,nickname,aggressivity,life_value,skin): 4 Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能 5 self.skin=skin #新属性 6 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类 7 Hero.attack(self,enemy) #调用功能 8 print(\'from riven\') 9 def fly(self): #在自己这里定义新的 10 print(\'%s is flying\' %self.nickname) 11 12 r1=Riven(\'锐雯雯\',57,200,\'比基尼\') 13 r1.fly() 14 print(r1.skin) 15 16 \'\'\' 17 运行结果 18 锐雯雯 is flying 19 比基尼 20 21 \'\'\'
5,继承的实现原理
1,方法解析顺序(MRO)列表
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
1 class A(object): 2 def test(self): 3 print(\'from A\') 4 5 class B(A): 6 def test(self): 7 print(\'from B\') 8 9 class C(A): 10 def test(self): 11 print(\'from C\') 12 13 class D(B): 14 def test(self): 15 print(\'from D\') 16 17 class E(C): 18 def test(self): 19 print(\'from E\') 20 21 class F(D,E): 22 # def test(self): 23 # print(\'from F\') 24 pass 25 f1=F() 26 f1.test() 27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 28 29 """ 30 from D 31 (<class \'__main__.F\'>, <class \'__main__.D\'>, <class \'__main__.B\'>, <class \'__main__.E\'>, <class \'__main__.C\'>, <class \'__main__.A\'>, <class \'object\'>) 32 """
2,区别新式类和经典类
1 # 在python2中-->经典类:没有继承object的类,以及它的子类都称经典类 2 class Foo: 3 pass 4 class Bar(Foo): 5 pass 6 7 # 在python2中-->新式类:继承object的类,以及它的子类都称之为新式类 8 class Foo(object): 9 pass 10 class Bar(Foo): 11 pass 12 13 # 在python3中-->新式类:一个类没有继承object类,默认就继承了object 14 class Foo(): # --> class Foo(object): 15 pass 16 print(Foo.__bases__) 17 18 ### 19 (<class \'object\'>,)
3,深度优先和广度优先方式查找
从左边开始就一直走到底,然后后面再这样一直轮下去
新式类不会走到头,快要到头的时候折返回来往另外一个找,最后一个爹一条路走到底
代码示例
1 class A(object): 2 def test(self): 3 print(\'from A\') 4 5 class B(A): 6 def test(self): 7 print(\'from B\') 8 9 class C(A): 10 def test(self): 11 print(\'from C\') 12 13 class D(B): 14 def test(self): 15 print(\'from D\') 16 17 class E(C): 18 def test(self): 19 print(\'from E\') 20 21 class F(D,E): 22 # def test(self): 23 # print(\'from F\') 24 pass 25 f1=F() 26 f1.test() 27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 28 29 #新式类继承顺序:F->D->B->E->C->A 30 #经典类继承顺序:F->D->B->A->E->C 31 #python3中统一都是新式类 32 #pyhon2中才分新式类与经典类 33 \'\'\' 34 print(F._mro_)执行结果 35 36 1 37 (<class \'__main__.F\'>, <class \'__main__.D\'>, <class \'__main__.B\'>, <class \'__main__.E\'>, <class \'__main__.C\'>, <class \'__main__.A\'>, <class \'object\'>) 38 \'\'\'
4,子类重用父类的方法或属性
在子类派生出的新方法中,往往需要重用父类的方法,我们有两种方式实现
1,法一:指名道姓(不依赖继承)
指名道姓,不依赖继承,即父类名.父类方法()
1 class Hero: 2 def __init__(self, nickname, life_value, aggressivity): 3 self.nickname = nickname 4 self.life_value = life_value 5 self.aggressivity = aggressivity 6 def attack(self, enemy): 7 enemy.life_value -= self.aggressivity 8 9 class Gailen(Hero): 10 camp = \'Demacia\' 11 def attack(self, enemy): 12 Hero.attack(self, enemy) # 指名道姓 13 print(\'from Gailen Class\') 14 15 class Riven(Hero): 16 camp = \'Noxus\' 17 18 gailen = Gailen(\'草丛伦\', 100, 30) 19 riven = Riven(\'锐雯雯\', 80, 50) 20 print(riven.life_value) 21 gailen.attack(riven) 22 print(riven.life_value) 23 24 """ 25 80 26 from Gailen Class 27 50 28 """
1 class Hero: 2 def __init__(self, nickname, life_value, aggressivity): 3 self.nickname = nickname 4 self.life_value = life_value 5 self.aggressivity = aggressivity 6 def attack(self, enemy): 7 enemy.life_value -= self.aggressivity 8 9 class Gailen(Hero): 10 camp = \'Demacia\' 11 def __init__(self, nickname, life_value, aggressivity, weapon): 12 # self.nickname = nickname 13 # self.life_value = life_value 14 # self.aggressivity = aggressivity 15 Hero.__init__(self, nickname, life_value, aggressivity) 16 17 self.weapon = weapon 18 def attack(self, enemy): 19 Hero.attack(self, enemy) # 指名道姓 20 print(\'from Gailen Class\') 21 22 gailen = Gailen(\'草丛伦\', 100, 30, \'大宝剑\') 23 print(gailen.__dict__) 24 25 """ 26 {\'nickname\': \'草丛伦\', \'life_value\': 100, \'aggressivity\': 30, \'weapon\': \'大宝剑\'} 27 """
2,法二:super()(依赖继承)
super(),依赖继承
1 # 方式二 2 class Hero: 3 def __init__(self, nickname, life_value, aggressivity): 4 self.nickname = nickname 5 self.life_value = life_value 6 self.aggressivity = aggressivity 7 def attack(self, enemy): 8 enemy.life_value -= self.aggressivity 9 10 class Gailen(Hero): 11 camp = \'Demacia\' 12 def attack(self, enemy): 13 super(Gailen, self).attack(enemy) # 依赖继承,super(自己的类名,self) 14 print(\'from Gailen Class\') 15 16 class Riven(Hero): 17 camp = \'Noxus\' 18 19 gailen = Gailen(\'草丛伦\', 100, 30) 20 riven = Riven(\'锐雯雯\', 80, 50) 21 22 print(riven.life_value) 23 gailen.attack(riven) 24 print(riven.life_value) 25 26 """ 27 80 28 from Gailen Class 29 50 30 """
在python3中super()中已经默认传入参数,可以不用传参数
1 class Hero: 2 def __init__(self, nickname, life_value, aggressivity): 3 self.nickname = nickname 4 self.life_value = life_value 5 self.aggressivity = aggressivity 6 def attack(self, enemy): 7 enemy.life_value -= self.aggressivity 8 9 class Gailen(Hero): 10 camp = \'Demacia\' 11 def __init__(self, nickname, life_value, aggressivity, weapon): 12 # self.nickname = nickname 13 # self.life_value = life_value 14 # self.aggressivity = aggressivity 15 super().__init__(nickname, life_value, aggressivity) # 在python3中默认可以不用在super中写入参数 16 17 self.weapon = weapon 18 def attack(self, enemy): 19 Hero.attack(self, enemy) # 指名道姓 20 print(\'from Gailen Class\') 21 22 gailen = Gailen(\'草丛伦\', 100, 30, \'大宝剑\') 23 print(gailen.__dict__) 24 25 """ 26 {\'nickname\': \'草丛伦\', \'life_value\': 100, \'aggressivity\': 30, \'weapon\': \'大宝剑\'} 27 """
,super()依赖mro列表查找
这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找。
1 # A没有继承B,但是A内super会基于C.mro()继续往后找 2 class A: 3 def f1(self): 4 print(\'from A\') 5 super().f1() 6 class B: 7 def f1(self): 8 print(\'from B\') 9 class C(A,B): 10 pass 11 12 print(C.mro()) 13 c = C() 14 c.f1() # 打印结果:from B 15 16 """ 17 [<class \'__main__.C\'>, <class \'__main__.A\'>, <class \'__main__.B\'>, <class \'object\'>] 18 from A 19 from B 20 """
6,组合(类的组合)
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
1,组合与重用性
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同。
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,这时用组合比较好。
代码示例
1 class People: 2 def __init__(self, name, age, sex): 3 self.name = name 4 self.age = age 5 self.sex = sex 6 7 class Teacher(People): 8 def __init__(self, name, age, sex, level, salary): 9 super().__init__(name, age, sex) 10 self.level = level 11 self.salary = salary 12 13 class Student(People): 14 def __init__(self, name, age, sex, class_time): 15 super().__init__(name, age, sex) 16 self.class_time = class_time 17 18 class Course: 19 def __init__(self, course_name, course_price, course_period): 20 self.course_name = course_name 21 self.course_price = course_price 22 self.course_period = course_period 23 def tell_info(self): 24 print(\'课程名为<%s> 课程价钱为<%s> 课程周期为<%s>\' % (self.course_name, self.course_price, self.course_period)) 25 26 class Date: 27 def __init__(self, year, mon, day): 28 self.year = year 29 self.mon = mon 30 self.day = day 31 def tell_info(self): 32 print(\'I was born in %s年%s月%s日\' % (self.year, self.mon, self.day)) 33 34 teacher1 = Teacher(\'luoma\', 28, \'male\', 10, 3000) 35 teacher2 = Teacher(\'laoluo\', 38, \'male\', 30, 3000) 36 python = Course(\'python\', 3000, \'3mons\') 37 linux = Course(\'linux\', 2000, \'4mons\') 38 39 teacher1.course = python 40 teacher2.course = python 41 42 43 print(python) 44 print(teacher1.course)以上是关于python-面向对象进阶的主要内容,如果未能解决你的问题,请参考以下文章