【“笨办法”学Python】44.继承与组合

44.继承与组合



前言

  大部分使用继承的场合都可以用组合取代或简化,而多重继承则需要不惜一切地避免!


一、继承

  继承就是用来指明一个类的大部分或全部功能都是从一个父类中获得的。

class Foo(Bar):
	pass

  上述代码就发生了继承,创建了一个叫做Foo的类,并让它继承自Bar。Python语言会让Bar的实例所具有的动作都工作在Foo的实例上。这样就可以把通用的功能放在Bar里面,然后再给Foo特别设置一些功能。
  当做这种设定的时候,父类和子类有3种交互方式:
(1) 子类上的动作完全等同于父类上的动作(隐式继承)
(2) 子类上的动作完全覆盖了父类上的动作(显式覆盖)
(3) 子类上的动作部分替换了父类上的动作(在运行前或运行后替换)

1、隐式继承

  父类里定义了一个函数但没有在子类中定义时发生的隐式行为。

class Parent(object):

    def implicit(self):
        print("PARENT implicit()")

class Child(Parent):
    pass

dad = Parent()
son = Child()

dad.implicit()
son.implicit()

  class Child:下面使用的pass是Python中创建空代码的方法。这样就创建了一个叫Child的类,但没有在里面定义任何细节。在这里它将会从它的父类继承所有的行为。运行代码会得到:
请添加图片描述
  调用son.implicit()时,且Child中没有定义过implicit()这个函数,但是这个函数依旧可以运行正常。它调用了父类Parent中定义的这个函数。
  这就说明,如果将函数放在基类中(Parent类),那么所有子类(Child类)将会自动获得这些函数功能。需要很多类的时候,可以避免重复写代码。

2、显式覆盖

  隐式调用函数有一个问题,有时候需要让子类中的函数有不同的行为,这种情况下隐式继承是做不到的。这时候需要覆盖子类中的函数,让它实现新功能。要做到这一点,只要在子类Child中定义一个同名函数就可以了。

class Parent(object):

    def override(self):
        print("PARENT override()")

class Child(Parent):

    def override(self):
        print("CHILD override()")

dad = Parent()
son = Child()

dad.override()
son.override()

  在两个类中都定义了一个叫override()的函数。运行代码会得到:
请添加图片描述
  运行dad.override()时,这里运行的是Parent.overr()函数,因为dad这个变量是定义在Parent里的。
  运行son.override()时,打印出来的结果是Child。override()函数,因为son是Child的一个实例,而子类中新定义的同名函数在这里取代了父类里的函数。

3、在运行前或运行后替换

  使用继承的第三种方法是覆盖的一个特例,在这种情况下,可以在父类中定义的内容运行前或者之后在修改行为。首先依然用一样的覆盖函数,然后要用Python的内置函数super()来调用父类Parent中的版本。

class Parent(object):

    def altered(self):
        print("PARENT altered()")

class Child(Parent):

    def altered(self):
        print("CHILD, BEFORE PARENT altered()")
        super(Child, self).altered()
        print("CHILD, AFTER PARENT altered()")

dad = Parent()
son = Child()

dad.altered()
son.altered()

  当调用son.altered()时,完成了以下操作:
(1) 由于覆盖了Parent.altered,实际运行的是Child.altered,因此会打印"CHILD, BEFORE PARENT altered()"
(2) 接下来使用了super()函数来获取Parent.altered这个版本
(3) 因此调用 super(Child, self).altered(),它知道继承的关系,并且会访问Parent类。即“用Child和self这两个参数调用super,然后在此返回的基础上调用altered”
(4) 到这里Parent.altered版本就会运行,而且打印出Parent里的消息
(5) 最后,从Parent.altered返回,Child.altered函数接着打印后面的消息

  运行代码会得到:
请添加图片描述

4、三种方式组合使用

class Parent(object):

    def override(self):
        print("PARENT override()")


    def implicit(self):
        print("PARENT implicit()")

    def altered(self):
        print("PARENT altered()")


class Child(Parent):

    def override(self):
        print("CHILD override()")

    def altered(self):
        print("CHILD, BEFORE PARENT altered()")
        super(Child, self).altered()
        print("CHILD, AFTER PARENT altered()")

