Exercise 44: 继承 vs 组合

原文链接:http://learnpythonthehardway.org/book/ex44.html

       在关于英雄打败邪恶的坏蛋的童话故事里面总有一个非常黑暗的地带。它可能是一个洞穴,一片森林,甚至另一个星球,反正就是每个人都知道主角一定会去的一个地方。当然,在你简短的了解了恶棍是怎么坏之后,是的,主角英雄就必须去往那该死的森林杀了那个坏蛋了。之后就英雄在那个邪恶的森林里陷入了各种有生命危险的情况。

      你很少看到在童话故事中主角会聪明的避免发生上述整个情况。你从没听主角这样说过,“等一分钟,如果我把自己的命运交给大海随波逐流我可能会死去,那么她就不得不去和一个叫做Humperdink的丑陋的王子结婚。Humperdink啊!我想我宁愿待着这里做一个农家男孩靠收租金过活。“ 如果他那样做了的话,那么就不会陷入火海,不会有死亡,不会有动乱,不会有刀光剑影,不会有巨人,或者其它任何故事中有的东西。因为这些,这些故事中的黑暗地带就像是一个黑洞一样无论这些主角怎么做都会被拉进来。

       在面向对象编程中,继承就是一片危险的地带。有经验的程序知道怎么避免这些危险,因为他们对黑暗地带继承于邪恶之母的多重继承有更深的了解。她喜欢用它错中复杂的巨大牙齿吞噬软件和项目,然后慢慢咀嚼。但是这片地带拥有强大的功能,所以几乎每个程序员都必须去学会通过它,并且在真正能称作程序员之前从邪恶女皇面前存活下来。你如果不能抵抗继承这片黑暗地带的拖拉的话,那么你就走进去吧。在经过冒险之后你就会学会远离这片该死的黑暗地带了,如果你再一次不得不去的话就带上一支军队前往吧。

       上面只是一个简单有趣的方式来告诉你避免一个叫做继承的一些东西。那些现在正在和邪恶女皇战斗的的程序员可能会告诉你应该勇敢的走进去。他们之所以这么说是他们需要你的帮助因为他们可能创建了太多无法处理的东西。但是你永远要记住下面这句话:大多数的使用的基础可以被简化或者用组合去替代,而至于多重继承应该不惜一切代价避免。


继承是什么?

       继承一般是指一个类从一个父类中得到大多数或者全部的属性。无论什么时候你写下诸如 class Foo(Bar) 就是在在说“创建了一个继承于Bar的Foo类”。当你这样操作后,编程语言会让你对Foo实例做的任何操作就像在Bar实例上做操作一样。这样做可以让你把一些共同的函数放在Bar类中,在需要一些特殊的操作就可以在Foo类中添加。
       当你做这种专门化操作的时候,有三种方式会使得父类和子类能相互作用:
1、子类上的操作就是父类中的某个操作。
2、子类重载父类中的操作。
3、子类修改父类操作。

我将用实例代码按顺序来向你展示上述每一条语句的含义。

隐式继承

       首先我向你展示隐式操作,它只在你在父类中定义了一个函数,而不在子类中定义的时候发生。
class Parent(object):
	
	def implicit(slef):
		print "PARENT imlicit()"
	
class Child(Parent):
	pass

dad = Parent()
son = Child()

dad.implicit()
son.implicit()
       在 class Child中使用的pass:是你告诉Python你想要一个空模块。就是创建了一个命名为 Child 的类但是在里面没有定义任何新的东西。因此它所有的行为操作都继承于Parent类。当你运行该脚本的时候可以得到下面的结果:
