全网最适合入门的面向对象编程教程:11 类和对象的 Python 实现-子类调用父类方法-模拟串口传感器和主机

摘要:

本节课,我们主要讲解了在 Python 类的继承中子类如何进行初始化、调用父类的属性和方法,同时讲解了模拟串口传感器和主机类的具体实现,并使用 xcom 串口助手与两个类进行串口通信使用。

原文链接:

FreakStudio 的博客

往期推荐:

学嵌入式的你,还不会面向对象??!

全网最适合入门的面向对象编程教程:00 面向对象设计方法导论

全网最适合入门的面向对象编程教程:01 面向对象编程的基本概念

全网最适合入门的面向对象编程教程:02 类和对象的 Python 实现-使用 Python 创建类

全网最适合入门的面向对象编程教程:03 类和对象的 Python 实现-为自定义类添加属性

全网最适合入门的面向对象编程教程:04 类和对象的Python实现-为自定义类添加方法

全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签

全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装

全网最适合入门的面向对象编程教程:07 类和对象的Python实现-类型注解

全网最适合入门的面向对象编程教程:08 类和对象的Python实现-@property装饰器

全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系

全网最适合入门的面向对象编程教程:10 类和对象的Python实现-类的继承和里氏替换原则

更多精彩内容可看:

给你的 Python 加加速:一文速通 Python 并行计算

一文搞懂 CM3 单片机调试原理

肝了半个月,嵌入式技术栈大汇总出炉

电子计算机类比赛的“武林秘籍”

一个MicroPython的开源项目集锦:awesome-micropython,包含各个方面的Micropython工具库

文档和代码获取:

可访问如下链接进行对文档下载:

https://github.com/leezisheng/Doc

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

相关示例代码获取链接如下:https://github.com/leezisheng/Python-OOP-Demo

正文

模拟串口传感器和主机类的具体实现:

接下来看一下我们新建的两个类方法的具体实现,可以明确的是,SensorClass 和 MasterClass 都需要调用 SerialClass 类中有关串口收发的方法,也就是子类调用父类的方法,子类调用父类的方法有三种方式:

  • 父类名.方法名(self):此时需要加上父类的类名前缀,且需要带上 self 参数变量。该方法单继承或多继承均适用。
  • super(子类名,self).父类方法名()/super().父类方法名:使用 super()函数,但如果涉及多继承,该函数只能调用第一个直接父类的构造方法。

SensorClass 类的实现

(1)首先,我们将 SensorClass 中工作状态的对应字符表示和命令对应字符表示设置为类属性,什么意思?我们来看如下代码:

  1. class SensorClass(SerialClass):
  2. _# 类变量:_
  3. _# RESPOND_MODE-响应模式-0_
  4. _# LOOP_MODE -循环模式-1_
  5. RESPOND_MODE,LOOP_MODE = (0,1)
  6. _# 类变量:_
  7. _# START_CMD - 开启命令 -0_
  8. _# STOP_CMD - 关闭命令 -1_
  9. _# SENDID_CMD - 发送ID命令 -2_
  10. _# SENDVALUE_CMD - 发送数据命令 -3_
  11. START_CMD,STOP_CMD,SENDID_CMD,SENDVALUE_CMD = (0,1,2,3)

类属性归类所有,与前面讲的实例属性不同,类属性就相当与全局变量,是实例对象共有的属性,类属性影响类的所有对象;而实例对象的属性为实例对象自己私有,实例属性只影响当前对象。类属性常用于存储常量、定义默认值或构造一个所有对象都能访问的缓存。

这里,我们定义了两种类属性:

  1. RESPOND_MODE,LOOP_MODE = (0,1)

用于表示 SensorClass 不同工作模式。

  1. START_CMD,STOP_CMD,SENDID_CMD,SENDVALUE_CMD = (0,1,2,3)

用于表示不同命令。

(2)在初始化中,我们调用父类初始化方法进行,同时可以在初始化 SensorClass 类时指定 id、state、port 三个参数。

  1. def __init__(self,id:int = 0,state:int = RESPOND_MODE,port:str = "COM11"):
  2. # 调用父类的初始化方法,super() 函数将父类和子类连接
  3. super().__init__(port)
  4. self.sensorvalue = 0
  5. self.sensorid = id
  6. self.sensorstate = state

