前言
上一篇博客我们试图模拟一个场景,里面有一只乌龟和十只鱼,乌龟通过吃鱼来补充体力,当乌龟体力消耗殆尽或者与被吃光则游戏结束。现在我们想要扩展游戏,给鱼类进行细分。例如有金鱼(Goldfish)........那么我们就在思考一个问题:能不能每次都要重头到尾重新定义一个新的鱼类?因为我们知道大部分鱼的属性和方法都是相似的。这样就引出了继承
知识点
继承:
class DerivedClassName(BaseClassName):#BaseClassName是父类
举个例子:
>>> class Parent:
def hello(self):
print('正在调用父类的方法')
>>> class chlid(Parent):
pass
>>> p=Parent()
>>> p.hello()
正在调用父类的方法
>>> c = chlid()
>>> c.hello()
正在调用父类的方法
#我么都知道在child类中没有定义任何的方法或者对象,但是它继承了Parent类,所以他可以调用hello方法
注意:如果子类中定义与父类同名的方法或者属性,则会自动覆盖父类对应的方法或属性
>>> class Parent:
def hello(self):
print('正在调用父类的方法')
>>> class Child(Parent):
def hello(self):
print('正在调用子类的方法...')
>>> c=Child()
>>> c.hello()
正在调用子类的方法...
那先我们将前言提到的程序加已改进,增加了各种鱼,当然我们现在只将继承机制,所以下面代码比较简单,最上面提到的游戏可以去这里查看
import random as r
class Fish:
def __init__(self):
self.x = r.randint(0,10)
self.y = r.randint(0,10)
def move(self):
self.x -= 1
print("我的位置是: ",self.x,self.y)
class Goldfish(Fish):
pass
class Carp(Fish):
pass
class Salmon(Fish):
pass
class Shark(Fish):
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print('吃货的梦想就是天天有得吃...')
self.hungry = False
else:
print("太撑了,吃不下了....")
======================== RESTART: D:/python/fishs.py ========================
>>> fish = Fish()
>>> fish.move()
我的位置是: -1 3
>>> fish.move()
我的位置是: -2 3
>>> goldfish = Goldfish()
>>> goldfish.move()
我的位置是: 5 4
>>> goldfish.move()
我的位置是: 4 4
>>> goldfish.move()
我的位置是: 3 4
>>> shark = Shark()
>>> shark.eat()
吃货的梦想就是天天有得吃...
>>> shark.eat()
太撑了,吃不下了....
但是到了shark调用move方法的时候就出现了错误。
>>> shark.move()
Traceback (most recent call last):
File "<pyshell#44>", line 1, in <module>
shark.move()
File "D:/python/fishs.py", line 10, in move
self.x -= 1
AttributeError: 'Shark' object has no attribute 'x'
原来我们这里在shark中重写了__init__方法,所以子类将父类的__init__方法直接就覆盖了
接下来我们提出两种解决方法:
1.调用未绑定的父类方法
import random as r
class Fish:
def __init__(self):
self.x = r.randint(0,10)
self.y = r.randint(0,10)
def move(self):
self.x -= 1
print("我的位置是: ",self.x,self.y)
class Goldfish(Fish):
pass
class Carp(Fish):
pass
class Salmon(Fish):
pass
class Shark(Fish):
def __init__(self):
Fish.__init__(self)#之所以叫未绑定的父类方法,是因为self参数是子类中参数,而不是父类中的参数
self.hungry = True
def eat(self):
if self.hungry:
print('吃货的梦想就是天天有得吃...')
self.hungry = False
else:
print("太撑了,吃不下了....")
======================== RESTART: D:/python/fishs.py ========================
>>> shark = Shark()
>>> shark.move()
我的位置是: 5 2
==============================================================================
#就先当于:
>>> Fish.__init__(shark)
>>> shark.move()
我的位置是: 0 6
2.使用super函数
import random as r
class Fish:
def __init__(self):
self.x = r.randint(0,10)
self.y = r.randint(0,10)
def move(self):
self.x -= 1
print("我的位置是: ",self.x,self.y)
class Goldfish(Fish):
pass
class Carp(Fish):
pass
class Salmon(Fish):
pass
class Shark(Fish):
def __init__(self):
super().__init__()#在这里调用super方法
self.hungry = True
def eat(self):
if self.hungry:
print('吃货的梦想就是天天有得吃...')
self.hungry = False
else:
print("太撑了,吃不下了....")
======================== RESTART: D:/python/fishs.py ========================
>>> shark = Shark()
>>> shark.move()
我的位置是: 4 0
Python还支持多重继承
>>> class Base1:
def foo1(self):
print("我是foo1,我为Base1代言")
>>> class Base2:
def foo2(self):
print("我是foo2,我为Base2代言")
>>> class c(Base1,Base2):
pass
>>> c=c()
>>> c.foo1()
我是foo1,我为Base1代言
>>> c.foo2()
我是foo2,我为Base2代言
但是他可能会导致代码混乱,当你不确定你必须要使用多重继承的语法的时候,尽量不要去使用,因为他有可能出现不可预知的bug。
测试题
0.继承机制给程序猿带来最明显的好处是?
0.减少相同代码量
1.如果按照以下方式重写魔法方法__init__,结果会怎样。
1.会报错,因为__init__特殊方法不应当返回除了None以外的任何对象。3
2.当定义了与相同名字的属性或者方法时,Python是否会自动删除父类的相关属性或者方法?
2.不会删除,只会覆盖
3.假设已经有鸟类的定义,现在我要定义企鹅类继承于鸟类,但我们都知道企鹅不会飞,我们应该如何来屏蔽父类中飞的方法?
3.重写父类方法
4.super函数有什么“超级”的地方?
4.super 函数超级之处在与你不需要明确给出任何基类的名字,它会自动帮您找出所有基类以及对应的 方法。由于你不用给出基类的名字,这就意味着你如果需要改变了类继承关系,你只要改变 class 语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。
5.多重继承使用不当会导致重复调用(也叫钻石继承)的问题,请分析一下代码在实际编程中可能出现的问题:
class A():
def __init__(self):
print('进入A..')
print('离开A..')
class B(A):
def __init__(self):
print('进入B..')
A.__init__(self)
print('离开B..')
class C(A):
def __init__(self):
print('进入C..')
A.__init__(self)
print('离开C..')
class D(B,C):
def __init__(self):
print('进入D..')
B.__init__(self)
C.__init__(self)
print('离开D..')
========================== RESTART: D:/python/复用.py ==========================
>>> d=D()
进入D..
进入B..
进入A..
离开A..
离开B..
进入C..
进入A..
离开A..
离开C..
离开D..
我在另一篇博客中更详细的讲解了这个问题
如何解决该问题也请大家去看上面这个博客
动动手
import math
class Point():
def __init__(self,x=0,y=0):
self.x = x
self.y = y
def getx(self):
return self.x
def gety(self):
return self.y
class Line():
def __init__(self,p1,p2):
self.x = p1.getx()-p2.getx()
self.y = p1.gety()-p2.gety()
self.len = math.sqrt(self.x*self.x+self.y*self.y)
def getLen(self):
return self.len
======================== RESTART: D:/python/length.py ========================
>>> p1 = Point(1,1)
>>> p2 = Point(4,5)
>>> line = Line(p1,p2)
>>> line.getLen()
5.0