写在之前
面向对象的程序设计都三个主要的特征:封装,继承,多态,这个也是类里面的重要内容,这三个特征我会从今天开始依次开始写,今天我们先来看第一个:「封装」,这一部分我会分两次来写,接下来进入正题。
概念
对于「继承」的概念,我们先来看在《维基百科》中的定义:
继承(Inheritance)是面向对象软件技术当中的一个概念。如果一个类别 A “继承”自另一个类别 B,就把这个 A 称为 “B 的子类别”,而把 B 称为 “A 的父类别”,也可以称为 “B 是 A 的超类”。
「继承」可以使得子类具有父类的各种属性和方法,而不需要再去重复编写相同的代码。在令子类继承父类的同时,可以重新去定义某些属性,并可以重写某些方法,简单点来说就是覆盖掉父类的属性和方法,使其拥有和父类不同的功能。此外,还可以为子类追加新的属性和方法,这都是些常见的做法。
所以由上面的描述我们可以知道使用继承的好处:一是可以实现代码的重用,但又不仅仅是代码重用;再者就是可以实现属性和方法的继承。当然了这个并不是全部,随着我们之后的学习,相信你对继承会有更深层次的了解。
如果你之前学过 Java 或者其它 OOP 语言的话,可能会对继承有不一样的理解。其实在 Python 里,因为存在「鸭子类型」(duck typing) ,使得接口定义的重要性大大的降低,从而继承的作用也被进一步的削弱了。至于什么是鸭子类型,因为现在还有几个没有讲,所以这个我会在之后的文章单独拿出来说。
再补充一点,如果你看过我前面的文章,你可能看到过我过所有的类都是 object 的子类,但是这里并没有显式的把 object 写出来,而是用隐式的方法继承了 object。总而言之,object 就是所有类的父类。
单继承
所谓的单继承,就是只从一个父类那里继承。
>>>classA:
...pass
...
>>>classB(A):
...pass
...
上面的例子,类 A 是一个普通的类,类 B 则是定义的一个子类,它用 B(A) 的形式继承了类 A(父类),虽然这个父类里什么也没有。
子类 B 继承父类 A 的方式就是在类名后面的括号里写上父类的类名。既然是继承了父类,那么根据我们上面说的,父类的一切都带到了子类里。上面也说了,在 Python3 里所有的类都是 object 的子类,但是不用写出 object,但是对于继承其它的类就不能隐藏了,必须要显式的写上父类的类名。
还记得我们前面的文章中写过的可以得到类的父类的那个特殊函数吗?我们在这里可以用一下:
>>> A.__base__
>>> B.__base__
为了深入的了解一下「继承」的作用,我们下面让父类做点儿事情:
>>>classA:
...def__init__(self):
...print('my name is rocky')
...
>>>classB(A):
...pass
...
>>>b = B()
my nameisrocky
父类 A 中增加了初始化函数,然后子类 B 继承了它。我们已经知道,当建立实例的时候,首先要执行类中的初始化函数,因为子类 B 继承了父类,就把父类中的初始化函数拿到了子类里面,所以在 b = B() 的时候,执行了父类中定义的初始化函数,这就是继承,而且是从一个父类那里继承来的,所以称为单继承。
下面我们来看一个相对完整一些的例子:
classPerson:
def__init__(self,name):
self.name = name
defheight(self,m):
h = dict((['height',m],))
returnh
defsex(self,se):
sex1 = se
returnsex1
classBoy(Person):
defget_name(self):
returnself.name
if__name__=="__main__":
lee = Boy('rocky')
print(lee.get_name())
print(lee.height(170))
print(lee.sex('男'))
运行上面的代码,所得的结果如下:
rocky
{'height':170}
男
首先我们定义了一个 Person 类,然后定义了一个子类 Boy。
在子类 Boy 中只写了一个方法 get_name(),但是因为继承了 Person,那么 Boy 就拥有了 Person 中的全部方法和属性,在子类 Boy 的方法 get_name() 中,使用了属性 self.name,但是在类 Boy 中并没有创建这个属性,只是因为其继承了 Person,在父类中有初始化函数。所以在使用子类创建实例的时候,必须要传一个参数,然后再调用方法。对于实例方法 lee.height(170) 也是因为继承的缘故。
在上面的程序中,子类 Boy 并没有和父类有重复的属性和方法,但有时候会出现下面的这种情况:
classBoy(Person):
def__init__(self):
self.name ='snow'
defget_name(self):
returnself.name
在子类里面也有一个初始化函数,并且定义了一个实例属性 self.name = 'snow',而在父类中也有初始化函数,在这种情况下运行以后会出现什么结果呢?你先猜一下,猜完了继续往下看:
TypeError: __init__() takes1positionalargumentbut2were given
嚯,竟然报错了。报错的消息是创建实例的时候,传入的参数个数多了。这个的根源在于,子类 Boy 中的初始化函数只有一个 self,但因为跟父类的初始化函数重名,虽然继承了父类,但是将父类中的初始化函数覆盖掉了,所以现在的实例化子类不应该再显式的传参数。
if__name__ =="__main__":
lee = Boy()
print(lee.get_name())
print(lee.height(170))
print(lee.sex('男'))
如此修改以后再运行,显示的结果如下:
snow
{'height':170}
男
从结果中我们不难看出来,如果子类中的属性或者方法与父类同名,那么就不再继承父类的该属性或方法。
写在之后
又到周一啦,时间过的贼啦快,明天还有一篇继承哦,记得看。
新的一周,就是干!
The end。
周一扫码可以少 Bug!
领取专属 10元无门槛券
私享最新 技术干货