这里,实际上 SensorClass 类初始化的参数应该包括其他有关串口配置相关的参数(波特率、校验位、数据位、停止位),由于串口通信双方这些参数配置相同,这里为了方便讲解故而简化。

(3)模拟传感器上电初始化,在实际传感器上电过程中会完成校准、自检等操作,这里我们简单输出传感器状态和 ID 号:

  1. _# 传感器上电初始化_
  2. def InitSensor(self):
  3. _# 传感器上电初始化工作_
  4. _# 同时输出ID号以及状态_
  5. print("Sensor %d Init complete : %d"%(self.sensorid,self.sensorstate))

(4)在传感器使能和关闭方法中,我们开启或关闭串口并打印相关信息:

  1. _# 开启传感器_
  2. def StartSensor(self):
  3. super().OpenSerial()
  4. print("Sensor %d start serial %s "%(self.sensorid,self.dev.port))
  5. _# 停止传感器_
  6. def StopSensor(self):
  7. super().CloseSerial()
  8. print("Sensor %d close serial %s " % (self.sensorid, self.dev.port))

(5)在传感器发送 ID 号的方法中,我们调用了父类的 WriteSerial 方法:

  1. _# 发送传感器ID号_
  2. def SendSensorID(self):
  3. super().WriteSerial(str(self.sensorid))
  4. print("Sensor %d send id "%self.sensorid)

(6)在传感器发送数据方法中,我们使用如下语句生成一个随机数:

  1. _# 生成[1, 10]内的随机整数_
  2. data = random.randint(1, 10)

注意,此方法需要使用 import random 语句导入 random 库。

同时调用父类的 WriteSerial 方法实现传感器数据的发送:

  1. _# 发送传感器数据_
  2. def SendSensorValue(self):
  3. _# 生成[1, 10]内的随机整数_
  4. data = random.randint(1, 10)
  5. super().WriteSerial(str(data))
  6. print("Sensor %d send data %d" % (self.sensorid,data))

(7)在传感器接收命令方法中,我们调用了父类的 ReadSerial 接收命令:

  1. _# 接收主机指令_
  2. def RecvMasterCMD(self):
  3. cmd = super().ReadSerial()
  4. print("Sensor %d recv cmd %d " % (self.sensorid,cmd))
  5. return cmd

MasterClass 类的实现

(1)首先定义关于工作模式和命令的类属性:

  1. class MasterClass(SerialClass):
  2. _# 类变量:_
  3. _# BUSY_STATE -忙碌状态-0_
  4. _# IDLE_STATE -空闲状态-1_
  5. BUSY_STATE, IDLE_STATE = (0, 1)
  6. _# 类变量:_
  7. _# START_CMD - 开启命令 -0_
  8. _# STOP_CMD - 关闭命令 -1_
  9. _# SENDID_CMD - 发送ID命令 -2_
  10. _# SENDVALUE_CMD - 发送数据命令 -3_
  11. START_CMD, STOP_CMD, SENDID_CMD, SENDVALUE_CMD = (0, 1, 2, 3)

(2)在初始化函数中调用父类的初始化方法,并定义 valuequeue 和__masterstatue 属性:

  1. _# 类的初始化_
  2. def __init__(self,state:int = IDLE_STATE,port:str = "COM17"):
  3. _# 调用父类的初始化方法,super() 函数将父类和子类连接_
  4. super().__init__(port)
  5. self.valuequeue = queue.Queue(10)
  6. self.__masterstatue = state

(3)在 StartMaster 方法中我们打开串口并使用 logging.info()方法输出调试信息:

  1. _# 开启主机_
  2. def StartMaster(self):
  3. super().OpenSerial()
  4. logging.info("START MASTER :"+self.dev.port)

这里,需要导入 logging 库并设置日志输出级别:

  1. import logging
  2. _# 设置日志输出级别_
  3. logging.basicConfig(level=logging.DEBUG)

(4)关闭主机方法中调用父类的 CloseSerial 方法:

  1. _# 停止主机_
  2. def StopMaster(self):
  3. super().CloseSerial()
  4. logging.info("CLOSE MASTER :" + self.dev.port)

