Class Python 中的层次结构
Class hierarchy in Python
我有一个中继板使用 Python 绑定通过 firmata 协议连接到 arduino。使用 pyfirmata (https://github.com/tino/pyFirmata) 进行通信没有问题。
继电器板有16个继电器。每组 3 个继电器是一个通道。每个通道都连接到一个被测设备输入或输出。这只是为了粗略地描述 releboard 的目的。
您可以在下面找到代码的框架。
#!/usr/bin/env python
__version__ = '0.1'
# Fault Injection Unit
# Power is connected to Fault Bus 1
# Ground is connected to Fault Bus 2
from pyfirmata import Arduino
class FaultInsertionBoard(object):
def __init__ (self, comPort = 'COM3'):
"""Initalize the Fault insertion Board
Open communication with host via serial port
Arguments:
comPort -- The serial port used to connect the board to the host.
"""
self.board = Arduino(comPort)
class Channel(object):
def __init__ (self, aChannel):
""" Create a Channel"""
pass
def NoFault():
""" Set the channel to the "No fault" condition
No Fault condition is:
-- DUT channel connected to the testing sistem
-- DUT channel disconnected from the Fault bus 1
-- DUT channel disconnected from the Fault bus 2
"""
pass
def OpenCircuit():
""" Set the channel to the "Open Circuit fault" condition
Open Circuit fault condition is:
-- DUT channel disconnected from the testing sistem
-- DUT channel disconnected from the Fault bus 1
-- DUT channel disconnected from the Fault bus 2
"""
pass
def ShortToGround():
""" Set the channel to the "Short to Ground fault" condition
Open Circuit fault condition is:
-- DUT channel disconnected from the testing sistem
-- DUT channel disconnected from the Fault bus 1
-- DUT channel connected to the Fault bus 2
"""
pass
def ShortToPower():
""" Set the channel to the "Short to Ground fault" condition
Open Circuit fault condition is:
-- DUT channel disconnected from the testing sistem: channel relay is open
-- DUT channel connected to the Fault bus 1: Fault Bus 1 relay is closed
-- DUT channel disconnected from the Fault bus 2: Fault Bus 1 relay is open
"""
pass
def main():
FaultBoard = FaultInsertionBoard('COM3')
VoutSensor = FaultBoard.Channel(0)
IOutSensor = FaultBoard.Channel(1)
VoutSensor.NoFault()
IOutSensor.NoFault()
VoutSensor.ShortToGround()
IOutSensor.ShortToPower()
if __name__ == "__main__":
main()
其中:
FaultInsertionBoard
是 Arduino
class 的简单包装
Firmata
.
Channel(n)
标识第 n
组三个继电器
NoFault
、ShortToPower
、ShortToGround
是各种配置
每个通道的三个继电器(它并不重要的实际
配置)。
现在的问题是:我对用 C 编写的嵌入式固件有很好的体验,远不如 Python。显然上面的代码是不正确的。
有人可以向我推荐一个 class 框架以获得上述功能吗?换句话说,我如何编写 Python 代码来驱动上述继电器?
PS:或者我可以这样写:
FaultBoard = FaultInsertionBoard('COM3')
FaultBoard.Channel(0).NoFault()
但我觉得不够优雅和清晰。
一方面,您的实际问题很笼统,以后您应该尝试更具体一些。另一方面,初学者通常很难知道从哪里开始,因此我将为您提供一些设计技巧,以帮助您克服这一挑战。
无嵌套classes
嵌套 classes 在 Python 中几乎没有用。完全合法,但毫无意义。它们不会让您神奇地访问包含 class,并且不会出现在任何实例中(因为它们可能出现在 Java 中)。嵌套所做的只是使命名空间更加复杂。
我要做的第一件事是将 Channel
移出 FaultInsertionBoard
。一个简单的 unindent 就足够了。我将向您展示如何进一步使用它。
命名约定
要记住的另一件事是 Python 命名约定。虽然不是必需的,但通常只将 class 名称大写,而其他所有内容都是小写,单词之间有下划线(而不是驼峰式)。
在函数参数的默认值定义中,不在=
两边放置空格也是惯例。
我将在整个回答过程中遵循这些约定。
继承与包容
对于 FaultInsertionBoard
:
,您可能应该使用继承而不是包含
class FaultInsertionBoard(Arduino):
pass
这将使FaultInsertionBoard
拥有Arduino
的所有方法和属性。您现在可以使用 fault_board.method()
而不是 fault_board.board.method()
,其中 method
是 Arduino
class.
的一些方法
您可能需要定义一些额外的初始化步骤,例如为 com_port
设置默认值,然后再设置通道。您可以定义自己的 __init__
版本,并在需要时调用父 class 的实现:
class FaultInsertionBoard(Arduino):
def __init__(self, com_port='COM3'):
super().__init__(com_port)
如果您使用Python 2.x,请使用super(FaultInsertionBoard, self).__init__
。
添加频道
为了能够实际访问通道实例,您需要定义一些数据结构来保存它们,并预先初始化一些通道。数据结构可以作为属性直接访问,或通过对参数进行一些额外检查的方法访问。
正如我之前提到的,嵌套 Channel
class 根本不会让你朝这个方向移动。事实上,由于你的 Channel
class 可能需要访问其父板,我们将在其构造函数中添加一个新的初始化参数:
class Channel:
def __init__(self, channel_id, parent):
self.id = channel_id
self.parent = parent
您有几个选项可供选择。最简单的是在 FaultInsertionBoard
中初始化一个 Channel
序列,您可以通过 []
而不是 ()
:
访问它
class FaultInsertionBoard(Arduino):
def __init__(self, com_port='COM3'):
super().__init__(com_port)
self.channels = []
self.channels.append(Channel(0, self))
self.channels.append(Channel(1, self))
...
现在 main
将如下所示:
def main():
fault_board = FaultInsertionBoard('COM3')
v_out_sensor = fault_board.channels[0]
i_out_sensor = fault_board.channel[1]
v_out_sensor.no_fault()
v_out_sensor.short_to_ground()
i_out_sensor.no_fault()
i_out_sensor.short_to_ground()
如果你绝对想使用括号来访问channel(0)
等频道,你可以在FaultInsertionBoard
中定义一个方法。保持 __init__
方法不变,您可以添加另一个方法:
def channel(self, index):
# Check index if you want to, possibly raise an error if invalid
return self.channels[index]
在这种情况下 main
将如下所示:
def main():
fault_board = FaultInsertionBoard('COM3')
v_out_sensor = fault_board.channel(0)
i_out_sensor = fault_board.channel(1)
v_out_sensor.no_fault()
v_out_sensor.short_to_ground()
i_out_sensor.no_fault()
i_out_sensor.short_to_ground()
第一种方法的优点是允许您直接访问 Channel
个对象的序列。由于您对通道应用相同的操作,因此您可以遍历所有通道以获得更简单的界面:
def main():
fault_board = FaultInsertionBoard('COM3')
for channel in fault_board.channels:
channel.no_fault()
channel.short_to_ground()
便捷方法
您似乎在代码中多次使用操作 x.no_fault(); x.short_to_ground()
。在这种情况下创建所谓的便捷方法通常很有帮助。您可以将以下内容添加到 Channel
:
def reset(self):
self.no_fault()
self.short_to_ground()
然后 main
看起来像这样:
def main():
fault_board = FaultInsertionBoard('COM3')
for channel in fault_board.channels:
channel.reset()
我有一个中继板使用 Python 绑定通过 firmata 协议连接到 arduino。使用 pyfirmata (https://github.com/tino/pyFirmata) 进行通信没有问题。
继电器板有16个继电器。每组 3 个继电器是一个通道。每个通道都连接到一个被测设备输入或输出。这只是为了粗略地描述 releboard 的目的。
您可以在下面找到代码的框架。
#!/usr/bin/env python
__version__ = '0.1'
# Fault Injection Unit
# Power is connected to Fault Bus 1
# Ground is connected to Fault Bus 2
from pyfirmata import Arduino
class FaultInsertionBoard(object):
def __init__ (self, comPort = 'COM3'):
"""Initalize the Fault insertion Board
Open communication with host via serial port
Arguments:
comPort -- The serial port used to connect the board to the host.
"""
self.board = Arduino(comPort)
class Channel(object):
def __init__ (self, aChannel):
""" Create a Channel"""
pass
def NoFault():
""" Set the channel to the "No fault" condition
No Fault condition is:
-- DUT channel connected to the testing sistem
-- DUT channel disconnected from the Fault bus 1
-- DUT channel disconnected from the Fault bus 2
"""
pass
def OpenCircuit():
""" Set the channel to the "Open Circuit fault" condition
Open Circuit fault condition is:
-- DUT channel disconnected from the testing sistem
-- DUT channel disconnected from the Fault bus 1
-- DUT channel disconnected from the Fault bus 2
"""
pass
def ShortToGround():
""" Set the channel to the "Short to Ground fault" condition
Open Circuit fault condition is:
-- DUT channel disconnected from the testing sistem
-- DUT channel disconnected from the Fault bus 1
-- DUT channel connected to the Fault bus 2
"""
pass
def ShortToPower():
""" Set the channel to the "Short to Ground fault" condition
Open Circuit fault condition is:
-- DUT channel disconnected from the testing sistem: channel relay is open
-- DUT channel connected to the Fault bus 1: Fault Bus 1 relay is closed
-- DUT channel disconnected from the Fault bus 2: Fault Bus 1 relay is open
"""
pass
def main():
FaultBoard = FaultInsertionBoard('COM3')
VoutSensor = FaultBoard.Channel(0)
IOutSensor = FaultBoard.Channel(1)
VoutSensor.NoFault()
IOutSensor.NoFault()
VoutSensor.ShortToGround()
IOutSensor.ShortToPower()
if __name__ == "__main__":
main()
其中:
FaultInsertionBoard
是Arduino
class 的简单包装Firmata
.Channel(n)
标识第n
组三个继电器NoFault
、ShortToPower
、ShortToGround
是各种配置 每个通道的三个继电器(它并不重要的实际 配置)。
现在的问题是:我对用 C 编写的嵌入式固件有很好的体验,远不如 Python。显然上面的代码是不正确的。
有人可以向我推荐一个 class 框架以获得上述功能吗?换句话说,我如何编写 Python 代码来驱动上述继电器?
PS:或者我可以这样写:
FaultBoard = FaultInsertionBoard('COM3')
FaultBoard.Channel(0).NoFault()
但我觉得不够优雅和清晰。
一方面,您的实际问题很笼统,以后您应该尝试更具体一些。另一方面,初学者通常很难知道从哪里开始,因此我将为您提供一些设计技巧,以帮助您克服这一挑战。
无嵌套classes
嵌套 classes 在 Python 中几乎没有用。完全合法,但毫无意义。它们不会让您神奇地访问包含 class,并且不会出现在任何实例中(因为它们可能出现在 Java 中)。嵌套所做的只是使命名空间更加复杂。
我要做的第一件事是将 Channel
移出 FaultInsertionBoard
。一个简单的 unindent 就足够了。我将向您展示如何进一步使用它。
命名约定
要记住的另一件事是 Python 命名约定。虽然不是必需的,但通常只将 class 名称大写,而其他所有内容都是小写,单词之间有下划线(而不是驼峰式)。
在函数参数的默认值定义中,不在=
两边放置空格也是惯例。
我将在整个回答过程中遵循这些约定。
继承与包容
对于 FaultInsertionBoard
:
class FaultInsertionBoard(Arduino):
pass
这将使FaultInsertionBoard
拥有Arduino
的所有方法和属性。您现在可以使用 fault_board.method()
而不是 fault_board.board.method()
,其中 method
是 Arduino
class.
您可能需要定义一些额外的初始化步骤,例如为 com_port
设置默认值,然后再设置通道。您可以定义自己的 __init__
版本,并在需要时调用父 class 的实现:
class FaultInsertionBoard(Arduino):
def __init__(self, com_port='COM3'):
super().__init__(com_port)
如果您使用Python 2.x,请使用super(FaultInsertionBoard, self).__init__
。
添加频道
为了能够实际访问通道实例,您需要定义一些数据结构来保存它们,并预先初始化一些通道。数据结构可以作为属性直接访问,或通过对参数进行一些额外检查的方法访问。
正如我之前提到的,嵌套 Channel
class 根本不会让你朝这个方向移动。事实上,由于你的 Channel
class 可能需要访问其父板,我们将在其构造函数中添加一个新的初始化参数:
class Channel:
def __init__(self, channel_id, parent):
self.id = channel_id
self.parent = parent
您有几个选项可供选择。最简单的是在 FaultInsertionBoard
中初始化一个 Channel
序列,您可以通过 []
而不是 ()
:
class FaultInsertionBoard(Arduino):
def __init__(self, com_port='COM3'):
super().__init__(com_port)
self.channels = []
self.channels.append(Channel(0, self))
self.channels.append(Channel(1, self))
...
现在 main
将如下所示:
def main():
fault_board = FaultInsertionBoard('COM3')
v_out_sensor = fault_board.channels[0]
i_out_sensor = fault_board.channel[1]
v_out_sensor.no_fault()
v_out_sensor.short_to_ground()
i_out_sensor.no_fault()
i_out_sensor.short_to_ground()
如果你绝对想使用括号来访问channel(0)
等频道,你可以在FaultInsertionBoard
中定义一个方法。保持 __init__
方法不变,您可以添加另一个方法:
def channel(self, index):
# Check index if you want to, possibly raise an error if invalid
return self.channels[index]
在这种情况下 main
将如下所示:
def main():
fault_board = FaultInsertionBoard('COM3')
v_out_sensor = fault_board.channel(0)
i_out_sensor = fault_board.channel(1)
v_out_sensor.no_fault()
v_out_sensor.short_to_ground()
i_out_sensor.no_fault()
i_out_sensor.short_to_ground()
第一种方法的优点是允许您直接访问 Channel
个对象的序列。由于您对通道应用相同的操作,因此您可以遍历所有通道以获得更简单的界面:
def main():
fault_board = FaultInsertionBoard('COM3')
for channel in fault_board.channels:
channel.no_fault()
channel.short_to_ground()
便捷方法
您似乎在代码中多次使用操作 x.no_fault(); x.short_to_ground()
。在这种情况下创建所谓的便捷方法通常很有帮助。您可以将以下内容添加到 Channel
:
def reset(self):
self.no_fault()
self.short_to_ground()
然后 main
看起来像这样:
def main():
fault_board = FaultInsertionBoard('COM3')
for channel in fault_board.channels:
channel.reset()