摘要:
本文主要介绍了类的继承的基本概念和里氏替换原则,以模拟传感器数据**串口输出-上位机**串口接收为例,对工作流程、工作模式和基本概念进行讲解,同时创建了主机类和传感器类,定义了属性和抽象方法。
原文链接:
FreakStudio的博客
往期推荐:
学嵌入式的你,还不会面向对象??!
全网最适合入门的面向对象编程教程:00 面向对象设计方法导论
全网最适合入门的面向对象编程教程:01 面向对象编程的基本概念
全网最适合入门的面向对象编程教程:02 类和对象的Python实现-使用Python创建类
全网最适合入门的面向对象编程教程:03 类和对象的Python实现-为自定义类添加属性
全网最适合入门的面向对象编程教程:04 类和对象的Python实现-为自定义类添加方法
全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签
全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装
全网最适合入门的面向对象编程教程:07 类和对象的Python实现-类型注解
全网最适合入门的面向对象编程教程:08 类和对象的Python实现-@property装饰器
全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系
更多精彩内容可看:
给你的Python加加速:一文速通Python并行计算
一文搞懂CM3单片机调试原理
肝了半个月,嵌入式技术栈大汇总出炉
电子计算机类比赛的“武林秘籍”
文档和代码获取:
可访问如下链接进行对文档下载:

本文档主要介绍如何使用 Python 进行**面向对象编程,需要读者对 Python 语法和单片机开发具有基本了解。相比其他讲解 Python 面向对象编程的博客或书籍而言,本文档更加详细、侧重于嵌入式上位机应用,以上位机和下位机的常见串口**数据收发、数据处理、动态图绘制等为应用实例,同时使用 Sourcetrail代码软件对代码进行可视化阅读便于读者理解。
相关示例代码获取链接如下:
正文:
类的继承的基本概念
刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接**继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类**或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则。
里氏替换原则
所谓里氏替换原则通俗来说就是子类可以扩展父类的功能,但不能改变父类原有的功能。有以下几个引申含义:
- (1)子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- (2)子类中可以增加自己特有的方法。
- (3)当子类的方法重载父类的方法时,方法的前置条件(方法的输入,入参)要比父类的入参更宽松。
- (4)当子类的方法实现父类的方法时(重写,重载,实现抽象方法),方法的后置条件(输出、返回值)要比父类更严格或相等。
更多相关信息可以看:
应用实例的抽象实现
这里,我们以模拟传感器数据**串口输出-上位机**串口接收为例,进行讲解:在现在的开发中,许多传感器都内部集成了ADC和MCU芯片,可以将采集到的传感器测量的物理量对应的电压量进行转换并进行滤波等处理后,将更精确的传感器数据通过串口进行输出。这里,传感器和上位机间关于串口通信的部分就可以抽象为串口类,二者都具有串口的收发功能。但传感器类和上位机接收类与串口类有有所不同:

| SensorClass类 |
|---|
| 类内成员 |
| 属性attribute:sensorvalue |
| 属性attribute:sensorid |
| 属性attribute:sensorstate |
| 方法operation:SendSensorID |
| 方法operation:SendSensorValue |
| 方法operation:RecvMasterCMD |
| 方法operation:StartSensor |
| 方法operation:StopSensor |
| 方法operation:InitSensor |
工作流程简单来说就是传感器初始化后开始工作,不断采集数据,接收到主机发送的查询数据命令后,发送传感器数据;对应的接收到主机发送的不同指令后执行对应操作。
我们来看如下示例代码,SensorClass类具体方法实现先省略:
class SensorClass(SerialClass): # 类的初始化 def __init__(self,id,state): # 调用父类的初始化方法,super() 函数将父类和子类连接 super().__init__() self.sensorvalue = 0 self.sensorid = id self.sensorstate = state
# 传感器上电初始化 def InitSensor(self): pass # 开启传感器 def StartSensor(self): pass # 停止传感器 def StopSensor(self): pass # 发送传感器ID号 def SendSensorID(self): pass # 发送传感器数据 def SendSensorValue(self): pass
这里,继承语法为:
class 派生类名(基类名) ...
在SensorClass类的初始化方法中,我们首先调用如下语句:
super().__init__()
这里,super()用来调用父类(基类)的方法,init()是类的构造方法, super().init() 就是调用父类的init方法, 同样可以使用super()去调用父类的其他方法。这里,你可能会问,我们为何不用如下语句来初始化父类SerialClass呢?
SerialClass.__init__()
这看起来多清晰、多方便。实际上,super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。这里,不对钻石继承问题做过多叙述,只需知道子类初始化时必须调用super() 方法。
有关钻石继承问题可以查看如下链接:
对于主机的MasterClass类来说,除了继承于父类SerialClass的属性和方法外,还包括其他属性和方法,如下讲解:
| MasterClass类 |
|---|
| 类内成员 |
| 属性attribute:valuequeue |
| 属性attribute:masterstate |
| 方法operation:RecvSensorID |
| 方法operation:RecvSensorValue |
| 方法operation:SendSensorCMD |
| 方法operation:StartMaster |
| 方法operation:StopMaster |
| 方法operation:RetMasterState |
工作流程简单来说就是主机在空闲时,发送数据查询指令,接收传感器数据;在忙碌时,不接收数据。
我们来看如下示例代码,MasterClass类具体方法实现先省略:
class MasterClass(SerialClass): # 类的初始化 def __init__(self,state): # 调用父类的初始化方法,super() 函数将父类和子类连接 super().__init__() self.valuequeue = queue.Queue(5) self.masterstatue = state # 开启主机 def StartMaster(self): pass # 停止主机 def StopMaster(self): pass # 接收传感器ID号 def RecvSensorID(self): pass # 接收传感器数据 def RecvSensorValue(self): pass # 主机发送命令 def SendSensorCMD(self): pass # 主机返回工作状态 def RetMasterStatue(self): pass
这里,我们使用队列创建了一个传感器数据缓冲区:
self.valuequeue = queue.Queue(5)
我们使用import queue语句导入队列库,队列类似于一条管道,元素先进先出,元素进入使用put()方法,元素获取使用get( )方法。关于新建的两个类方法的具体实现可以看下一节内容。

评论区
登录后即可参与讨论
立即登录