面对对象三大特征:继承 多态(鸭子类型)(面试题)
继承:至少两个类: 什么是什么的关系,为了避免几个类之间有相同的代码
组合:什么有什么的关系
父类:基类或超类
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
类的继承:单继承和多继承
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
python 两种类:经典类 新式类
python3 新式类 —— 都默认继承object class Animal(object): == class Animal:
python2 经典类和新式类 并存
class Animal: 经典类 —— 继承顺序 个别使用方法
class Animal(object): 新式类
开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
派生属性: 在自己的init方法里 使用父类的init方法————指名道姓调用方法
例子:
猫类 抓老鼠
狗类 看门
动物 吃 喝 睡
class Cat(): def eat(self): print('eating') def drink(self): print('drinking') def sleep(self): print('sleeping') def catch_mouse(self): print('yeah') class Dog(): def eat(self): print('eating') def drink(self): print('drinking') def sleep(self): print('sleeping') def watch_door(self): print('wangwangwang')
上面都有共同的属性,看起来代码重复较多,故可以建立一个动物类,用继承的手段来创建这两个子类。可以解决代码的重复性。
class Animal: # 超类、基类 def eat(self): print('eating') def drink(self): print('drinking') def sleep(self): print('sleeping') class Cat(Animal): # 派生类、子类 def catch_mouse(self): print('yeah') class Dog(Animal): # 派生类、子类 def watch_door(self): print('wangwangwang')
2、继承的分类
继承一般分为单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
二、派生
人 狗 相同属性的同时 还有一些不同的属性 class Animal: def __init__(self,aggressivity, life_value,name): self.name = name # 每一个角色都有自己的昵称; self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; self.life_value = life_value # 每一个角色都有自己的生命值; def eat(self): self.life_value += 10 class Person(Animal): def __init__(self, name, aggressivity, life_value, money): Animal.__init__(self, name, aggressivity, life_value)
# super().__init__(name, aggressivity, life_value) #新式类 self.money = money #派生属性:父类没有的属性 def attack(self,dog): dog.life_value -= self.aggressivity def get_weapon(self,weapon_obj): if self.money > weapon_obj.price: self.money -= weapon_obj.price # 金老板花钱买武器 self.weapon = weapon_obj # 金老板装备打狗棒 self.aggressivity += weapon_obj.aggr # 金老板的攻击力增加了 class Dog(Animal): def __init__(self, name, breed, aggressivity, life_value): Animal.__init__(self,aggressivity,life_value,name)
# super().__init__(name, aggressivity, life_value) #新式类 self.breed = breed # 每一只狗都有自己的品种; #派生属性:父类没有的属性 def bite(self,people): # 派生方法 :父类没有的方法 people.life_value -= self.aggressivity def eat(self): Animal.eat(self) print('dog is eating')
class Animal: def __init__(self,name,aggressivity,life_value): self.name = name self.aggr = aggressivity self.life_value = life_value def eat(self):
print('我来了') self.life_value += 10 class Person(Animal): def __init__(self,name,aggressivity,life_value,money): super().__init__(name,aggressivity,life_value) # 新式类 self.money = money # 派生属性:父类没有的属性 def attack(self,dog): dog.life_value -= self.aggr class Dog(Animal): def __init__(self,name,aggressivity,life_value,breed): super().__init__(name,aggressivity,life_value) # 新式类 self.breed = breed # 品种,派生属性:父类没有的属性 def bite(self,people): # 派生方法 :父类没有的方法 people.life_value -= self.aggr def eat(self): Animal.eat(self) print('哈哈哈') erha = Dog('小白',10,100,'哈士奇') # 实例化 print(erha.breed) # 查对象的种类 哈士奇 print(erha.name) # 对象的名字 小白 erha.eat() # 哈哈哈 print(erha.life_value) # 110 super(Dog,erha).eat # Animal eat(erha) Animal.eat(erha) # erha 补血 print(erha.life_value) # 120 # 只要想用父类,Animal.eat(snoopy) 父类名.父类的方法(子类对象)
派生: 父类的基础上又产生了子类—派生类
派生属性 : 在自己的init方法里 使用父类的init方法 —— 指名道姓调用方法
派生方法 : 在子类中增加父类没有的
只要子类有,就有子类的
只要想用父类,Animal.eat(snoopy) 父类名.父类的方法(子类对象) 2.7经典类中
用子类的对象,调用父类的方法:
如果子类中没有这个方法,直接就使用父类的
如果子类中有同名方法:
经典类 指名道姓 类名.方法名(子类对象) 类内外一致
新式类 super方法 super(子类名,子类对象).方法名() 类内可以省略super的参数
super 找父类
单继承:常用
多继承:1、钻石继承
经典类(深度优先)和新式类(广度优先),mro新式类:查看继承顺序
2、super
在多继承中,super不只是寻找当前类的父类,而是依据mro顺序,
父类子类调用方法的先后顺序
只要子类有就用子类的,子类没有找父类
子类父类都想用,先找子类,在子类中调父类(指名道姓,super)
class D: def __init__(self): print('d') class C(D): def __init__(self): print('c') super().__init__() class B(D): def __init__(self): print('b') super().__init__() class A(B,C): def __init__(self): print('a') super().__init__() #mro a = A() print(A.mro())
在多继承中,super不只是寻找当前类的父类,而是依据mro顺序,
从A节点出发,根据广度优先排序查找下一个类
三、继承的应用(面试题)
1、对象可以调用自己本类和父类的所有方法和属性, 先调用自己的 自己没有才调父类的。谁(对象)调用方法,方法中的self就指向谁
class Foo: def __init__(self): self.func() def func(self): print('Foo.func') class Son(Foo): def func(self): print('Son.func') s = Son() # Son.func
class A: def get(self): self.say() def say(self): print('AAAAA') class B(A): def say(self): print('BBBBB') b = B() b.get() #输出结果为:BBBBB
四、钻石继承(面试)
经典类和新式类的多继承问题,继承顺序问题
深度优先 经典类 py3中
广度优先 新式类 py2中
class A(object): #新式类,若不继承object则变成经典类 def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类
一、经典类:
1、
2、
二、新式类(具有查看方法),经典类没有
1、
2、
a = A() a.f() print(A.mro()) #新式类:查看继承顺序
在好多个有继承关系的类里面,找一个方法,找的顺序问题
py3 —— 新式类--广度优先
py2 —— 经典类--深度优先
面试 —— 能对应 新式类 是广度优先 经典类是深度优先
class A:pass class B(A):pass A、B是经典类 class A1(object):pass class B1(A1):pass A1、B1是新式类
钻石继承:新式类为广度优先,经典类为深度优先的原则进行继承,其中在新式类中可以用:子类名.__mro__ 查看继承顺序,而经典类中此方法不存在。
三、多态特性
python 天生支持多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
1、多态
指的同一事物有多种形态,如动物有:人、狗、猪形态
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say aoao')
文件有多种形态:文本文件,可执行文件
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file')
2、多态性
多态性是指在不考虑实例类型的情况下使用实例。
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
鸭子类型
:对于某些方法来说,可以无差别对待的几个类型,就是鸭子类型。
python不崇尚相似数据类型之间的继承关系
数据类型之间减少依赖关系,解耦
例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法
例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass