python之面向对象编程(三)组合与继承

组合

面向对象编程代码复用的方式除了继承还有组合,组合是指在一个类中有一部分数据是其他类的实例(对象)。

案例

有二个类,分别是圆形、圆环,用组合的方式写。

以下是圆形类:

class Circle:
    pi = 3.14159265358979
    def __init__(self, r) -> None:
        self.r = r

    @property
    def area(self):
        return self.pi*self.r**2

    @property
    def perimeter(self):
        return self.pi*self.r*2

以下是圆环类:

class Ring:
    def __init__(self, inner, outer):
        inner, outer = (inner, outer) if inner < outer else (outer, inner)
        self.inner = Circle(inner)
        self.outer = Circle(outer)

    @property
    def area(self):
        return self.outer.area-self.inner.area

    @property
    def perimeter(self):
        return self.outer.perimeter+self.inner.perimeter

注意

圆环类的init函数,它的参数inner和outer是圆的半径,代码块中首先判断inner和outer两个参数的大小,确保大的赋值给outer、小的赋值给inner,然后用inner和outer作参数分别调用Circle类创建inner实例和outer实例。因此创建Ring类的实例中会有2个属性是Circle类的实例。

area方法和perimeter方法中都是调用了Circle中的对应方法进行计算。

总结

组合的方式可以提高代码复用率,将来遇到需求变更(例如更改pi的精度,只要求小数点后2位)时只需要改动少量代码,减少了出错的概率,提高了代码的可维护性。

继承

继承是一种定义类的方式,子类可以通过继承来复用父类的数据和代码块。

子类中有一项非常重要的隐藏数据——类指针,访问子类.变量(变量指向数据是它就是属性、指向代码块时它就是方法)的时候如果子类中无此变量时,解释器会通过类指针访问其父类,若父类中也无此变量时会继续向上访问祖父类,若一直访问到所有类的祖宗object类(在python中所有的类都继承自object类)时仍然找不到这个变量时,解释器就会报错。注意:类指针是单向的!意思就是子类可以访问父类的变量,而父类不能访问子类的变量。

另外:python支持多继承,而java只支持单继承。

补充:学习继承最重要的一点是搞清楚类指针、不同类的命名空间以及实例的命名空间创建、查找变量的过程,想要学明白的朋友最好是多阅读代码自己画一下内存示意图。

案例

class Animal:
    kind = '动物'

    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name}正在进食!')


class Cat(Animal):
    def __init__(self, name, count=0):
        self.count = count
        Animal.__init__(self, name)

    def mousing(self):
        print(f'猫咪“{self.name}”捉住了{self.count}只老鼠')


white_cat = Cat('大白猫')
white_cat.count += 1
white_cat.mousing()
white_cat.eat()
print(white_cat.kind)

out:
猫咪“大白猫”捉住了1只老鼠
大白猫正在进食!
动物

讲解

以上是一个关于继承的简单案例,接下来我通过这个案例详细梳理一下:

首先定义父类,父类是Animal,类体中有指向object类的类指针和kind属性、__init__方法和eat方法,这些是所有动物的通用属性和方法。写代码时父类代码要放在子类代码前面。

然后定义子类,定义子类Cat时将父类Animal当作参数,这就是子类继承了父类。类体中有指向Animal类的类指针、__init__方法和mousing方法,在__init__方法中初始化了count属性(抓到老鼠的数量),其余的属性则调用父类的__init__方法初始化。

接下来创建实例大白猫,创建实例时首先开辟一块命名空间,在这块内存空间中创建一个指向Cat类的类指针,然后将该内存空间的地址(该地址的名字叫self)传给Cat类的__init__方法,再创建了self.count属性,接下来调用父类的__init__方法创建了self.name属性。所以实例“大白猫”的命名空间中有:指向Cat类的类指针、name属性、count属性。

注意:实例的属性各不相同,所以属性都放在实例自己的命名空间中。一个类的所有实例调用的方法都一样,所以实例的方法都放在它们所属类的命名空间中!

图示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值