第4章:Python面向对象编程——类、继承、多态,封装仪器驱动
为什么ATE编程离不开面向对象?
你可能刚入门时会疑惑:写个仪器控制脚本,直接调用VISA库不行吗?行是行,但规模化ATE项目里,你会发现代码膨胀到没法看。我一老同事曾把整个测试系统写成了一个几千行的单文件,后来加一台新仪器,改了三天,还引入了五个bug。嗯,那之后,我们就把面向对象搬了进来。
说白了,面向对象就是帮你把“仪器”和“操作”打包成一块块积木。每块积木内部复杂,但对外只暴露简单的接口。你换仪器,只需换积木,不用改测试流程。
类:第一块积木——把你的数字万用表封装起来
先看一个最简单的例子。我手头有一台Keysight 34465A数字万用表。我们会为它写一个类:
class DMM:
"""数字万用表驱动类"""
def __init__(self, resource_name):
self.resource = resource_name
self.visa_handle = None
def connect(self):
# 实际连接代码略
self.visa_handle = rm.open_resource(self.resource)
def measure_dc_voltage(self):
# 发送指令,返回电压值
return self.visa_handle.query("MEAS:VOLT:DC?")
你看,_ _ init _ _ 就像给积木起名字、分配资源。connect()和measure_dc_voltage()是它能干的事。我习惯把每个仪器类放在独立文件里,比如dmm.py、oscillo.py,方便管理。
继承:让万用表和示波器共享一个“爹”
为什么需要继承?你想想看,所有仪器都有连接、断开、ID查询这些通用操作。如果每种仪器都写一遍connect(),那可太傻了。我们定义一个基类
class Instrument:
def __init__(self, resource_name):
self.resource = resource_name
self.visa_handle = None
def connect(self):
self.visa_handle = rm.open_resource(self.resource)
def disconnect(self):
if self.visa_handle:
self.visa_handle.close()
def query_id(self):
return self.visa_handle.query("*IDN?")
然后让DMM继承它:
class DMM(Instrument):
def measure_dc_voltage(self):
return self.visa_handle.query("MEAS:VOLT:DC?")
同样,Oscilloscope也继承Instrument,添加自己的方法。我经历过一个项目,基类写好之后,增加一台新示波器只用了半天——仅仅实现了scope特定的测量方法,其他全复用。
多态:换仪器不用改测试流程
多态是个高大上的词,用大白话讲就是“同样的调用方式,不同的行为”。比如我们定义一个测试函数,只需要知道仪器类有measure()方法,不管它是DMM还是PowerSupply:
def run_test(instrument):
result = instrument.measure() # 多态调用
if result > 1.0:
print("FAIL")
else:
print("PASS")
测试流程根本不用知道你是谁。我后来写过一个“仪器切换器”,根据配置文件里的仪器类型,动态创建对象:
def create_instrument(config):
if config['type'] == 'DMM':
return DMM(config['resource'])
elif config['type'] == 'OSC':
return Oscilloscope(config['resource'])
# ...
跑一下run_test(create_instrument(cfg)),换仪器就像换零件,测试函数纹丝不动。是不是清爽很多?
用一张图看清这一切
下面这张图展示了基类、子类和多态调用的层次关系。你抬头看SVG,一目了然:
封装仪器驱动的实战要点
光有理论不够。我把这几年摸爬滚打的套路列出来:
- 每个驱动器一个类,一个文件。别把万用表和电源放在一起,你会后悔的。
- 基类只放公共操作。比如连接、断开、ID查询。别过早设计虚方法,需要时再添加。
- 多态接口保证签名一致。比如所有仪器类都有measure()或query(),参数列表统一。
- 异常处理统一。我一般会在基类里定义一个raise_visa_error(),子类调用它,错误信息格式一致,调试方便。
- 资源路径从外部传进来。别写死,用配置文件或环境变量。
一句话总结:类把仪器功能打包,继承消除重复代码,多态让测试程序与具体仪器解耦。你在ATE项目中越早用上这三个特性,后期维护越轻松。
当然,面向对象不是银弹。如果一个仪器只有一两个操作,写个函数也行,别过度设计。我见过有人给一个开关矩阵写了6层继承,改了需求后全推翻——适度原则很重要。
好了,这一章就把Python面向对象在ATE仪器驱动中的用法讲清楚了。下去后你可以拿自己手头的仪器试试,写一个基类和一个子类,跑跑看。代码跑通了,你会更有感觉。