(5)如下调用父类的 ReadSerial 方法接收 ID 号和数据:

  1. _# 接收传感器ID号_
  2. def RecvSensorID(self):
  3. sensorid = super().ReadSerial()
  4. logging.info("MASTER RECIEVE ID : " + str(sensorid))
  5. return sensorid
  6. _# 接收传感器数据_
  7. def RecvSensorValue(self):
  8. data = super().ReadSerial()
  9. logging.info("MASTER RECIEVE DATA : " + str(data))
  10. self.valuequeue.put(data)
  11. return data

(6)调用父类的 WriteSerial 方法发送指令:

  1. _# 主机发送命令_
  2. def SendSensorCMD(self,cmd):
  3. super().WriteSerial(str(cmd))
  4. logging.info("MASTER SEND CMD : " + str(cmd))

(7)如下 RetMasterStatue 方法获取主机状态:

  1. _# 主机返回工作状态-_
  2. def RetMasterStatue(self):
  3. return self.__masterstatue

完整代码

以下为两个类的完整代码:

  1. class SensorClass(SerialClass):
  2. _# 类变量:_
  3. _# RESPOND_MODE -响应模式-0_
  4. _# LOOP_MODE -循环模式-1_
  5. RESPOND_MODE,LOOP_MODE = (0,1)
  6. _# 类变量:_
  7. _# START_CMD - 开启命令 -0_
  8. _# STOP_CMD - 关闭命令 -1_
  9. _# SENDID_CMD - 发送ID命令 -2_
  10. _# SENDVALUE_CMD - 发送数据命令 -3_
  11. START_CMD,STOP_CMD,SENDID_CMD,SENDVALUE_CMD = (0,1,2,3)
  12. _# 类的初始化_
  13. def __init__(self,port:str = "COM11",id:int = 0,state:int = RESPOND_MODE):
  14. _# 调用父类的初始化方法,super() 函数将父类和子类连接_
  15. super().__init__(port)
  16. self.sensorvalue = 0
  17. self.sensorid = id
  18. self.sensorstate = state
  19. _# 传感器上电初始化_
  20. def InitSensor(self):
  21. _# 传感器上电初始化工作_
  22. _# 同时输出ID号以及状态_
  23. print("Sensor %d Init complete : %d"%(self.sensorid,self.sensorstate))
  24. _# 开启传感器_
  25. def StartSensor(self):
  26. super().OpenSerial()
  27. print("Sensor %d start serial %s "%(self.sensorid,self.dev.port))
  28. _# 停止传感器_
  29. def StopSensor(self):
  30. super().CloseSerial()
  31. print("Sensor %d close serial %s " % (self.sensorid, self.dev.port))
  32. _# 发送传感器ID号_
  33. def SendSensorID(self):
  34. super().WriteSerial(str(self.sensorid))
  35. print("Sensor %d send id "%self.sensorid)
  36. _# 发送传感器数据_
  37. def SendSensorValue(self):
  38. _# 生成[1, 10]内的随机整数_
  39. data = random.randint(1, 10)
  40. super().WriteSerial(str(data))
  41. print("Sensor %d send data %d" % (self.sensorid,data))
  42. _# 接收主机指令_
  43. def RecvMasterCMD(self):
  44. cmd = super().ReadSerial()
  45. print("Sensor %d recv cmd %d " % (self.sensorid,cmd))
  46. return cmd
  47. class MasterClass(SerialClass):
  48. _# 类变量:_
  49. _# BUSY_STATE -忙碌状态-0_
  50. _# IDLE_STATE -空闲状态-1_
  51. BUSY_STATE, IDLE_STATE = (0, 1)
  52. START_CMD, STOP_CMD, SENDID_CMD, SENDVALUE_CMD = (0, 1, 2, 3)
  53. _# 类的初始化_
  54. def __init__(self,state:int = IDLE_STATE,port:str = "COM17"):
  55. _# 调用父类的初始化方法,super() 函数将父类和子类连接_
  56. super().__init__(port)
  57. self.valuequeue = queue.Queue(10)
  58. self.__masterstatue = state
  59. _# 开启主机_
  60. def StartMaster(self):
  61. super().OpenSerial()
  62. logging.info("START MASTER :"+self.dev.port)
  63. _# 停止主机_
  64. def StopMaster(self):
  65. super().CloseSerial()
  66. logging.info("CLOSE MASTER :" + self.dev.port)
  67. _# 接收传感器ID号_
  68. def RecvSensorID(self):
  69. sensorid = super().ReadSerial()
  70. logging.info("MASTER RECIEVE ID : " + str(sensorid))
  71. return sensorid
  72. _# 接收传感器数据_
  73. def RecvSensorValue(self):
  74. data = super().ReadSerial()
  75. logging.info("MASTER RECIEVE DATA : " + str(data))
  76. self.valuequeue.put(data)
  77. return data
  78. _# 主机发送命令_
  79. def SendSensorCMD(self,cmd):
  80. super().WriteSerial(str(cmd))
  81. logging.info("MASTER SEND CMD : " + str(cmd))
  82. _# 主机返回工作状态-_
  83. def RetMasterStatue(self):
  84. return self.__masterstatue

