继承
面向对象三大特性:
1、封装:根据职责将属性和方法封装到一个抽象的类中。
2、继承:实现代码的重用,相同的代码不需要重复编写。
3、多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度。
单继承
继承的概念:子类拥有父类的所有方法和属性。
继承的语法:
class 类名(父类名):
pass
继承也叫做派生,父类被称为基类,子类被称为派生类。
继承的传递性:
子类能继承父类的方法,也能继承爷类的方法,但是不能继承叔类的方法。
方法的重写
当父类的方法不能满足子类的需求时,可以对方法进行重写,在子类中定义一个和父类同名的方法并且实现。即可覆盖父类方法。如果父类原本封装的方实现是子类的方法的一部分,那么久可以对父类的方法进行扩展。在子类中重写父类方法,并且在需要位置使用super().父类方法
来调用父类方法的执行。
在python中super
是一个特殊的类,super()
就是使用super
类创建出来的对象。
class Dog:
def bark(self):
print("汪汪叫")
class XiaoTianQuan(Dog):
def bark(self):
print("像神一样叫唤")
super().bark()
xtq = XiaoTianQuan()
还有一种不推荐的方式调用父类方法:
父类名.方法(self)
Dog.bark(self)
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法。
子类对象可以通过父类的公有方法简介访问到私有属性或私有方法。
多继承
子类可以拥有多个父类。
语法格式:
class 子类名(父类1, 父类2,...)
pass
在使用多继承时,应该避免不同父类之间存在同名的属性或者方法。
MRO:methond resolution order. 主要用于在多继承时判断 属性或者方法 的调用路径。有一个内置属性__mro__
可以查看方法搜索顺序。
print(类名.__mro__)
一般是就近原则。
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
object
是python为所有对象提供的基类,提供了一些内置的属性和方法,可以使用dir函数查看。
在python3中定义类时,如果没有指定父类,会默认使用object
作为该类的基类,在python2中则不同。使用object
作为基类的是新式类,不以object作为基类的是经典类。推荐在定义类的时候,如果没有父类,统一继承自object
类。
class 类名(object):
pass
多态
多态:不同的子类对象调用相同的父类方法,产生不同的执行结果。
多态以继承和重写父类方法为前提,可以增加代码的灵活度。
比如做如下封装:
class Dog(object):
def __init__(self, name):
self.name = name
def play_game(self):
print("%s蹦蹦跳跳的玩耍" % self.name)
class XiaoTianQuan(Dog):
def play_game(self):
print("%s飞到填上去玩耍" % self.name)
class Person(object):
def __init__(self, name):
self.name = name
def game_with_dog(self, dog):
print("%s和%s一起玩耍" % (self.name, dog.name))
dog.play_game()
通过不同的调用,可以实现人和普通的狗或者哮天犬玩耍。
wangcai = Dog("旺财")
xiaoming = Person("小明")
xiaoming.game_with_dog(wangcai)
结果如下:
小明和旺财一起玩耍
旺财蹦蹦跳跳的玩耍
而如果使用子类生成神犬:
xtq = XiaoTianQuan("哮天犬")
erlangshen = Person("二郎神")
erlangshen.game_with_dog(xtq)
则结果如下:
二郎神和哮天犬一起玩耍
哮天犬飞到填上去玩耍
因为父类和子类虽然方法名相同,但是方法的实现内容却不同,所以可以通过父类或者子类生成不同的对象,使用同样的方法,实现不同的功能。
在Person
类中只需要让狗对象调用game
方法,而不关心具体是什么狗,在程序执行时,传入不同的狗对象实参,就会产生不同的执行结果。