dad = Parent()
son = Child()

dad.implicit()
son.implicit()

dad.override()
son.override()

dad.altered()
son.altered()

  运行代码会得到:
请添加图片描述


二、要用super()的原因

  多重继承是指定义的类继承了一个或多个类:

class SuperFun(Child, BadStuff):
	pass

  即“创建一个叫SuperFun的类,让它同时继承了Child和BadStuff两个类”。一旦在SuperFun实例上调用任何隐式动作,Python就必须回到Child和BadStuff的类层次结构中查找可能的函数,而且必须要用固定的顺序去查找。为实现这一点,Python使用了一个叫“方法解析顺序(method resolution order,MRO)”的东西,还用了一个叫C3的算法。
  因为有这个MRO和C3算法,Python会自己用MRO去工作,然后会提供super()函数,用来在各种需要修改行为类型的场合处理一切,有了super()就不用担心把继承关系弄糟,因为Python会为你找到正确的函数。


三、super()和__init__搭配使用

  super()最常见的用法是在基类的__init__()函数中使用。通常这也是唯一可以进行这种操作的地方,这时需要在子类里做一些事情,然后在父类中完成初始化:

class Child(Parent):
	
	def __int__(self, stuff):
	self.stuff = stuff
	super(Chile, self).__init__()

  在__init__()里面先设置了一些变量,然后才让Parent.__init__完成初始化。
  有两个例子可以帮助理解:

# 示例一
class Person(object):
    def __init__(self,name,gender,age):
        self.name = name
        self.gender = gender
        self.age = age

class Student(Person):
    def __init__(self,name,gender,age,school,score):
        super(Student,self).__init__(name,gender,age)
        self.school = school
        self.score = score

s = Student('Alice','female',18,'Middle school',87)

print(s.name)
print(s.gender)
print(s.age)
print(s.school)
print(s.score)

  运行代码会得到:
请添加图片描述


# 示例二
class Person(object):
    def __init__(self,name,gender,age):
        self.name = name
        self.gender = gender
        self.age = age

    def override(self):
        print("CHILD override()")

class Student(Person):
    def __init__(self,school,score):
        self.school = school
        self.score = score

p = Person('Alice','female',18)
s = Student('Middle school',87)

s.override()
print(p.name)
print(p.gender)
print(p.age)
print(s.school)
print(s.score)

  运行代码会得到:
请添加图片描述


四、组合

  继承是一种很有用的技术,不过还有一种实现相同功能的方法,就是直接使用别的类和模块,而非依赖与隐式继承。很容易通过调用模块里的函数来实现。

class Other(object):

    def override(self):
        print("OTHER override()")

    def implicit(self):
        print("OTHER implicit()")

    def altered(self):
        print("OTHER altered()")

class Child(object):

    def __init__(self):
        self.other = Other()

    def implicit(self):
        self.other.implicit()

    def override(self):
        print("CHILD override()")

    def altered(self):
        print("CHILD, BEFORE OTHER altered()")
        self.other.altered()
        print("CHILD, AFTER OTHER altered()")

son = Child()

son.implicit()
son.override()
son.altered()

  这里Other和Child不是父类子类的关系,而是“A里有B的关系”,这里Child里有一个Other用来完成它的功能。运行代码会得到:

请添加图片描述


# 五、继承和组合的应用场景   三个大体的指导原则: (1) 不惜一切代价的避免使用多重继承,因为它太复杂以至于不可靠。如果非要用,那得准备好钻研类层次结构,以及花费时间去找各种东西的来龙去脉。 (2) 如果有一些代码会在不同位置和场合应用到,那就用组合来把它们做成模块。 (3) 只有在代码的可复用部分之间有清楚的关联,可以通过一个单独的共性联系起来的时候,才使用继承,或者,现有代码或者别的不可抗拒因素所限非要继承不可的时候去使用继承。

总结

  以上内容介绍了注释("#")的作用,有关Python、数据科学、人工智能等文章后续会不定期发布,请大家多多关注,一键三连哟(●’◡’●)。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机器视觉小学徒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值