继承是所有开发语言的必修内容,而本文写的只是Python继承中的特殊之处,关于继承概念及内容可以自行百度(不装B,感觉百度挺好的)
1.构造函数:
要说继承,先要说一下构造函数。Java要求是与类名相同并且无返回值,而Python则是强制要求命名为“__init__()”。
当创建类的对象时,会自动先调用构造函数,一般用于初始化。构造函数可以不写,那么程序会隐式自动增加一个空的构造函数。
2.继承写法:
(1).class 空格 类名称 括号内填写父类名 冒号
具体写法如下
class A: def __init__(self): pass def print_class_name(self): print "this is class A" class B(A): def __init__(self): pass if __name__ == "__main__": class_b = B() class_b.print_class_name()
上面代码首先定义了一个名为“A”的类,包含一个名为“print_class_name”的方法。然后,定义一个名为“B”的类,继承“A”,同时继承了“A”类的“print_class_name”的方法。
此时“A”类为“B”类的父类或者叫基类,“B”类是“A”类的子类,子类会继承父类的所有公共方法。
(2).意义:
一字记之曰“懒!”(感叹号不算字)我始终相信赖人才能推动科学进步。
言归正传,假如你要写一个老王类,包含年龄、性别等方法,后面还要写一个老王的儿子小王类,也有年龄、性别等方法
class FatherWang: def __init__(self, age=43, sex='man'): self.a = age self.s = sex def age(self): print self.a def sex(self): print self.s class SonWang: def __init__(self, age=13, sex='man'): self.a = age self.s = sex def age(self): print self.a def sex(self): print self.s if __name__ == "__main__": father = FatherWang(43, "man") father.age() father.sex() son = SonWang(13, "man") son.age() son.sex()
你会发现两个类中有相同名称和功能的方法,这样写岂不是很重复很累?(尽管按键盘次数不算太多,我依然觉得很累)如果有继承就很好解决了。
class FatherWang: def __init__(self, age=43, sex='man'): self.a = age self.s = sex def age(self): print self.a def sex(self): print self.s class SonWang(FatherWang): def __init__(self, age=13, sex='man'): FatherWang.__init(age, sex) if __name__ == "__main__": father = FatherWang(43, "man") father.age() father.sex() son = SonWang(13, "man") son.age() son.sex()
两者运行结果完全一样,但是使用继承方法却省了很多按键盘的次数。
3.经典类与新式类:
(1)经典类写法:
class A: pass
(2)新式类写法:
class A(object): pass
可以看出,新式类和经典类的区别在于,是否继承object这个基类。object是所有类的父类。所以之前不带“(object)”的写法,属于经典类写法,加上“(object)”就是新式类的写法。
(3).原因:这里我得吐槽一下Python的版本混乱。2.2版本之前只有经典类写法,这里有一个问题,代码如下
class A: passclass B(object): passa = A()b = B()print a.__class__print type(a)print "----------"print b.__class__print type(b)
结果为:
__main__.A
<type 'instance'>
----------
<class '__main__.B'>
<class '__main__.B'>
首先A类为经典类,B类为新式类。__class__属性和type()方法都是返回对象类型,那么问题来了,使用经典类的写法返回结果却不一致。因此在2.2版本之后出现了新式类来解决这个问题,自然,新式类和经典类还有更大的区别在后面说。另外在3.3版本中,无论使用哪种写法,python都会隐式的继承object,所以3.3版本不会再有经典类(在这里我只想问,早干什么去了!),但是鉴于3.3兼容性问题,貌似没有太多人用。
4.方法重写与方法重载
(1).方法重写:
class FatherWang: def __init__(self, age=43, sex='man'): self.a = age self.s = sex def age(self): print self.a def sex(self): print self.s def name(self): print "Wang_yang" class SonWang(FatherWang): def __init__(self, age=13, sex='man'): FatherWang.__init(age, sex) def name(self): print "Wang_xiaoming" if __name__ == "__main__": father = FatherWang(43, "man") father.age() father.sex() father.name() son = SonWang(13, "man") son.age() son.sex() son.name()
比继承写法(2)中的代码相比,两个类分别多了同名的方法“name”,之前说过子类会继承父类的方法,那么这时候两个类有相同名字的方法,冲突了,怎么处理?
这个时候,就叫方法重写。可以理解为,子类的“name”方法把父类的“name”方法覆盖了,重新写了,所以调用子类的“name”方法时,会以子类的为准(尽管这种理解并不准确,但是可以很好解释“方法重写”这个名词,后面会讲到正确理解)。
注意下面的代码
class FatherWang: def __init__(self, age=43, sex='man'): self.a = age self.s = sex def age(self): print self.a def sex(self): print self.s def name(self): print "Wang_yang" class SonWang(FatherWang): def __init__(self, age=13, sex='man'): FatherWang.__init__(self, age, sex) def name(self, last_name='xiaoming'): print "Wang_"+last_name if __name__ == "__main__": son = SonWang(13, "man") son.name('a') son.name()
结果会是什么样子呢?答案是会打印Wang_a和Wang_xiaoming。
如果学习过静态编程语言的会感动困惑,子类和父类的name方法参数不同啊,应该是重载啊?在python,子类会重写父类同名的方法,忽略参数。
那么方法重写的定义为:子类与父类拥有方法名相同时,子类会重写父类方法。
(2).方法重载:
另外说个题外话,如果同一类中,有两个方法名相同参数不同的方法,会怎样呢,代码如下:
class SonWang: def __init__(self, age=13, sex='man'): pass def name(self, last_name='xiaoming'): print "Wang_"+last_name def name(self): print "name" if __name__ == "__main__": son = SonWang(13, "man") son.name() son.name('a')
结果会如何呢?结果会报错。。。。。而且无论怎么调换方法位置、参数什么的都会报错。究其原因,是因为两个同名的方法,python只会认一个,认最后一个。
所以把无参的name方法放在后面,会报这个方法只需要1个参数而你给了2个参数的错;如果把name带参数的方法放在后面则恰恰相反,会报这个方法需要2个参数而你给了1个参数的错。
所以说,python没有方法重载。
是不是很变态!既然不支持方法重载,居然还能编译通过,我也是奇了葩了。另外很多人说通过方法参数默认赋值就是python的方法重载,而我却不这么认为。方法重载应该是将同一个名字方法扩展多种功能,而不是通过默认值达到减少方法数量。
5.多继承
python支持多继承,尽管我觉得多继承并不好,因为会乱。说到多继承之前,先回顾一下新式类和经典类,还有方法重写。前面埋得坑在这里讲。
(1).经典类的多继承
class FatherWang: def __init__(self, age=43, sex="man"): self.a = age self.s = sex print "I am FatherWang" def age(self): print "Father age:"+str(self.a) def sex(self): print "Father sex:"+str(self.s)class MotherLi: def __init__(self, age=40, sex="woman"): self.a = age self.s = sex print "I am MotherLi" def age(self): print "Mother age:"+str(self.a) def sex(self): print "Mother sex"+str(self.s)class SonWang(FatherWang, MotherLi): def __init__(self, age=13, sex="man"): FatherWang.__init__(self, age, sex) MotherLi.__init__(self, age, sex) print "I am SonWang"if __name__ == "__main__": son = SonWang() son.age() son.sex()
执行结果:
I am FatherWang
I am MotherLiI am SonWangFather age:13Father sex:man在之前代码上稍作修改,另外增加了一个MotherLi的类,SonWang类继承了FatherWang类和MotherLi类。注意,这是经典类的写法。
首先,我们知道了python多继承的写法,就是在括号中上一个父类后面加个逗号,然后再写上下一个父类的名字:
class SonWang(FatherWang, MotherLi):
其次,FatherWang类和MotherLi类,都有名为age和sex方法,SonWang类为什么会继承FatherWang类的方法呢?那么把SonWang类的继承顺序改一下
class SonWang(MotherLi, FatherWang):
就会发现继承的是MotherLi类的方法。
通过结果可知,是按照继承的顺序。
让我们把代码结构变得更发杂一些吧,我想会崩溃的,哈哈哈
class Grandfather: def __init__(self, age=73, sex="man"): self.a = age self.s = sex print "I am Grandfather" def age(self): print "Grandfather age:"+str(self.a) def sex(self): print "Grandfather sex:"+str(self.s) def Interesting(self): print "Grandfather Interesting" class Grandmother: def __init__(self, age=70, sex="woman"): self.a = age self.s = sex print "I am Grandmother" def age(self): print "Grandmother age:"+str(self.a) def sex(self): print "Grandmother sex:"+str(self.s) def Interesting(self): print "Grandmother Interesting"class FatherWang(Grandfather, Grandmother): def __init__(self, age=43, sex="man"): self.a = age self.s = sex Grandfather.__init__(self, age, sex) Grandmother.__init__(self, age, sex) print "I am FatherWang" def age(self): print "Father age:"+str(self.a) def sex(self): print "Father sex:"+str(self.s)class MotherLi(Grandfather, Grandmother): def __init__(self, age=40, sex="woman"): self.a = age self.s = sex Grandfather.__init__(self, age, sex) Grandmother.__init__(self, age, sex) print "I am MotherLi" def age(self): print "Mother age:"+str(self.a) def sex(self): print "Mother sex"+str(self.s) def Interesting(self): print "MotherLi Interesting"class SonWang(FatherWang, MotherLi): def __init__(self, age=13, sex="man"): FatherWang.__init__(self, age, sex) MotherLi.__init__(self, age, sex) print "I am SonWang" if __name__ == "__main__": son = SonWang() son.age() son.sex() son.Interesting()
执行结果:
I am Grandfather
I am GrandmotherI am FatherWangI am GrandfatherI am GrandmotherI am MotherLiI am SonWangFather age:13Father sex:manGrandfather Interesting话说,我自己都有点儿晕。简单来讲,就是儿子继承了老爸、老妈,然后老爸继承了爷爷、奶奶,妈妈继承了老爷、姥姥。(真是一大家子啊)
通过执行结果可知,儿子类先找到老爸类,然后再找老爸类的第1个父类爷爷类,此时发现爷爷类没有父类了,那么执行初始化。然后还要继续找到老爸类的第2个父类奶奶类,此时发现奶奶类没有父类了,执行初始化。此时老爸类的所有父类都初始化完成,初始化自己。然后开始找妈妈类……
那么为什么Interesting方法会使用爷爷类的呢?奶奶类、老爷类、姥姥类都有啊?首先儿子类没有Interesting方法,会先找第1个父类老爸类。发现老爸类也没有,再找老爸类的第1个父类,发现找到了,那么就直接调用不再往下找了。
结论:经典类的多继承,按照继承顺序查找。即,从左到右,从下到上的方式。注意,只有经典类是这样的!
(2).新式类的多继承:
class Grandfather(object): def __init__(self, age=73, sex="man"): self.a = age self.s = sex print "I am Grandfather" def age(self): print "Grandfather age:"+str(self.a) def sex(self): print "Grandfather sex:"+str(self.s) def Interesting(self): print "Grandfather Interesting" class Grandmother(object): def __init__(self, age=70, sex="woman"): self.a = age self.s = sex print "I am Grandmother" def age(self): print "Grandmother age:"+str(self.a) def sex(self): print "Grandmother sex:"+str(self.s) def Interesting(self): print "Grandmother Interesting"class FatherWang(Grandfather, Grandmother): def __init__(self, age=43, sex="man"): self.a = age self.s = sex Grandfather.__init__(self, age, sex) Grandmother.__init__(self, age, sex) print "I am FatherWang" def age(self): print "Father age:"+str(self.a) def sex(self): print "Father sex:"+str(self.s)class MotherLi(Grandfather, Grandmother): def __init__(self, age=40, sex="woman"): self.a = age self.s = sex Grandfather.__init__(self, age, sex) Grandmother.__init__(self, age, sex) print "I am MotherLi" def age(self): print "Mother age:"+str(self.a) def sex(self): print "Mother sex"+str(self.s) def Interesting(self): print "MotherLi Interesting"class SonWang(FatherWang, MotherLi): def __init__(self, age=13, sex="man"): FatherWang.__init__(self, age, sex) MotherLi.__init__(self, age, sex) print "I am SonWang" if __name__ == "__main__": son = SonWang() son.age() son.sex() son.Interesting()
执行结果:
I am Grandfather
I am GrandmotherI am FatherWangI am GrandfatherI am GrandmotherI am MotherLiI am SonWangFather age:13Father sex:manMotherLi Interesting奇怪,此时调用Interesting方法,返回的竟然是MotherLi类的。因为是这样的,使用新式类时,python发现SonWang类的两个父类FatherWang类和MotherLi类有相同的父类,那么找到SonWang类的第1个父类FatherWang类时,发现没有,便会去找SonWang类的第2个父类MotherLi类,找到了便执行。如果MotherLi类也没有,才会继续到Grandfather类找。
结论1:新式类多继承时,当子类的多个父类拥有相同第二级父类时,会先找子类的父类,如果都没有子类调用的方法,再按照子父类顺序进行下一级父类查找。那么很有趣的事情发生了,代码如下
class FatherGrandfather(object): def __init__(self, age=73, sex="man"): self.a = age self.s = sex print "I am FatherGrandfather" def age(self): print "FatherGrandfather age:"+str(self.a) def sex(self): print "FatherGrandfather sex:"+str(self.s) def Interesting(self): print "FatherGrandfather Interesting" class FatherGrandmother(object): def __init__(self, age=70, sex="woman"): self.a = age self.s = sex print "I am FatherGrandmother" def age(self): print "FatherGrandmother age:"+str(self.a) def sex(self): print "FatherGrandmother sex:"+str(self.s) def Interesting(self): print "FatherGrandmother Interesting"class MotherGrandfather(object): def __init__(self, age=73, sex="man"): self.a = age self.s = sex print "I am MotherGrandfather" def age(self): print "MotherGrandfather age:"+str(self.a) def sex(self): print "MotherGrandfather sex:"+str(self.s) def Interesting(self): print "MotherGrandfather Interesting"class MotherGrandmother(object): def __init__(self, age=70, sex="woman"): self.a = age self.s = sex print "I am MotherGrandmother" def age(self): print "MotherGrandmother age:"+str(self.a) def sex(self): print "MotherGrandmother sex:"+str(self.s) def Interesting(self): print "MotherGrandmother Interesting"class FatherWang(FatherGrandfather, FatherGrandmother): def __init__(self, age=43, sex="man"): self.a = age self.s = sex FatherGrandfather.__init__(self, age, sex) FatherGrandmother.__init__(self, age, sex) print "I am FatherWang" def age(self): print "Father age:"+str(self.a) def sex(self): print "Father sex:"+str(self.s)class MotherLi(MotherGrandfather, MotherGrandmother): def __init__(self, age=40, sex="woman"): self.a = age self.s = sex MotherGrandfather.__init__(self, age, sex) MotherGrandmother.__init__(self, age, sex) print "I am MotherLi" def age(self): print "Mother age:"+str(self.a) def sex(self): print "Mother sex"+str(self.s)class SonWang(FatherWang, MotherLi): def __init__(self, age=13, sex="man"): FatherWang.__init__(self, age, sex) MotherLi.__init__(self, age, sex) print "I am SonWang" if __name__ == "__main__": son = SonWang() son.age() son.sex() son.Interesting()
执行结果:
I am FatherGrandfather
I am FatherGrandmotherI am FatherWangI am MotherGrandfatherI am MotherGrandmotherI am MotherLiI am SonWangFather age:13Father sex:manFatherGrandfather Interesting执行结果出乎意料啊,是新式类啊,居然调用的不是MotherL的Interesting方法。
结论2:如果新式类多继承时,子类的多个父类没有相同的二级父类时,依然会按照经典类查找方式查找。
(3).关键字:super