派森编程学习笔记“上帝视角下的游戏操盘手” 【自学笔记】
继承在Python中继承是指:在类的基础上,它可以实现现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
编写类时,不一定非得从零开始,可以使用继承,一个类继承另一个类时,它将自动获得另一个类的所有属性和方法,原有的类称为父类、超类、基类多种叫法,而新类称为子类或派生类。 子类继承了其父类的所有属性和方法,同时也可以定义属于自己的属性和方法,继承的过程,就是从一般到特殊的过程。
重用现有类的功能,并在此基础上进行扩展。子类直接具有父类的成员(共性),还可以扩展新功能。耦合度高,父类的变化可以直接影响子类。
父类相对于子类更抽象,范围更宽泛,子类相对于父类更具体,范围更狭小。
看一段使用了继承的代码:class Cat(object):
def __init__(self):
self.color = 'red'
def run(self):
print('猫咪今天很活泼')
# 子类
class Cat_son(Cat):
pass
cat = Cat_son()
print('小猫的颜色是' + cat.color)
cat.run()
在Cat()类的括号里我们写了一个object
在Python中创建类,在类名的后面可以加括号也可以不加,建议是加上括号。加上括号,里面可以继承其他的类,继承了object的类是新式类,object是所有内建类型的父类。
在Cat_son()这个类中,继承了Cat()类的属性和方法,创建Cat_son()类的实例化对象后可以直接调用Cat()类中的属性和方法。
单继承:子类只允许继承一个父类,这样的继承方式在于其继承有序,逻辑结构清晰、语法简单且隐患少,而缺点则是子类的功能只能在当前唯一的继承中扩展。
但是父类可以同时被多个子类继承,和每一个类可以创建无限个实例化对象相同。
class Person():
def __init__(self):
# 实例属性
self.name = '大白'
# 实例方法
def run(self):
print(self.name + '在跑步!')
# 创建Student类,继承Person类,Person类为父类,Student类为子类
class Student(Person):
# 子类可以继承父类中的所有方法和属性,即使子类没有自己的方法和属性
pass
# 创建子类的实例化对象
s1 = Student()
# 子类直接使用父类的方法
s1.run()
# 子类直接使用父类的属性
print(s1.name)
虽然子类Student()中没有定义__init__()方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的实例化对象,就默认执行了继承自Person()类的__init__()方法。
我们可以使用**isinstance()**函数来判断某个实例对象是否属于某个类。注意:是实例对象。具体用法是传入两个参数,第一个参数是实例对象名,第二个对象是类名,输出的是True或者False。
class Person():
def __init__(self):
# 实例属性
self.name = '大白'
class Student(Person):
pass
# 创建子类的实例化对象
s1 = Student()
#使用isinstance()判断s1是否属于Person()类
print(isinstance(s1,Person))
另外需要注意的一点:父类创建的实例化对象不属于子类,子类创建的实例化对象属于父类,而且他们所创建的实例化对象都属于object()类。
class Person():
def __init__(self):
# 实例属性
self.name = '大白'
class Student(Person):
pass
# 创建子类的实例化对象
s1 = Student()
p1 = Person()
#使用isinstance()判断s1是否属于Person()类
print(isinstance(s1,Person))
#使用isinstance()判断s1是否属于object
print(isinstance(s1,object))
#使用isinstance()判断p1是否属于object
print(isinstance(p1,object))
多重继承
多重继承:一个子类继承两个或者两个以上的父类,父类中的属性和方法同时被子类继承下来。
# Father父类
class Father(object):
# 实例方法
def hobby(self):
print("我喜欢玩电子游戏。")
# 实例方法
def cook(self):
print("我不喜欢做饭。")
# Mother父类
class Mother(object):
# 实例方法
def cook(self):
print("我喜欢做饭。")
# 实例方法
def hobby(self):
print("我不喜欢玩电子游戏。")
# 多继承,同时继承 Father 父类和 Mother 父类
class Son(Father, Mother):
pass
# 创建实例化对象
son = Son()
# 调用父类中的hobby方法
son.hobby()
# 调用父类中的cook方法
son.cook()
定义的Father()父类和Mother()父类中都有cook()方法和hobby()方法,在创建子类Son()的时候,括号里从左向右依次继承了Father()父类和Mother()父类。
多重继承可以继承多个父类,即可以继承所有父类的属性和方法,在定义子类的时候,将父类的名字写入到括号中即可。
通过上面这段代码我们可以得出,当继承多个父类时,如果父类中有相同的方法,那么子类会从左到右优先使用最先被继承的方法。
多个父类中,不重名的属性和方法不会有任何影响。
当子类继承父类后,如果子类不想使用父类的方法,可以通过重写来覆盖父类的方法。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
# Father父类
class Father(object):
# 实例方法
def hobby(self):
print("我喜欢玩电子游戏。")
# 实例方法
def cook(self):
print("我不喜欢做饭。")
# Mother父类
class Mother(object):
# 实例方法
def cook(self):
print("我喜欢做饭。")
# 实例方法
def hobby(self):
print("我不喜欢玩电子游戏。")
# 多继承,同时继承 Father 父类和 Mother 父类
class Son(Father, Mother):
def cook(self):
print('我最不喜欢做饭了。')
def hobby(self):
print('我最不喜欢电子游戏了。')
# 创建实例化对象
son = Son()
son.hobby()
son.cook()
多重继承不仅仅只可以继承两个父类,可以继承很多很多,只要都写到子类的括号中去就可以了,继承的优先级是从左往右,也就是所谓的就近原则。
多层继承多层继承:继承也可以产生在两个及两个以上的层级之间,也就是父类-子类-孙子类(打比喻),或者是父类的父类,父类的爷爷类等等。
多重继承是继承的宽度扩展,而多层继承是继承的深度扩展。
类B继承了类A,类C继承了类B,类D继承了类C,这种继承方式就是多层继承。
class A(object):
def play(self):
print('A is playing!')
class B(A): # 继承类A
def play(self): # 自动覆盖父类的此方法
print('B is playing')
class C(B): # 继承B
def play(self):
print('C is playing')
class D(C): # 继承C
pass
d = D()
d.play()
当有多层继承时,如果父类中有相同方法,那么子类会优先调用最先被继承的方法。我们修改一下代码确认一下是不是。
class A(object):
def play(self):
print('A is playing!')
class B(A): # 继承类A
def play(self): # 自动覆盖父类的此方法
print('B is playing')
class C(B): # 继承base
def playing(self):
print('C is playing')
class D(C): # 继承A,B
pass
d = D()
d.play()
类B继承了类A,类C继承了类B,类D继承了类C,那么类D可以使用类C中的方法和属性,也可以使用类B中的方法和属性,也可以使用类A中的方法和属性。
当类D的实例化对象调用属性和方法时,会优先在自己的内部查找是否有该属性和方法,如果没有找到,那么它会自动向它的父类中寻找,如果没有,就会继续向父类的父类中查找,一直查找到object类中。如果到最后都没有找到,就说明对象没有该属性或方法,程序报错,如果找到就会返回找到的属性和方法,不再继续往上查找。
这就是多层继承的优先级,类属性和类方法的优先级一样。
菱形继承当一个子类继承多个父类时,多个父类最终继承了同一个父类,这样的继承方式称为菱形继承,也叫做钻石继承。
class A(object):
def play(self):
print('A is playing!')
class B(A): # 继承类A
def play(self):
print('B is playing')
class C(A): # 继承类A
def playing(self):
print('C is playing')
class D(A): # 继承类A
def __init__(self):
self.name = '大白'
class E(B,C,D): #继承类B、C、D
pass
e = E()
e.play()
print(e.name)
在菱形继承中,也是根据就近原则进行寻找父类的属性和方法,一直向上寻找至object类。
子类重写父类的同名属性和方法有时候父类的方法不能满足子类的需要,我们可以对子类的方法重写,具体的实现方式就相当于在子类中定义了一个和父类同名的方法并实现。重写之后只会对子类的方法调用,而不会调用父类的方法,遵守就近原则。
重写,有时又称覆盖,是一个意思,指的是对类中已有方法的内部实现进行修改。
如果我们在子类中重写了从父类继承来的类方法,那么当在类的外部通过子类对象调用该方法时,Python 总是会执行子类中重写的方法。
举个例子,鸟都是有翅膀的,都会飞,我们可以定义一个和鸟相关的类:
class Bird():
#鸟有翅膀
def isWing(self):
print("鸟有翅膀")
#鸟会飞
def fly(self):
print("鸟会飞")
但是,对于鸵鸟来说,它虽然也是鸟类,也有翅膀,但是它只会跑并不会飞,针对这种情况,我们再定义一个鸵鸟类继承Bird()类。
class Bird():
#鸟有翅膀
def isWing(self):
print("鸟有翅膀")
#鸟会飞
def fly(self):
print("鸟会飞")
class Ostrich(Bird):
# 重写Bird类的fly()方法
def fly(self):
print("鸵鸟不会飞")
可以看到,因为 Ostrich 继承自 Bird,因此 Ostrich 类拥有 Bird 类的 isWing() 和 fly() 方法。其中,isWing() 方法同样适合 Ostrich,但 fly() 明显不适合,因此我们在 Ostrich 类中对 fly() 方法进行重写。
事实上,如果我们在子类中重写了从父类继承来的类方法,那么当在类的外部通过子类对象调用该方法时,Python 总是会执行子类中重写的方法。
这就产生一个新的问题,即如果想调用父类中被重写的这个方法,该怎么办呢?
Python 中的类可以看做是一个独立空间,而类方法其实就是出于该空间中的一个函数。而如果想要全局空间中,调用类空间中的函数,只需要在调用该函数是备注类名即可。
class Bird():
#鸟有翅膀
def isWing(self):
print("鸟有翅膀")
#鸟会飞
def fly(self):
print("鸟会飞")
class Ostrich(Bird):
# 重写Bird类的fly()方法
def fly(self):
print("鸵鸟不会飞")
# 创建Ostrich对象
ostrich = Ostrich()
#调用 Bird 类中的 fly() 方法
Bird.fly(ostrich)
此程序中,需要大家注意的一点是,使用类名调用其类方法,Python 不会为该方法的第一个 self 参数自定绑定值,因此采用这种调用方法,需要手动为 self 参数赋值。
通过类的继承学会了复用代码,减少代码冗余,提高编程效率。



