狼狼 发表于 2017-5-6 06:05:20

Learn Python The Hard Way学习(44)

balabala...讲了一个故事,就不翻译了。



只要记着:我们要尽量简单的使用继承,或者用组合代替继承,而且做好不要使用多继承。




什么是继承?

继承被用来指明一个类可以从它的父类取得大部分或者全部的特征。当你写class Foo(Bar)的时候,继承就发生了。Foo类的实例和Bar类的实例一样工作。继承就是为了你能再Bar类中定义普通的方法,而在Foo类中定义特殊的方法。




当做了继承后,父类和子类有三个相互影响的方法:


[*]子类的动作暗含了父类的动作
[*]子类的动作覆盖了相应的父类动作
[*]子类的动作改变了相应的父类动作

暗含继承



先在父类定义一个implicit函数,子类什么也不做。

class Parent(object):

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

class Child(Parent):
pass

dad = Parent()
son = Child()
dad.implicit()
son.implicit()






在子类中使用pass就是告诉python,这里是一个空的代码块。就是说子类完全继承父类的特征。运行代码的结果如下:

parent implicit()

parent implicit()






注意,我调用了son.implicit()在16行,但是子类并没有这个方法,它调用了父类的对应方法执行。这里告诉你,子类会自动取得父类的所有特征,不需要重复编写代码。




覆盖暗含

问题是有时候你希望子类继承的函数有不同的提现,解决办法就是覆盖父类的函数,定义一个同名的函数在子类中,代码如下:

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()






运行效果如下:

parent override()

child override()






正如你看到的,子类运行相应的函数的时候会运行覆盖过的函数,相当于子类的新版本。




改变之前或者之后

改变和覆盖有一些区别,我们需要先覆盖父类的函数,然后再通过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行比较重要,当我调用son.altered()的时候做了下面这些事情:


[*]因为我已经覆盖了父类的函数altered,所以第9行会在子类调用altered的时候执行。
[*]我希望在第9行后执行父类的altered,所以我使用了super。
[*]在第10行我调用super(child, self).altered(),有点像不久前使用的getattr函数,不过这里是从继承的父类那里调用函数。
[*]然后,父类的函数运行,并打印信息。
[*]最后,从父类函数返回,继续运行剩下的子类的语句。

运行结果如下:



parent altered()

child, before parent altered()

parent altered()

child, after parent altered()






混合使用三个方法

class Parent(object):

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

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

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()






给代码加上注释,区别这三种方式,运行如下:

parent implicit()

parent implicit()

parent override()

child override()

parent altered()

child, before parent altered()

parent altered()

child, after parent altered()






使用super()的理由

现在有个麻烦,就是多重继承,意思是一个类继承了多个类,像下面这样:

class SuperFun(Child, BadStuff):

pass




就像是说:”创建一个SuperFun,同时继承Child和BadStuff类。




这样的话,当SuperFun实例有隐含的动作的时候,python需要去两个类中取寻找,这就必须有一定的顺序。Python使用一个叫“Method
Resolution Order”(MRO)和C3算法实现。




由于MRO很复杂,Python不可能让你直接使用它,所以给了一个super方法让你调用,使用后python会自动实现内部的调用。




在__init__中使用super()

通常super()都是在__init__中使用的,完成父类的初始化,下面是一个例子:

class Child(Parent):

def __init__(self, stuff):

    self.stuff = stuff

    super(Child, self).__init__()




组成

继承很有用,但是另外一个方法可以使用类和模块最相同的事情,而不依赖隐含继承。下面这种方法可以简单的实现上面那三种方式。看例子:

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()






上面的代码我没有用Parent,以免产生is-a关系的误解,运行如下:

other implicit()

child override()

child, before other altered()

other altered()

child, after other altered()






你可以看到上面的代码完成了相同的功能。唯一不同的是我必须定义一个Child.implicit的函数。现在我可以说,我需要Other类的话,只要把它写到other.py文件中,然后通过模块调用就好了。




什么时候用继承,什么时候用组合

不管是继承还是组合,都是为了解决问题,继承可以让你继承父类的特征,组合可以让你简单的从其他类中调用函数。




他们都是解决重复使用的问题,到底用哪一个合适呢?这个问题是很主观的,不管我给出了我自己的三条规则:


[*]不要使用多重继承,它太复杂了。一定要用的话,必须清楚的了解它。
[*]使用组合打包你的代码,它可以在其他无关的地方使用你打包好的模块。
[*]在非常明确的复用代码情况下才使用继承。

不管怎么样,不要墨守成规,你可以在工作中和同事制定你们自己的规则,能很好的解决问题就可以了。






加分练习

这里只有一个加分练习,因为这个练习比较大,去看看http://www.python.org/dev/peps/pep-0008/并且用到你的代码中。你会发现和我们书上说的不太一样,不过现在你必须去理解它的描述并且用到你的代码中。用了以后可能会让代码有点混乱,不过你必须理解它的重要性,这是一种好的规范。
页: [1]
查看完整版本: Learn Python The Hard Way学习(44)