如何为PLC扩展板Python API的属性自定义命名?
问题:自定义命名访问Pixtend PLC扩展板的IO属性
我在树莓派上用Kontron Pixtend PLC扩展板读写气动阀和开关,官方提供的Python API(PiXtendV2L类)用@property实现了IO属性(比如digital_in0、relay0)的读写。我想用更有意义的自定义名称,比如把self.p.relay0映射为self.valve,self.p.digital_in0映射为self.switch,但直接赋值后无法触发原属性的getter和setter,读写状态完全无效——哪怕内存地址看起来一样也没用。怎么用少量代码实现自定义命名的访问?
相关代码片段:PiXtendV2L类的部分实现:
class PiXtendV2L(): # ... 其他代码 ... @property def digital_in0(self): """ 获取数字输入0的状态:False为关闭,True为开启 :return: 当前状态 :rtype: bool """ return self.test_bit(self._digital_in0, self.BIT_0) == 1 @property def relay0(self): """ 获取或设置继电器0的状态:False为关闭,True为开启 :return: 当前状态 :rtype: bool """ return self.test_bit(self._relay_out, self.BIT_0) == 1 @relay0.setter def relay0(self, value): if not isinstance(value, bool): raise ValueError(f"值错误!不允许使用{str(value)},只能用False(关)或True(开)") bit_num = self.BIT_0 if value is self.OFF: self._relay_out = self.clear_bit(self._relay_out, bit_num) if value is self.ON: self._relay_out = self.set_bit(self._relay_out, bit_num) # ... 其他代码 ...
我原来的无效代码:
self.p = PiXtendV2L(com_interval=0.05, callback=self.SPSCallback) self.valve = self.p.relay0 self.switch = self.p.digital_in0 if self.switch: self.value = True else: self.valve = False
解决方案
问题根源
直接赋值self.valve = self.p.relay0只是把relay0当前的布尔值拷贝到了self.valve里,之后读写self.valve都是操作这个本地变量,和PiXtend的原属性完全解绑,自然不会触发getter/setter。要实现自定义名称的代理,需要把自定义属性和原属性的访问逻辑绑定起来。
方法1:用@property手动代理(推荐,少量自定义名称时)
在你的控制器类里,用@property把自定义名称直接代理到PiXtend的原属性上,代码直观且容易维护:
class YourController: def __init__(self): self.p = PiXtendV2L(com_interval=0.05, callback=self.SPSCallback) # 气动阀:代理relay0 @property def valve(self): return self.p.relay0 @valve.setter def valve(self, value): self.p.relay0 = value # 开关:代理digital_in0 @property def switch(self): return self.p.digital_in0
使用时直接操作self.valve和self.switch即可,和使用原属性完全一样:
if self.switch: self.value = True else: self.valve = False
方法2:用__getattr__/__setattr__批量映射(适合大量自定义名称)
如果需要自定义很多IO名称,用属性映射的方式可以减少重复代码:
class YourController: def __init__(self): self.p = PiXtendV2L(com_interval=0.05, callback=self.SPSCallback) # 定义自定义名称到原属性的映射 self._io_map = { 'valve': 'relay0', 'switch': 'digital_in0', # 可以添加更多映射 } def __getattr__(self, name): # 当访问自定义名称时,代理到原属性 if name in self._io_map: return getattr(self.p, self._io_map[name]) # 其他属性按默认逻辑处理 raise AttributeError(f"'{type(self).__name__}' 对象没有属性 '{name}'") def __setattr__(self, name, value): # 避免初始化时的递归问题,先处理内部变量 if name == '_io_map' or name == 'p': super().__setattr__(name, value) elif name in self._io_map: # 设置自定义名称时,代理到原属性的setter setattr(self.p, self._io_map[name], value) else: # 其他属性按默认逻辑处理 super().__setattr__(name, value)
这种方式只需要维护_io_map字典,就能快速添加新的自定义名称,无需重复写@property。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

