一、继承与多态
面向对象编程(OOP)的三大特征,即:封装、继承、多态,下面主要讲解继承与多态
1、object类
在介绍继承与多态之前,先介绍下Python中的
object
类。在Python中object
类是所有类的基类,所有的Python类都隐式地继承自object
,如下:
class A:
pass
print(A.__base__) # 输出结果:<class 'object'>
官方参考文档:https://docs.python.org/zh-cn/3.9/library/functions.html?highlight=object#object
根据官方说法,调用o = object()会返回一个没有特征的新对象。object是所有类的基类。它具有所有 Python 类实例的通用方法,这个函数不接受任何实参,示例:
o = object()
print(o) # 输出结果:<object object at 0x00EBF0F0>
没有特征的新对象是什么意思呢?它是指这个对象o没有任何自定义的属性或者方法,它只有一些object类提供的特殊方法。而且,不能给这个对象添加任何属性或者方法。官方文档中也有说明:由于object没有__dict__,因此无法将任意属性赋给 object 的实例。
内置的
dir
方法列出对象的所有属性及方法,调用dir
方法查看object
类提供的全部属性,如下:
print(dir(object))
# 输出结果:['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
2、继承
通过继承可以继承父类的全部方法和属性,Python里面的继承有单继承与多继承,格式如下:
# 派生类
class Derive(父类1, 父类2, ..., 父类n):
pass
2.1、单继承
单继承,示例:
class Animal:
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
如果子类与父类的方法名、属性名相同,调用子类时默认使用子类的方法、属性
class Person:
name = "person"
def say(self):
print(f"my name is {self.name}")
class Student(Person):
name = "Student"
def say(self):
print(f"my name is {self.name}");
zhangsan = Student()
zhangsan.say() # 输出结果:my name is Student
2.2、多继承
多继承,示例:
class P1():
def talk(self):
print("我是p1")
class P2():
def talk(self):
print("我是p2")
class Person(P1, P2): # P1排在第一位,调用P1的talk()
pass
p = Person()
p.talk() # 输出结果:我是p1
注意:如果派生类有多个父类,多个父类都有相同的方法,方法调用时是按照继承参数的顺序来的,谁排在第一个就调用谁的方法
3、多态
多态:即同一个接口根据访问的对象的不同而有不同的行为。继承时如果派生类的方法与基类同名,派生类的方法会覆盖基类的方法,由于这个特性才实现的多态,示例:
class Animal:
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
def run_twice(animal):
animal.run()
my_animal = Animal()
my_dog = Dog()
my_cat = Cat()
run_twice(my_animal) # 输出结果:Animal is running...
run_twice(my_dog) # 输出结果:Dog is running...
run_twice(my_cat) # 输出结果:Cat is running...
从输出结果可以看出来,
run_twice
函数里面调用的animal.run()
函数,根据输入对象的不同(实际分别传入的是my_animal, my_dog, my_cat
对象),分别有不同的输出结果。
多态存在的三个必要条件:
- 有继承
- 有重写,即:派生类重写基类的同名方法
- 父类引用指向子类对象
4、查看继承关系
通过类属性
__mro__
可以查看类继承关系,如下:
class Person:
name = "person"
def say(self):
print(f"my name is {self.name}")
class Student(Person):
name = "Student"
def say(self):
print(f"my name is {self.name}")
print(Student.__mro__) # 输出结果:(<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>)
5、super方法
5.1、方法定义
通过
super()
方法可以调用父类或超类中的方法,方法声明如下:
super(type[, object-or-type])
参数解析:
- type:类,可选参数
- object-or-type:对象或类,一般是 self,可选参数
help 帮助信息:
>>> help(super)
Help on class super in module builtins:
class super(object)
| super() -> same as super(__class__, <first argument>)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)
| super(type, type2) -> bound super object; requires issubclass(type2, type)
| Typical use to call a cooperative superclass method:
| class C(B):
| def meth(self, arg):
| super().meth(arg)
| This works for class methods too:
| class C(B):
| @classmethod
| def cmeth(cls, arg):
| super().cmeth(arg)
... ...
从官方的帮助文档可以看出
- super 是一个继承自 object 的类,调用 super() 函数其实就是 super 类的实例化
- 根据官方文档的解释 super() 函数返回的对象-super object,就是一个代理对象
- super() 有四种参数的组合形式
- super() 适用于类的静态方法
5.2、使用场景
使用
super()
方法主要解决下面这些问题
- 在子类中调用父类的方法
- 多用于多继承问题中,解决查找顺序(MRO)、重复调用(钻石继承)等种种问题
5.2.1、单继承场景
在
help()
的帮助信息中,也说明了类中使用super()
不带参数的形式等同于super(__class__, <first argument>)
这种形式,这也是 Python 2.x 和 Python 3.x 关于 super() 的区别,示例:子类调用父类方法
class A:
def funxx(self):
print("执行 A 中的 funxx 方法 ... ...")
class B(A):
def funxx(self):
# super(B,self) 首先找到 B 的父类(就是类 A),然后把类 B 的对象转换为类 A 的对象
super().funxx()
print("执行 B 中的 funxx 方法 ... ...")
b = B()
b.funxx()
输出结果:
执行 A 中的 funxx 方法 ... ...
执行 B 中的 funxx 方法 ... ...
5.2.2、多继承场景
https://blog.csdn.net/qq_41961087/article/details/117674563?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_utm_term~default-1-117674563-blog-82912808.pc_relevant_3mothn_strategy_recovery&spm=1001.2101.3001.4242.2&utm_relevant_index=4