1.继承与派生
上文我们已经说过,Python中一切皆对象。我们从对象中抽取了共同特征和技能,得到了类的概念。类与类之间也有共同特征,我们可以从有共同特征和技能的类中提取共同的技能和特征,叫做父类。
比如老师和学生,都有名字,年纪,生日,性别等等,都会走,说话,吃饭。。。我们就可以从老师和学生中总结出来一个‘人’类,称为父类,那老师和学生就是‘人’类的子类,子类继承父类,就有了父类的特征和方法。
继承是一种什么‘是’什么的关系,继承是一种产生新类的方法,当然目的也是为了减少代码重用。
继承的 基本形式是:
<span style="color: #0000ff">class</span><span style="color: #000000"> People: </span><span style="color: #0000ff">pass</span> <span style="color: #0000ff">class</span> Student(People):<span style="color: #008000">#</span><span style="color: #008000">People称为基类或者父类</span> <span style="color: #0000ff">pass</span>
在Python中支持多继承,一个子类可以继承多个父类
我们可以通过__bases__的方法查看继承的所有父类,会返回一个元组。
<span style="color: #0000ff">class</span><span style="color: #000000"> People: </span><span style="color: #0000ff">pass</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Animals: </span><span style="color: #0000ff">pass</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Student(People,Animals): </span><span style="color: #0000ff">pass</span> <span style="color: #0000ff">print</span>(Student.<span style="color: #800080">__bases__</span>)<span style="color: #008000">#</span><span style="color: #008000">(<class '__main__.People'>, <class '__main__.Animals'>)</span> <span style="color: #0000ff">print</span>(People.<span style="color: #800080">__bases__</span>)<span style="color: #008000">#</span><span style="color: #008000">(<class 'object'>,)</span>
可以看到,在People父类中,默认也继承了一个object类,这就是新式类和经典类的区别:
凡是继承了object类的类及其子类,都称为新式类,没有继承object类的类,称为经典类。
在Python 3中,默认就是新式类,而在Python2.X中,默认都是是经典类
继承怎么减少代码呢?看例子
<span style="color: #0000ff">class</span><span style="color: #000000"> People: </span><span style="color: #0000ff">def</span> <span style="color: #800080">__init__</span><span style="color: #000000">(self,name,age): self.name</span>=<span style="color: #000000">name self.age</span>=<span style="color: #000000">age </span><span style="color: #0000ff">def</span><span style="color: #000000"> walk(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">%s is walkig</span><span style="color: #800000">'</span>%<span style="color: #000000">self.name) </span><span style="color: #0000ff">class</span><span style="color: #000000"> Teacher(People): </span><span style="color: #0000ff">def</span> <span style="color: #800080">__init__</span><span style="color: #000000">(self,name,age,level): People.</span><span style="color: #800080">__init__</span><span style="color: #000000">(self,name,age) self.level</span>=<span style="color: #000000">level t1</span>=Teacher(<span style="color: #800000">'</span><span style="color: #800000">zhang</span><span style="color: #800000">'</span>,18,10<span style="color: #000000">) </span><span style="color: #0000ff">print</span>(t1.level) <span style="color: #008000">#</span><span style="color: #008000">10</span> <span style="color: #0000ff">print</span>(t1.name) <span style="color: #008000">#</span><span style="color: #008000">zhang 子类可以用父类定义的属性</span> t1.walk() <span style="color: #008000">#</span><span style="color: #008000">zhang is walking 子类无需定义就可以用父类的方法</span> <span style="color: #0000ff">print</span>(issubclass(Teacher,People)) <span style="color: #008000">#</span><span style="color: #008000">True查看Teacher类是不是People类的子类</span>
从上面的例子中可以看到,Teacher类继承了父类People类,但是Teacher又有自己特有的属性level,子类也可以定义自己独有的方法,甚至可以和父类的方法重名,但是执行时会以子类定义的为准。
这就叫做派生
2.组合
继承是解决什么‘是’什么的问题,那还有一种场景就是什么有什么,比如老师有生日,学生也有生日,生日有年月日这些属性,如果每个类都写的话,又是重复代码。但是又不能让学生和老师继承生日类。这时就用到了组合。组合就是解决什么‘有’什么的问题。看例子
<span style="color: #0000ff">class</span><span style="color: #000000"> Date: </span><span style="color: #0000ff">def</span> <span style="color: #800080">__init__</span><span style="color: #000000">(self,year,mon,day): self.year</span>=<span style="color: #000000">year self.mon</span>=<span style="color: #000000">mon self.day</span>=<span style="color: #000000">day </span><span style="color: #0000ff">def</span><span style="color: #000000"> tell_birth(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">出生于%s年%s月%s日</span><span style="color: #800000">'</span>%<span style="color: #000000">(self.year,self.mon,self.day)) </span><span style="color: #0000ff">class</span><span style="color: #000000"> Teacher: </span><span style="color: #0000ff">def</span> <span style="color: #800080">__init__</span><span style="color: #000000">(self,name,age,year,mon,day): self.name</span>=<span style="color: #000000">name self.age</span>=<span style="color: #000000">age self.birth</span>=<span style="color: #000000">Date(year,mon,day) t</span>=Teacher(<span style="color: #800000">'</span><span style="color: #800000">egon</span><span style="color: #800000">'</span>,19,2010,10,10<span style="color: #000000">) </span><span style="color: #0000ff">print</span>(t.birth) <span style="color: #008000">#</span><span style="color: #008000"><__main__.Date object at 0x0000017E559380F0></span> t.birth.tell_birth() <span style="color: #008000">#</span><span style="color: #008000">出生于2010年10月10日</span>
什么?嫌参数太多?*args学过吧,你高兴就好
<span style="color: #008080"> 1</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Date: </span><span style="color: #008080"> 2</span> <span style="color: #0000ff">def</span> <span style="color: #800080">__init__</span><span style="color: #000000">(self,year,mon,day): </span><span style="color: #008080"> 3</span> self.year=<span style="color: #000000">year </span><span style="color: #008080"> 4</span> self.mon=<span style="color: #000000">mon </span><span style="color: #008080"> 5</span> self.day=<span style="color: #000000">day </span><span style="color: #008080"> 6</span> <span style="color: #0000ff">def</span><span style="color: #000000"> tell_birth(self): </span><span style="color: #008080"> 7</span> <span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">出生于%s年%s月%s日</span><span style="color: #800000">'</span>%<span style="color: #000000">(self.year,self.mon,self.day)) </span><span style="color: #008080"> 8</span> <span style="color: #008080"> 9</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Teacher: </span><span style="color: #008080">10</span> <span style="color: #0000ff">def</span> <span style="color: #800080">__init__</span>(self,name,age,*<span style="color: #000000">args): </span><span style="color: #008080">11</span> self.name=<span style="color: #000000">name </span><span style="color: #008080">12</span> self.age=<span style="color: #000000">age </span><span style="color: #008080">13</span> self.birth=Date(*<span style="color: #000000">args) </span><span style="color: #008080">14</span> t=Teacher(<span style="color: #800000">'</span><span style="color: #800000">egon</span><span style="color: #800000">'</span>,19,2010,10,10<span style="color: #000000">) </span><span style="color: #008080">15</span> <span style="color: #0000ff">print</span>(t.birth) <span style="color: #008000">#</span><span style="color: #008000"><__main__.Date object at 0x0000017E559380F0></span> <span style="color: #008080">16</span> t.birth.tell_birth() <span style="color: #008000">#</span><span style="color: #008000">出生于2010年10月10日</span>
3.抽象类与接口
继承有两种用途:1.代码重用,子类继承父类的方法
2.声明某个子类兼容于某父类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
需要注意的是,Python中并没有接口的关键字,我们只能是模仿接口的功能
比如在 Python中,一切皆文件嘛,那程序是文件,硬件是文件,文本文档也是文件,我们知道什么叫文件呢,就是能读能写,那程序,文本文档这些,都应该有读和写的功能,我们来模拟一下
<span style="color: #0000ff">class</span><span style="color: #000000"> Interface: </span><span style="color: #0000ff">def</span><span style="color: #000000"> read(self): </span><span style="color: #0000ff">pass</span> <span style="color: #0000ff">def</span><span style="color: #000000"> write(self): </span><span style="color: #0000ff">pass</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Txt(Interface): </span><span style="color: #0000ff">def</span><span style="color: #000000"> read(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">文本文档的读取方式</span><span style="color: #800000">'</span><span style="color: #000000">) </span><span style="color: #0000ff">def</span><span style="color: #000000"> write(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">文本文档的写入方式</span><span style="color: #800000">'</span><span style="color: #000000">) </span><span style="color: #0000ff">class</span><span style="color: #000000"> Sata(Interface): </span><span style="color: #0000ff">def</span><span style="color: #000000"> read(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">硬盘文件的读取方式</span><span style="color: #800000">'</span><span style="color: #000000">) </span><span style="color: #0000ff">def</span><span style="color: #000000"> write(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">硬盘文件的写入方式</span><span style="color: #800000">'</span><span style="color: #000000">) </span><span style="color: #0000ff">class</span><span style="color: #000000"> process(Interface): </span><span style="color: #0000ff">def</span><span style="color: #000000"> read(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">进程数据的读取方式</span><span style="color: #800000">'</span><span style="color: #000000">) </span><span style="color: #0000ff">def</span><span style="color: #000000"> write(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">进程数据的写入方式</span><span style="color: #800000">'</span>)
这么做的意义就是:我们不需要知道子类有什么具体的方法,既然他们继承了文件类,那他们就是文件,那他们就有读和写这两个功能
父类限制了子类子类必须有read和write这两个方法,而且名字也必须一样(当然现在只是我们主观上的限制,一会我们说完抽象类,就可以从代码级别上限制了),这样就实现了统一,模拟了接口的概念,这就是归一化设计。在归一化设计中,只要是基于一个接口设计的类,那么所有的这些类实例化出来的对象,在用法上是一样的
我们再来说一下抽象类:
Python中的抽象类需要导入一个模块来实现。抽象类只能被继承,不能被实现
抽象类的写法:
<span style="color: #0000ff">import</span><span style="color: #000000"> abc </span><span style="color: #0000ff">class</span> File(metaclass=<span style="color: #000000">abc.ABCMeta): @abc.abstractmethod </span><span style="color: #0000ff">def</span><span style="color: #000000"> read(self): </span><span style="color: #0000ff">pass</span><span style="color: #000000"> @abc.abstractmethod </span><span style="color: #0000ff">def</span><span style="color: #000000"> write(self): </span><span style="color: #0000ff">pass</span> <span style="color: #008000">#</span><span style="color: #008000">父类使用了抽象类,那子类就必须继承父类的方法,而且名字也必须一样</span><span style="color: #008000"> #</span><span style="color: #008000">这样就实现了代码级别的限制</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Txt(File): </span><span style="color: #0000ff">def</span><span style="color: #000000"> read(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">文本文档的读取方式</span><span style="color: #800000">'</span><span style="color: #000000">) </span><span style="color: #0000ff">def</span><span style="color: #000000"> write(self): </span><span style="color: #0000ff">print</span>(<span style="color: #800000">'</span><span style="color: #800000">文本文档的写入方式</span><span style="color: #800000">'</span>)
4.继承的实现原理
1)继承顺序:
python支持多继承,当一个类继承多个父类时,继承顺序是怎样的呢?这个顺序在新式类和经典类中是不一样的。
在新式类中,继承顺序是广度优先,在经典类中是深度优先,举个栗子:
图不重要,看内容
在这个图中,H是子类,H继承E,F,G,E,F,G,又分别继承B,C,D,B,C,D,同时继承A
在新式类中的顺序是:H E B F C G D A
在经典类中的顺序是:H E B A F C G D
2)继承原理:
当我们定义一个类后,Python就会根据上面的继承规律解析出一个继承顺序的列表(MRO列表),可以通过mro()查看,但是这个方法只有在新式类中才有,经典类没有
Atas ialah kandungan terperinci Python面向对象编程(二). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!