3、Python编程基础回顾:语法、数据类型、控制流、函数定义,为ATE编程做准备

说实话,很多做ATE的兄弟一开始都问我:“老王,我C语言写得挺溜,干嘛还要学Python?”

嗯,这个问题我当年也问过。直到我接手了一个高速SerDes的测试程序——ATE机台用的是Python脚本控制pattern和Datalog。那一刻我才明白:Python在ATE领域不是替代C,而是做C做不到的事:快速分析Datalog、动态生成pattern、封装测试流程。

这一章我就带你快速过一遍Python最核心的基础。这些都是我平时在ATE项目里天天用的东西,不废话,直接上干货。

Python基础→ATE编程知识框架 Python基础 → ATE测试程序核心要素 Python基础回顾 基本语法 & 数据类型 控制流(if / for / while) 函数定义与作用域 文件读写 / Datalog处理 配置解析 / 参数校验 测试流程封装 / 回调 💡 本课程后三章你将用这些知识写出完整的ATE测试程序框架

3.1 基本语法——别在小事上翻车

Python的语法极其简洁,但越是简单的东西,越容易大意。我见过老工程师写ATE脚本时,因为缩进问题debug了一整天——嗯,就是那种tab和空格混用的经典错误。

我的习惯:项目里统一用4个空格缩进,编辑器设成“自动转空格”。ATE机台自带的编辑器可能不友好,但至少保证你本地写完再上传前,用python -m tabnanny检查一下。
Python基本语法要点(ATE场景常用)
语法元素说明ATE中的常见用法
注释# 单行注释标注测试项、Datalog版本
变量赋值v = 3.3 无需声明类型保存电源电压、频率值
行尾分号不需要,但可以写(不推荐)避免旧C习惯污染
缩进同一代码块必须对齐if/for/while的块结构
print函数print("VDD=%.2f" % v)调试输出、Datalog打印
我曾经踩过的坑:在ATE机台终端里直接粘贴代码,缩进全被转换成tab,结果程序完全跑歪。现在我的做法是——永远先写本地.py文件,用black格式化一遍再上传。

3.2 数据类型——选对容器,事半功倍

Python的数据类型比你想象的多,但ATE编程里常用的就那么几个。我来给你梳理一下,顺便说说我实际怎么用的。

3.2.1 数字与字符串

整型、浮点型、复数——基本够用。字符串用单引号或双引号都行,但ATE机台里的路径字符串经常带反斜杠,我习惯用原始字符串:r"C:\test\pattern",省得转义。

实战技巧:从Datalog里提取数值时,经常碰到“12.345”这种字符串,用float("12.345")一步到位。如果字符串里有单位比如“12.345mV”,先用rstrip("mV")去掉。

3.2.2 列表、元组、字典、集合

容器可变有序常用操作ATE场景
list是是append(), pop()存储测试向量结果序列
tuple否是拆包:a,b = (1,2)函数返回多个值
dict是否(3.7+ 有序)d["key"], get()配置参数键值对、测试极限值
set是否in, & 交集去重、筛选有效测试项

举个例子,我在做Datalog分析时经常这样用:

# 把每行测试结果解析成字典
results = []
for line in lines:
    parts = line.strip().split(",")
    record = {
        "test_name": parts[0],
        "value": float(parts[1]),
        "limit_low": float(parts[2]),
        "limit_high": float(parts[3])
    }
    results.append(record)
# 然后就可以方便地判断是否pass
for r in results:
    if r["value"] < r["limit_low"] or r["value"] > r["limit_high"]:
        print("FAIL: " + r["test_name"])

3.3 控制流——让程序知道什么时候该干什么

ATE测试程序本质上是一连串的“判断→执行→记录”。控制流就是骨架。

3.3.1 if/elif/else

我习惯把条件判断和极限值写在程序头部,用字典索引而不是一长串elif:

limits = {
    "VDD_CORE": (1.1, 1.3),
    "VDD_IO":   (3.0, 3.6),
}
def check_voltage(test_name, measured):
    low, high = limits.get(test_name, (0, 100))
    if measured < low or measured > high:
        return "FAIL"
    return "PASS"

