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

摘要:

本文我们主要介绍了数据封装的基本概念和特性,如何设置自定义类的私有属性和私有方法,protect属性的概念和特点。

文档和代码获取:

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

正文:

面向对象编程的一个重要特点就是数据封装。在上面的SerialClass类中,有关于串口波特率、设备名称等多个属性,我们编写代码的过程中,往往不希望别的模块可以直接访问本模块的数据,而仅通过调用我们的接口函数来访问数据,也就是高内聚和低耦合原则。

在Python中,我们可以通过给属性或方法命名时可以用两个下划线作为开头来改变属性和方法的访问权限。在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,对于私有属性或私有方法来说,是不允许外界访问的。

  • (1)类的私有属性格式:__private_attrs两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时__private_attrs。

  • (2)类的私有方法格式:__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods。

这里,我们联系上一节中串口收发的相关方法,可以看到在使用串口收发的相关方法时,我们并没有判断当前串口的状态,如果串口状态为关闭,那么就不能正常工作。于是我们添加了一个__devstate私有属性用于标识串口状态,同时添加了RetSerialState()方法用于外界获取串口状态,示例代码如下:

import serial import serial.tools.list_ports class SerialClass:  # 初始化  # 使用默认参数  def __init__(self,  devport = "COM17",  devbaudrate = 115200,  devbytesize = serial.EIGHTBITS,  devparity = serial.PARITY_NONE,  devstopbits = serial.STOPBITS_ONE):  # 直接传入serial.Serial()类  self.dev             = serial.Serial()  self.dev.port        = devport  self.dev.baudrate    = devbaudrate  self.dev.bytesize    = devbytesize  self.dev.parity      = devparity  self.dev.stopbits    = devstopbits  # 表示串口设备的状态-打开或者关闭  # 初始化时为关闭  self.__devstate      = False  # 打开串口  def OpenSerial(self):  self.dev.open()  self.__devstate = True  # 关闭串口  def CloseSerial(self):  self.dev.close()  self.__devstate = False  # 串口读取  def ReadSerial(self):  if self.__devstate:  # 非阻塞方式读取  # 按行读取  data = self.dev.readline()  # 收到为二进制数据  # 用utf-8编码将二进制数据解码为unicode字符串  # 字符串转为int类型  data = int(data.decode('utf-8', 'replace'))  return data  # 串口写入  def WriteSerial(self,write_data):  if self.__devstate:  # 非阻塞方式写入  self.dev.write(write_data.encode())  # 输出换行符  # write的输入参数必须是bytes 格式  # 字符串数据需要encode()函数将其编码为二进制数据,然后才可以顺利发送  # \r\n表示换行回车  self.dev.write('\r\n'.encode())  def RetSerialState(self):  if self.dev.isOpen():  self.__devstate = True  return True  else:  self.__devstate = False  return False # 生成串口类的实例 serdev = SerialClass() print("serdev state :",serdev.RetSerialState()) serdev.OpenSerial() print("serdev state :",serdev.RetSerialState()) serdev.CloseSerial() print("serdev state :",serdev.RetSerialState()) print(serdev.__devstate)

这里我们在初始化方法中使用了默认参数,即在定义方法时,直接给形式参数指定一个默认值,这样的话,即便初始化时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。

代码运行结果如下,可以看到在打开或关闭串口设备时,RetSerialState()方法可以正常获取串口状态,而当我们访问类内私有属性时,程序报错:

好的,现在我们终于松了一口气,声明__devstate为私有属性后,外部终于无法访问我们的数据了。但是真的是这样吗?

尝试将:

print(serdev.__devstate)

修改为:

print(serdev._SerialClass__devstate)

此时,我们运行程序,发现代码输出如下:

坏了,怎么外界还能访问我们类内私有属性?这是什么魔法?

这是 Python 的名字改装在起作用。Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性。

由此可知,在Python中私有属性为假私有属性。那为什么不从语法上保证 private 字段的私密性呢?用最简单的一句话来说:We are all consenting adults here(我们都是成年人)。正如Python 程序员的观点:开放要比封闭好。

通过以上讲解,我们实际上已经对类的封装特性有所了解,所谓封装就是:

"隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口"。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。

对于类的私有属性来说,子类是无法访问的,私有变量只有本类的内部能直接调用。这时我们可以用protect属性进行修改,在属性和方法前加一个下划线就是 protect 类型了。类的 protect 属性,子类可以继承,同时实例对象、类对象都能直接调用 protect 属性、方法。

受保护属性在类的外部是可见但不建议直接访问,其命名约定表示它们是内部实现的一部分,不应该被直接访问。总体而言,Python强调封装性,鼓励使用公共方法来访问和修改属性,而不是直接在外部访问。这种做法有助于提高代码的可维护性,防止意外的修改和增加代码的灵活性。



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