如何让父类自动识别子类,无需显式声明?(Python SCPI开发)
实现SCPI命令层级式Python API的问题解决
我正在开发一个处理SCPI命令的Python API,目标是实现类似instrument.modulator.drive("?")的层级调用方式。SCPI命令示例如下:
MODulator:DRiVe :DRiVe? :AMPlitude :AMPlitude? :AMPRange?
我创建了modulator子类继承自HindsPEM200父类,但调用pem.modulador.drive()时出现如下错误:
AttributeError: 'HindsPEM200' object has no attribute 'modulador'
请问如何让父类自动识别子类,无需显式声明?当前代码如下:
import pyvisa class HindsPEM200: def __init__(self,address:str = None, write_termination:str = "\n", read_termination:str = "\n", baudrate:int = 250000, timeout:int = 1000 ): self.address = address # Open PyVISA resource manager and alias it! self.rm = pyvisa.ResourceManager() self.connection(address = address, write_termination=write_termination, read_termination=read_termination, baudrate=baudrate, timeout=timeout) def connection(self, address = None, write_termination:str = "\r", read_termination:str = "\r", baudrate:int = 250000, timeout:int = 1000 ): """ This functions uses PyVISA to initiate communications with the instrument. Parameters ---------- address : str, optional DESCRIPTION. This parameter sets the COM address of the device. The default value is None, and it will make the procedure raise an exception write_termination : str, optional DESCRIPTION. The default is '/n/r'. read_termination : str, optional DESCRIPTION. The default is '/n/r'. baudrate : int, optional DESCRIPTION. The default is 250000. timeout : int, optional DESCRIPTION. The default is 600. Returns ------- None.""" if address == None: print("ERROR:: No address specified!\n") else: print(f"::VISA:: Instrument address is {address}\n") self.hinds_pem = self.rm.open_resource(address) self.hinds_pem.write_termination = write_termination self.hinds_pem.read_termination = read_termination self.hinds_pem.baud_rate = baudrate self.hinds_pem.timeout = timeout print(f"::VISA:: Connection opened for {address}\n") return self.hinds_pem def identification(self): """ Query the instrument identification string""" idn_str = self.hinds_pem.query('*IDN?') print("\n=========================================================") print(f"::*IDN?:: Identification Table for {self.address}") print("=========================================================") print(f"{self.address} is {idn_str}") print("=========================================================\n") ################# # :MODulator:DRive(?) ################# class modulator(HindsPEM200): def __init__(self): HindsPEM200.__init__(self) def drive(self,args = None): input_str = "MODulator:DRIVe?" answer = self.hinds_pem.query(input_str) print(answer) ############################################################################### ############################################################################### pem = HindsPEM200("ASRL9::INSTR") pem.identification() pem.modulador.drive()
问题分析与解决方案
核心错误点
- 继承关系错误:你当前用
modulator继承HindsPEM200,这是"is-a"(是一种)的关系,但实际你需要的是"has-a"(拥有一个)的关系——仪器实例包含调制器模块,而非调制器是一种仪器。 - 拼写错误:类名是
modulator,但调用时写的是modulador,这是直接触发AttributeError的原因之一。 - 父类未初始化子类实例:即使继承关系正确,父类实例也不会自动拥有子类的属性,因为子类是独立的类。
修改后的代码实现
- 重构
modulator类,使其不再继承父类,而是持有仪器实例的引用; - 在
HindsPEM200的初始化方法中创建modulator实例,作为自身属性。
import pyvisa class HindsPEM200: def __init__(self, address:str = None, write_termination:str = "\n", read_termination:str = "\n", baudrate:int = 250000, timeout:int = 1000 ): self.address = address self.rm = pyvisa.ResourceManager() self.connection(address=address, write_termination=write_termination, read_termination=read_termination, baudrate=baudrate, timeout=timeout) # 初始化调制器模块,作为当前仪器实例的属性 self.modulator = Modulator(self) def connection(self, address = None, write_termination:str = "\r", read_termination:str = "\r", baudrate:int = 250000, timeout:int = 1000 ): if address == None: print("ERROR:: No address specified!\n") else: print(f"::VISA:: Instrument address is {address}\n") self.hinds_pem = self.rm.open_resource(address) self.hinds_pem.write_termination = write_termination self.hinds_pem.read_termination = read_termination self.hinds_pem.baud_rate = baudrate self.hinds_pem.timeout = timeout print(f"::VISA:: Connection opened for {address}\n") return self.hinds_pem def identification(self): idn_str = self.hinds_pem.query('*IDN?') print("\n=========================================================") print(f"::*IDN?:: Identification Table for {self.address}") print("=========================================================") print(f"{self.address} is {idn_str}") print("=========================================================\n") # 重构调制器类,不再继承父类,而是接收仪器实例 class Modulator: def __init__(self, instrument): self.instrument = instrument # 持有父仪器实例的引用 def drive(self, args = None): # 根据参数生成对应的SCPI命令 if args == "?": input_str = "MODulator:DRIVe?" elif args is not None: input_str = f"MODulator:DRIVe {args}" else: input_str = "MODulator:DRIVe" answer = self.instrument.hinds_pem.query(input_str) print(answer) # 调用示例 pem = HindsPEM200("ASRL9::INSTR") pem.identification() # 调用层级方法,注意拼写与类名一致 pem.modulator.drive("?")
额外优化说明
- 遵循PEP8规范,类名首字母大写(
Modulator而非modulator) - 增强
drive方法的灵活性,支持查询("?)、设置值(如drive(10))和无参数三种场景 - 保持层级调用的语义清晰,符合
instrument.modulator.drive()的预期用法
内容的提问来源于stack exchange,提问作者Strelok
相关产品推荐
相关产品推荐