为什么要这样?因为ATE测试项经常变,极限值改一个字典比改十行if判断安全得多。你想想看,改错一个elif的缩进,后果多严重。

3.3.2 for循环——遍历数据的主力

ATE测试里最常见的循环就是遍历所有测试项、Pattern、Datalog行。记住enumerate和zip这两个神器:

# 同时拿到索引和数据
for idx, result in enumerate(results):
    print(f"第{idx+1}项: {result['test_name']} => {result['value']}")

# 同时遍历两个列表
measured_values = [1.25, 3.31, 0.98]
pass_fail = ["PASS", "PASS", "FAIL"]
for v, pf in zip(measured_values, pass_fail):
    if pf == "FAIL":
        print(f"异常值: {v}")
避坑指南:千万不要在循环内部修改正在遍历的列表!我当年写ATE脚本时,想一边遍历一边删除不合格的测量值,结果索引全乱了。正确的做法:用列表推导式或新列表收集合格项。

3.3.3 while循环——适合等待条件

比如等待DUT进入某个状态,或者轮询机台寄存器:

timeout = 1000
cnt = 0
while read_register(0x1A) != 0x80 and cnt < timeout:
    time.sleep(0.001)
    cnt += 1
if cnt >= timeout:
    raise RuntimeError("寄存器响应超时")

对这种场景,我一般会加个超时保护,防止死循环把机台卡住。你想想,ATE机台一卡就是几分钟,产线leader得骂死我。

3.4 函数定义——把你的套路封装起来

ATE测试里有很多重复动作:设置电源、测量电压、写寄存器、读Datalog……不用函数的话,一千行代码你看都不想看。我每次写函数前都会问自己:这个操作以后会不会复用?如果会,就抽出来。

3.4.1 定义与调用

def measure_voltage(pin_name, settle_time_ms=10):
    """测量指定引脚的电压,返回float值(单位V)。"""
    # 假装这里有ATE机台API
    print(f"测量 {pin_name},等待{settle_time_ms}ms...")
    time.sleep(settle_time_ms/1000)
    return 1.25  # 实际会返回真实值

参数默认值、文档字符串(docstring)都加上。我习惯在docstring里写清楚:单位、返回值含义、异常情况。

3.4.2 返回值与多值返回

Python函数可以返回多个值,本质是返回元组:

def check_binary_output(pin_name, expected):
    measured = read_pin(pin_name)
    is_match = (measured == expected)
    return is_match, measured

# 调用时直接拆包
ok, val = check_binary_output("DQ0", 1)
if not ok:
    print(f"DQ0预期1,实测{val}")

我个人不喜欢返回太复杂的结构,超过3个返回值就用字典或命名元组,否则调用方搞不清顺序。

3.4.3 作用域与global

ATE脚本里经常有一个全局配置字典,比如test_params。我建议所有函数都不要直接修改全局变量,要么传参,要么用return新值。如果非要用global,那就要加注释说明为什么。

test_params = {"vdd": 1.2, "speed": 100}

def update_vdd(new_v):
    # 坏写法:直接改全局
    global test_params
    test_params["vdd"] = new_v

# 好写法:返回新字典
def update_params(orig, key, value):
    new_dict = orig.copy()
    new_dict[key] = value
    return new_dict
我曾经犯过的错:在ATE机台上写了一个长脚本,多个函数都用了global test_params。后来机台内存不足,调试时发现某个回调函数意外修改了全局参数导致后续测试全错。从那以后我定了个规矩:函数里只读全局变量,改的话用返回值传递。

3.5 这一章的核心,其实就一句话

Python的语法、数据类型、控制流、函数,这些基础东西,你只要能熟练用到ATE场景里,就足以应付大部分日常测试脚本。说实话,很多ATE脚本也就一两百行,但结构一定要清晰,因为你写的程序大概率要交给产线同事维护。他们可没功夫看你的天马行空。