c:\>python ex44-1.py
PARENT imlicit()
PARENT imlicit()
      可以注意到即使我在13行调用 son.implicit() ,即使Child类没有定义一个 implicit的函数,它还是调用了在Parent中定义的 implicit函数。这就是向你展示了,如果你在一个基类(例如:Parent类)中定义了一个函数,那么在所有的子类(例如: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 的函数,那么让我们来运行下看看发生了什么。
c:\>python ex44-2.py
PARENT override()
CHILD override()
       你可以看见,当运行到14行的时候,它执行的是 Parent.override 函数因为 dad 是Parent类的一个实例对象。但是当运行到第15行的时候它打印出来的却是 Child.override 的消息,因为 son 是Child的一个实例化对象并且Child通过定义自己版本的函数来重载了override函数。
       现在先休息一会,在继续学习之前先把这两个概念弄明白。

修改前或者修改后

       第三种使用继承的方式是重载的一种特殊情况,这种情况下你可以在Parent类执行前或后来改变函数的行为操作。首先你得像上一个练习中一样重载该函数,但是之后你要用一个Python内置的函数 super 来让父类调用。下面这个例子中的操作可以让你弄明白我描述的内容:
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()
       这里重要的是第9-11行,在Child类中当调用 son.altered() 函数时做了如下操作:
1、因为我已经继承了 Parent.altered然后执行了 Child.altere 操作,并且在第9行执行了你们所期望的操作。
2、在这种情况下,我想要同时做修改前和修改后的操作所以在第9行,我想要使用 super 关键字来调用 Parent.altered 函数。
3、在第10行我调用了 super(Child, self).altered() ,这个有点像你之前使用过的 getattr 函数 ,但是它更知道怎么使用继承来为你获取Parent 类中的属性和方法。这样说你应该能理解:带 Child 和 self 两个参数来调用 super 函数,然后不管它还回什么都用返回的来调用altered函数。
4、这这里,Parent.altered 的函数被执行,并且打印出 Parent 对于的消息。
5、最后,返回了Parent.altered和Child.altered函数的执行结果后继续打印出后面的打印消息。
如果你运行这个脚本的话,应该可以看到下面的结果:
PARENT altered()
CHILD ,BEFORE PARENT altered()
PARENT altered()
CHILD ,AFTER PARENT altered()


三种组合

       为进一步展示这些,我写了最后一个版本的一个文件中来向你们展示每一种继承的相互作用:
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 ,slef).altered()
		print "CHILD ,AFTER PARENT altered()"

dad = Parent()
son = Child()

dad.implicit()
son.implicit()

dad.override()
son.override()

dad.altered()
son.altered()
      通读代码中的每一行,并且写一个注释来解释该行做了什么操作,是否重载。然后运行该脚本看看是不是你所期望的结果:
c:\>python ex44-4.py
PARENT implicit()
PARENT implicit()
PARENT override()
CHILD override()
PARENT altered()
CHILD ,BEFORE PARENT altered()
PARENT altered()
CHILD ,AFTER PARENT altered()

关于 super() 函数的解释

       super() 函数看起来似乎就是一个普通的函数,但是当我们遇到多重继承的时候往往会陷入麻烦之中。多重继承就是当你定义一个类,这个类继承于一个或者多个类时,就像下面这样:
class SuperFun(Child ,BadStuff):
    pass
这个例子告诉我们:创建了一个名为 SuperFun 的类,它同时继承了Child 类和 BadStuff类。

       在这种情况下,任何 SuperFun的实例句柄无论何时都隐式继承了父类中的各种操作,Python 必须去查看在 Child 类和 BadStuff类层次结构中可能继承的函数,但是这种继承需要一致的顺序去继承。为了判断继承的属性和方法来自于哪个类Python使用了被称为“方法解析顺序(MRO)”的方法和一个叫做 C3的算法来直接查找。
       由于MRO是非常复杂的,并且使用了一个定义好的算法,Python没了这些就无法正常工作。这看起来令人苦恼,对吧?为了解决这个问题,Python 给了你这个 super()函数,让它来为你处理这一切,而你只需要像我上面做的 Child.altered 操作一样改变相应的行为类型就可以了。使用super()你不用担心是否得到正确的结果,Python 肯定会为你找到正确的函数给你。

带 __init__ 使用 super()

       super()实际上最常用在基类中的__init__函数。这个函数通常你在子类中做一些操作时需要调用的唯一一个地方,调用后在父类中完成各种初始化操作。这里有个解释这操作的例子:
class Child(Parent):

    def __init__(self ,stuff):
        self.stuff = stuff
        super(Child ,self).__init__()
这个非常想上面提到的 Child.altered 的例子,我在 __init__ 函数中设置的一些变量在Parent的__init__函数初始化之前。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值