204-面向对象三大特性-2.继承

三种特性:

2、python中的继承

什么是继承

我们接下来来聊聊Python代码中的“继承”:类是用来描述现实世界中同一组事务的共有特性的抽象模型,但是类也有上下级和范围之分,比如:生物 => 动物

继承的基本语法

假设A类要继承B类中的所有属性和方法(私有属性和私有方法除外

class B(object):
    pass

class A(B):
    pass

a = A()
a.B中的所有公共属性
a.B中的所有公共方法

相关名词解释

继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承!

派生:从一个已有的类产生一个新的类,称为派生!

很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的!

父类:也叫作基类,就是指已有被继承的类!

子类:也叫作派生类或扩展类

扩展:在子类中增加一些自己特有的特性,就叫作扩展,没有扩展,继承也就没有意义了

单继承:一个类只能继承自一个其他的类,不能继承多个类,单继承也是大多数面向对象语言的特性!

多继承:一个类同时继承了多个父类, (C++、Python等语言都支持多继承)

单继承

一个类只能继承自一个其他的类,不能同时继承多个类。这个类会有具有父类的属性和方法。

基本语法:

# 1、定义一个共性类(父类)
class Person(object):
    pass
# 2、定义一个个性类(子类)
class Student(Person):
    pass

栗子:

# 定义一个公共类
class Person(object):
	def speak(self):
		print('i can speak')

# 定义学生
class Student(Person):
	pass
# 定义ikun
class Ikun(Person):
	pass

ZhangSan = Student()
ZhangSan.speak()

单继承特性(多层继承):传递性

在Python继承中,如A类继承了B类,B类又继承了C类。则根据继承的传递性,则A类也会自动继承C类中所有属性和方法(公共)

class C(object):
    def func(self):
        print('我是C类中的相关方法func')

class B(C):
    pass

class A(B):
    pass

a = A()
a.func()

多继承

Python语言是少数支持多继承的一门编程语言,所谓的多继承就是允许一个类同时继承自多个类的特性。

基本语法:

class B(object):
    pass

class C(object):
    pass

class A(B, C):
    pass

a = A()
a.B中的所有属性和方法
a.C中的所有属性和方法

栗子:

# 定义黑子
class HeiZi(object):
	def speak(self):
		print('我们家哥哥')
# 定义ikun
class Ikun(Person):
	def speak(self):
		print('你干嘛~嗨嗨哎呦')

class ZhenAiFen(Ikun, Heizi):
	pass

ZhangSan = ZhenAiFen()
ZhangSan.speak()

注意:虽然多继承允许我们同时继承自多个类,但是实际开发中,应尽量避免使用多继承,因为如果两个类中出现了相同的属性和方法就会产生命名冲突。

上面代码中,继承的两个类中都有speak方法,但是子类只调用了Ikun的的speak方法,没有调用HeiZi的speak方法,这里就涉及到方法解析顺序,一般的优先级是:本类中定义的方法>排在前面的父类中的方法

super()调用父类属性和方法

super():调用父类属性或方法,完整写法:super(当前类名, self).属性或方法(),在Python3以后版本中,调用父类的属性和方法我们只需要使用super().属性或super().方法名()就可以完成调用了。

案例:Car汽车类、GasolineCar汽油车、ElectricCar电动车

class Car(object):
    def __init__(self, brand, model, color):
        self.brand = brand
        self.model = model
        self.color = color

    def run(self):
        print('i can run')


class GasolineCar(Car):
    def run(self):
        print('i can run with gasoline')


class ElectricCar(Car):
    def __init__(self, brand, model, color, battery):
        super().__init__(brand, model, color)
        # 电池属性
        self.battery = battery

    def run(self):
        print(f'i can run with electric,i have a {self.battery} + "kwh battery"')


bwm = GasolineCar('宝马', 'X5', '白色')
bwm.run()

tesla = ElectricCar('特斯拉', 'Model S', '红色', 70)
tesla.run()


### MRO属性或MRO方法:方法解析顺序

MRO(Method Resolution Order):方法解析顺序,我们可以通过`类名.__mro__`或`类名.mro()`获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找到。

```python
class Car(object):
    def __init__(self, brand, model, color):
        self.brand = brand
        self.model = model
        self.color = color

    def run(self):
        print('i can run')


class GasolineCar(Car):
    def run(self):
        print('i can run with gasoline')


class ElectricCar(Car):
    def __init__(self, brand, model, color, battery):
        super().__init__(brand, model, color)
        # 电池属性
        self.battery = battery

    def run(self):
        print(f'i can run with electric,i has a {self.battery} + "kwh battery"')

print(ElectricCar.__mro__)
print(ElectricCar.mro())

代码的输出是:

D:\Python\Python37\python.exe D:/PycharmProjects/pythonProject/07-Python中的MR0方法解析顺序.py
(<class '-_main-_.ElectricCar'>, <class '-_main__.Car'>, <class 'object'>)
[<class '--main_-.Electriccar'>, <class '--main--.Car'>, <class 'object'>]]

说明:有MRO方法解析顺序可知,在类的继承中,当某个类创建了一个对象时,调用属性或方法,首先在自身类中去寻找,如找到,则直接使用,停止后续的查找。如果未找到,继续向上一级继承的类中去寻找,如找到,则直接使用,没有找到则继续向上寻找...直到object类,这就是Python类继承中,其方法解析顺序。

综上:object类还是所有类的基类(因为这个查找关系到object才终止)

常见问题

问题1:在定义类时,其没有遵循类的命名规则

答:在Python中,类理论上是区分大小写的(在Python中类可以全部大写也可以全部小写)。但是要遵循一定的命名规范:首字母必须是字母或下划线,其中可以包含字母、数字和下划线,而且要求其命名方式采用大驼峰。

电动汽车:EletricCar

父类:Father

子类:Son

问题2:父类一定要继承object么?Car(object)

答:在Python面向对象代码中,建议在编写父类时,让其自动继承object类。但是其实不写也可以,因为默认情况下,Python中的所有类都继承自object。

问题3:打印属性和方法时,不是只能使用print()函数输出,也可以使用return。

在实际打印对象信息,还建议使用__str__

class Person():
    def __init__(self, name):
        self.name = name

    def speak(self):
        print('i can speak')

# 创建对象,打印属性和方法
p = Person('Tom')
print(p.name)
p.speak()

问题4:在定义魔术方法__init__而非__int__