python第38课练习—类和对象:继承
2019-06-12 本文已影响0人
YoYoYoo
1、继承机制给程序员带来最明显的好处是?
答:可以偷懒,据说这是每一个优秀程序员的梦想!
- 如果一个类A继承自另一个类B,就把这个A称为B的子类,把B称为A的父类、基类或超类。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码(偷懒)。
- 在子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见做法。
2、如果按以下方式重写魔法方法 __init__,结果会怎样?
class MyClass:
def __init__(self):
return 'I love FishC.com!'
答:会报错,因为__init__特殊方法不应当返回除了None以外的任何对象。
>>> myclass = MyClass()
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
myclass = MyClass()
TypeError: __init__() should return None, not 'str'
3、当子类定义了与父类相同名字的属性或方法时,Python是否会自动删除父类的相关属性或方法?
答:不会删除!Python的做法跟其他大部分面向对象编程语言一样,都是将父类属性或方法覆盖,子类对象调用的时候会调用到覆盖后的新属性或方法,但父类的仍然还在,只是子类对象“看不到”。
4、假设已有鸟类的定义,现在我要定义企鹅类继承于鸟类,但我们都知道企鹅是不会飞的,我们应该如何屏蔽父类(鸟类)中飞的方法?
答:覆盖父类方法,例如将函数体内容写pass,这样调用fly方法就没有任何反应了。
class Bird:
def fly(self):
print('Fly away!')
class Penguin(Bird):
def fly(self):
pass
>>>bird = Bird()
>>>penguin = Penguin()
>>>bird.fly()
Fly away!
>>>penguin.fly()
5、super函数有什么“超级”的地方?
答:super函数超级之处在于你不需要明确给出任何基类(父类)的名字,它会自动帮你找出所有基类以及对应的方法。由于你不用给出基类的名字,这就意味着你如果需要改变类继承关系,你只要改变class语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。
举个例子
- A: 没有用super函数
def __init__(self):
Fish.__init__(self)
self.hungry = True
- B:用了super函数后
def __init__(self):
super().__init__() # 修改父类的时候这里就不用修改了
self.hungry = True
6、多重继承使用不当会导致重复调用(也叫钻石继承、菱形继承)的问题,请分析以下代码在实际编程中有可能导致什么问题?
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...')
答:多重继承容易导致重复调用问题,下边实例化D类后我们发现A被前后进入两次。
这有什么危害呢?举个例子,假设A的初始化方法里有一个计数器,那这样D一实例化,A的计数器就跑了两次(如果遭遇多个钻石重叠结构重叠还要更多),很明显是不符合程序设置的初衷的(程序应该可控,而不能受到继承关系影响)。
进入D...
进入B...
进入A...
离开A...
离开B...
进入C...
进入A...
离开A...
离开C...
离开D...
为了让大家明白,这里只是举例简单的钻石继承问题,在实际编程中,如果不注意多重继承的使用,会导致比这个复杂N倍的现象,调试起来不是一般的痛苦......所以要尽量避免多重继承。
7、如何解决上一题中出现的问题?
答:super函数再次大显神通。
class A():
def __init__(self):
print('进入A...')
print('离开A...')
class B(A):
def __init__(self):
print('进入B...')
super().__init__()
print('离开B...')
class C(A):
def __init__(self):
print('进入C...')
super().__init__()
print('离开C...')
class D(B,C):
def __init__(self):
print('进入D...')
super().__init__()
print('离开D...')
d = D()
输出:
进入D...
进入B...
进入C...
进入A...
离开A...
离开C...
离开B...
离开D...
练习
1、定义一个点(Point)类和直线(Line)类,使用getLen的方法可以获得直线的长度。
提示:
- 设点A(x1,y1)、点B(x2,y2),则两点构成的直线长度
- Python中计算开根号可以使用math模块中的sqrt函数
- 直线需有两点构成,因此初始化时需要有两个点(Point)对象作为参数。
代码清单:
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
p1 = Point(1,1)
p2 = Point(4,5)
line = Line(p1,p2)
print(line.getLen())
输出:
5.0