You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让父类自动识别子类,无需显式声明?(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()

问题分析与解决方案

核心错误点

  1. 继承关系错误:你当前用modulator继承HindsPEM200,这是"is-a"(是一种)的关系,但实际你需要的是"has-a"(拥有一个)的关系——仪器实例包含调制器模块,而非调制器是一种仪器。
  2. 拼写错误:类名是modulator,但调用时写的是modulador,这是直接触发AttributeError的原因之一。
  3. 父类未初始化子类实例:即使继承关系正确,父类实例也不会自动拥有子类的属性,因为子类是独立的类。

修改后的代码实现

  1. 重构modulator类,使其不再继承父类,而是持有仪器实例的引用;
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 03:34:50