Python之旅的第22天(继承)
面向对象编程的三大特点:继承、多态、封装
今天的主要内容是记录一下关于类的继承的内容。
类的继承其实是很类似与我们生活中的继承,父辈有的会传递给子类,也不算完全相同,下面来看看实际情况如何。
类的继承:单继承、多继承
首先是单继承的问题,如何来实现,以及有哪些功能。
class Father: '这是一个父类的测试类' money = 10000000 def __init__(self,name,age): self.name = name self.age = age @property def zhengqian(self): return '%s需要挣钱'%self.name class Son(Father): #此时实现了对父类Father的继承 pass # 那么在继承之后会有那些实际的作用呢 # 首先我们通过Son类子类实例化一个对象,这时候需要传递参数 # p1 = Son() # 如果你不传递任何参数,会报错:__init__() missing 2 required positional arguments: 'name' and 'age' # 虽然子类中没有参数,但是需要按照父类__init__方法要求传递参数 # 所以也可以侧面证明子类可以调用父类的方法 p1 = Son('Dad',36) print(p1.money) print(p1.zhengqian) # 10000000 # Dad需要挣钱 # 子类可以调用父类的数据属性、功能属性
继承同时具有的两种含义:
1.继承基类方法,并做出自己的改变或者扩展
# 案例:猫和狗都会吃喝拉撒,但是他们叫声又不同 class Animal: def __init__(self,name): self.name = name @property def eat(self): return '%s在吃%s粮'%(self.name,self.name) @property def paly(self): return '%s在玩逗%s草'%(self.name,self.name) class Cat(Animal): @property def jiao(self): return '%s\"喵喵喵~~~\"'%self.name class Dog(Animal): @property def jiao(self): return '%s\"汪汪汪~~~\"'%self.name cat1 = Cat('猫') dog1 = Dog('狗') # 此时以dog和cat两个类分别创建出来的两个实例化对象就可以实现调用父类的,也有自己独有的 print(cat1.eat) print(cat1.jiao) print(dog1.eat) print(dog1.jiao) # 输出内容如下: # 猫在吃猫粮 # 猫"喵喵喵~~~" # 狗在吃狗粮 # 狗"汪汪汪~~~"
2.声明某个子类兼容与某基类,定义一个借口类,子类继承借口类,并且实现接口定义的方法
实践中,继承的第一种含义意义并不大,甚至是有害的,因为它使子类与基类之间出现了耦合。
继承第二种含义非常重要,又叫做接口继承。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定一个兼容接口,是得外部调用者无需关心细节,可一视同仁的处理实现了特定接口的所有对象”这就是归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好像linux的泛文件概念一样,所有东西都可以当做文件来进行处理,不必关心它的内存、硬盘、网络还是屏幕。
import abc class All_file: def read(self): pass def write(self): pass class Disk(All_file): def read(self): print('disk read') def write(self): print('disk write') class Cdrom(All_file): def read(self): print('cdrom read') def write(self): print('cdrom write') class Mem(All_file): def read(self): print('mem read') def write(self): print('mem write') # m1=Mem() m1.read() m1.write()
但是像上面这样进行接口归一化并没有产生什么约束力,调用者好像不写具体细节也可以。
这个时候我们就需要引入我在Python学习中最草率的模块。
import abc
在基类写上(metaclass=abc.ABCMeta): 并在需要约束的子类前面增加@abc.abstractmethod
这样如果调用者不对抽象的方法进行实现就会报错。
import abc class All_file(metaclass=abc.ABCMeta): @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): pass class Disk(All_file): def read(self): print('disk read') def write(self): print('disk write') class Cdrom(All_file): def read(self): print('cdrom read') def write(self): print('cdrom write') class Mem(All_file): def read(self): print('mem read') def write(self): print('mem write') # m1=Mem() m1.read() m1.write()
上面一直说的都是单继承的方式,但是如果是多继承呢,我们就得明确在查找基类的顺序,因为有可能多个基类都有,那到底用谁的就是问题了
继承关系是:
#coding:utf-8 class A: # def test(self): # print('A') pass class B(A): # def test(self): # print('B') pass class C(A): # def test(self): # print('C') pass class D(B): # def test(self): # print('D') pass class E(C): # def test(self): # print('E') pass class F(D,E): # def test(self): # print('F') pass f1=F() f1.test() #经典类:F->D->B->A-->E--> # print(F.__mro__) #F-->D->B-->E--->C--->A新式类
这是关于新式类的查找方法,主要是Python3中,Python2中会区分经典类和object,经典类的顺序是F-->D->B--->A-->E--->C
下面是关于子类如何进行基类方法的调用问题:
首先是我们使用传统方法:
class Vehicle: Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦') print('开动啦') class Subway(Vehicle): def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) self.line=line def show_info(self): print(self.name,self.speed,self.load,self.power,self.line) def run(self): Vehicle.run(self) print('%s %s 线,开动啦' %(self.name,self.line)) line13=Subway('北京地铁','10km/s',1000000000,'电',13) line13.show_info() line13.run()
这样会存在基类修改类名之后相应的子类就没办法正常使用了:
这个时候我们引入了super(),这个的优势有两个,不用因为名字修改而修改名字,而且你连传递self都不用了
class Vehicle1: Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦') print('开动啦') class Subway(Vehicle1): def __init__(self,name,speed,load,power,line): # Vehicle.__init__(self,name,speed,load,power) # super().__init__(name,speed,load,power) #super(__class__,self).__init__(name,speed,load,power) super(Subway,self).__init__(name,speed,load,power) self.line=line def show_info(self): print(self.name,self.speed,self.load,self.power,self.line) def run(self): # Vehicle.run(self) super().run() print('%s %s 线,开动啦' %(self.name,self.line)) line13=Subway('北京地铁','10km/s',1000000000,'电',13) line13.show_info() line13.run() print(line13.__class__)
今天的内容就是这些,现在有一个类的相关训练,需要做如下事情,暂时记在这里,抽空做了吧。
问题应该不大,但是暂时还没有好的思路