当前位置:首页 > 物联网 > Freak嵌入式

摘要:

本文主要介绍了类的继承的基本概念和里氏替换原则,以模拟传感器数据串口输出-上位机串口接收为例,对工作流程、工作模式和基本概念进行讲解,同时创建了主机类和传感器类,定义了属性和抽象方法。

文档和代码获取:

本文档主要介绍如何使用 Python 进行面向对象编程,需要读者对 Python 语法和单片机开发具有基本了解。相比其他讲解 Python 面向对象编程的博客或书籍而言,本文档更加详细、侧重于嵌入式上位机应用,以上位机和下位机的常见串口数据收发、数据处理、动态图绘制等为应用实例,同时使用 Sourcetrail代码软件对代码进行可视化阅读便于读者理解。

正文:


类的继承的基本概念


刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则

里氏替换原则


所谓里氏替换原则通俗来说就是子类可以扩展父类的功能,但不能改变父类原有的功能。有以下几个引申含义:

  • (1)子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

  • (2)子类中可以增加自己特有的方法。

  • (3)当子类的方法重载父类的方法时,方法的前置条件(方法的输入,入参)要比父类的入参更宽松。

  • (4)当子类的方法实现父类的方法时(重写,重载,实现抽象方法),方法的后置条件(输出、返回值)要比父类更严格或相等。

应用实例的抽象实现


这里,我们以模拟传感器数据串口输出-上位机串口接收为例,进行讲解:在现在的开发中,许多传感器都内部集成了ADC和MCU芯片,可以将采集到的传感器测量的物理量对应的电压量进行转换并进行滤波等处理后,将更精确的传感器数据通过串口进行输出。这里,传感器和上位机间关于串口通信的部分就可以抽象为串口类,二者都具有串口的收发功能。但传感器类和上位机接收类与串口类有有所不同:

SensorClass类

类内成员
作用
属性attribute:sensorvalue
传感器采集的数据
属性attribute:sensorid
传感器ID号,用于识别是哪个传感器
属性attribute:sensorstate
传感器工作状态,包括: ①被动响应模式(默认): 主机发送查询数据指令后,传感器发送数据; ②轮询工作模式: 间隔100ms发送一个传感器数据,轮询工作;
方法 operation:SendSensorID
发送传感器ID号
方法 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 接收传感器ID号
方法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( )方法。关于新建的两个类方法的具体实现可以看下一节内容。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