《零基础入门学习Python》(38)类和对象:继承

前言

上一篇博客我们试图模拟一个场景,里面有一只乌龟和十只鱼,乌龟通过吃鱼来补充体力,当乌龟体力消耗殆尽或者与被吃光则游戏结束。现在我们想要扩展游戏,给鱼类进行细分。例如有金鱼(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.继承机制给程序猿带来最明显的好处是?

扫描二维码关注公众号,回复: 4005472 查看本文章

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

猜你喜欢

转载自blog.csdn.net/qq_38721302/article/details/83541923