模拟实例

这里,我们使用 XCOM 软件和我们的 Python 程序进行交互。

传感器实验模拟

这里,我们首先在主函数中创建传感器对象,完成初始化后使能传感器中串口模块,并设置循环,轮询读取指令并执行操作,示例代码如下:

  1. if __name__ == "__main__":
  2. _# 创建传感器对象_
  3. s = SensorClass(port="COM11", id=1, state=SensorClass.RESPOND_MODE)
  4. _# 初始化传感器_
  5. s.InitSensor()
  6. _# 传感器开启_
  7. s.StartSensor()
  8. while True:
  9. _# 根据不同指令执行不同操作_
  10. cmd = s.RecvMasterCMD()
  11. _# START_CMD, STOP_CMD, SENDID_CMD, SENDVALUE_CMD = (0, 1, 2, 3)_
  12. if cmd == SensorClass.SENDID_CMD:
  13. s.SendSensorID()
  14. elif cmd == SensorClass.SENDVALUE_CMD:
  15. s.SendSensorValue()
  16. elif cmd == SensorClass.STOP_CMD:
  17. s.StopSensor()
  18. break
  19. print(" Sensor Stop Work!")

我们来看一下实际验证效果:

主机实验模拟

这里,我们首先在主函数中创建并开启主机对象,我们的 xcom 模拟传感器,主机在轮询中发送接收数据指令,并将接收的数据加入主机类的队列,最后发送停机命令,并关闭主机。

  1. if __name__ == "__main__":
  2. m = MasterClass(state=MasterClass.IDLE_STATE, port="COM17")
  3. m.StartMaster()
  4. _# START_CMD, STOP_CMD, SENDID_CMD, SENDVALUE_CMD = (0, 1, 2, 3)_
  5. _# 发送指令,获取传感器ID_
  6. m.SendSensorCMD(MasterClass.SENDID_CMD)
  7. m.RecvSensorID()
  8. for i in range(3):
  9. _# 发送指令,获取传感器数据_
  10. m.SendSensorCMD(MasterClass.SENDVALUE_CMD)
  11. m.RecvSensorValue()
  12. m.SendSensorCMD(MasterClass.STOP_CMD)
  13. m.StopMaster()
  14. print("Master Stop Work!")

这里我们将主机日志打印到文件中:

  1. _# 在配置下日志输出目标文件和日志格式_
  2. LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
  3. logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT)

我们来看一下实际验证效果:

可以看到两个实验都可以完整运行,关于两个类的交互工作,我们将在后续多线程中讲到。

全网最适合入门的面向对象编程教程:11 类和对象的Python实现-子类调用父类方法-模拟串口传感器和主机的更多相关文章

  1. Java基础--面向对象编程1(类与对象)

    1.类(class)的定义 类是对一组具有相同特征和行为的对象的抽象描述. 在程序中,引入类的概念,就是为了快速生成更多的具有相同特性和行为的事物. 2.对象(object)的定义 对象是类的具体实现 ...

  2. Python开发基础-Day17面向对象编程介绍、类和对象

    面向对象变成介绍 面向过程编程 核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西.主要应用在一旦完成很少修改的地方,如linux ...

  3. python基础之面向对象编程介绍、类和对象

    面向对象变成介绍 面向过程编程 核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西.主要应用在一旦完成很少修改的地方,如linux ...

  4. 第2节 Scala中面向对象编程:9、getClass和classOf;10、调用父类的constructor;11、抽象类和抽象字段;

    6.3.4.     Scala中getClass 和 classOf Class A extends class B B b=new A    b.getClass ==classOf[A] B b ...

  5. python面向对象的三大特征--继承子类调用父类方法

    #在子类中调用父类方法 class Vehicle: country="China" def __init__(self,name,speed,load,power): self. ...

  6. [Java入门笔记] 面向对象编程基础(二):方法详解

    什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...

  7. 最适合入门的Laravel中级教程(一)

    Laravel 是一个全栈框架: 我们使用 Laravel 开发业务常见有 3 个方向: 前端页面和后端逻辑混合的应用 主要是面向对 SEO 有需求的项目: 比如说新闻资讯博客文章等: 一般在控制器中 ...

  8. Python入门之面向对象编程(一)面向对象概念及优点

    概念 谈到面向对象,很多程序员会抛出三个词:封装.继承和多态:或者说抽象.一切都是对象之类的话,然而这会让初学者更加疑惑.下面我想通过一个小例子来说明一下 面向对象一般是和面向过程做对比的,下面是一个 ...

  9. Python:面向对象编程3 定制类(有更新)

    Python:面向对象编程3  定制类(有更新) ⚠️本文主要内容为对Data model相关知识点的提取学习记录.(内容来自文档和部分网页教程案例) ⚠️:这个连接指向<流畅的python&g ...

  10. [.net 面向对象编程基础] (9) 类和类的实例

    [.net 面向对象编程基础] (9) 类和类的实例 类 ,顾名思义就是分类.类别的意思.我们要面向对象编程,就需要对不同的事物进行分类.类可以说是.net面向对象的核心. 类:就是具有相同的属性和功 ...

随机推荐

  1. C4996 scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details

    编译器报错,编译器使用的是Visual Studio2019版本 修改项目属性的检测sdl. 第一步,右键项目--->属性 *第二步,在打开的属性中选择,配置属性-->C/C++---&g ...

  2. debug技巧之远程调试

    一.前言 大家好啊,我是summo,今天给大家分享一下我平时是怎么调试代码的,不是权威也不是教学,就是简单分享一下,如果大家还有更好的调试方式也可以多多交流哦. 当我们的应用发布到线上之后,就不能随意 ...

  3. rsync备份服务器部署详情

    rsync  -avz --bwlimit=1024M /data/wanxhe  rsync_backup@10.x.x.38::backup/gpu007/data/ --password-fil ...

  4. 记一次DRF问题排障

    1 最近在搞django,在写一个接口的时候用到了APIview,之后再调用接口的时候一直显示405,不允许使用post方法 视图

  5. OpenOCD + DAP-LINK调试ESP32的失败经历

    目的 手里有调试STM32的DAP-LINK,想试试通过JTAG调试ESP32 OpenOCD支持CMSIS-DAP DAP-LINK支持的芯片,我手上这款描述如下,应该JTAG协议的都支持 平台 w ...

  6. 论GNU、Linux和GNU/Linux之间的关系

    相信很多人看到了这个标题就会产生疑问,这篇文章到底要讲什么东西?在回答这个问题之前,我先提出几个问题? 1. 什么是Linux? 2. 什么是GNU? 3. GNU/Linux是什么玩意儿? 在回答了 ...

  7. 一键云部署:ROS的Terraform托管服务助你轻松上线2048经典游戏

    在现代云计算环境中,自动化部署已经成为一项重要的任务.Terraform,作为HashiCorp公司的一款开源工具,以其强大的基础设施即代码(IaC)能力,使得我们能够轻松管理和部署各种云资源. 阿里 ...

  8. Flask-Limit详细说明:接口限流

    速率限制通常作为服务的防御措施予以实施.服务需要保护自身以免过度使用(无论是有意还是无意),从而保持服务可用性.在Flask项目开发过程中,遇到了需要对接口进行限制的需求,又不想去造轮子,这时候就需要 ...

  9. React 中的 useRef 与 useState

    React 是一个流行的 JavaScript 库,用于构建用户界面.它提供了几个钩子,使开发人员能够管理状态并执行副作用. React 中两个常用的钩子是 useRef 和 useState .虽然 ...

  10. WPS WORD EXCEL 不合并显示

    WPS WORD EXCEL 不合并显示 版本:WPS 12 , 下载时间约是2023 年. 1.在开始菜单里找到 WPS OFFICE - 配置工具 2.点击"高级(A)". 3 ...