3、Python基础回顾:数据类型、控制流、函数定义、文件操作

好,咱们进入第三章。说实话,很多嵌入式工程师对Python又爱又恨。爱的是它写脚本快,恨的是总觉得它「不靠谱」。我刚开始也这么想,直到我在一个ARM项目里用Python写了个自动化测试脚本,一口气跑了三天三夜没出问题。嗯,从那以后我就踏实了。

这一章咱们快速过一遍Python的核心基础。别嫌啰嗦,我敢打赌,你写嵌入式脚本时踩的坑,十有八九都出在这些基础点上。

3.1 数据类型:别小看这些「小东西」

Python的数据类型,说白了就那几种。但嵌入式场景下,有些坑你得提前知道。

数字类型

整型、浮点型、复数型。嵌入式里最常用的是整型和浮点型。我个人习惯,凡是涉及寄存器地址、位操作,一律用整型。浮点型在MCU上跑,性能开销大,能不用就不用。

# 整型
reg_addr = 0x40021000
bit_mask = 0b00001111

# 浮点型
voltage = 3.3
current = 0.15

# 复数型(嵌入式里用得少,但信号处理时会用到)
impedance = 50 + 20j

我曾经踩过的坑:在Python 2里,整数除法会直接截断。比如 5 / 2 = 2。Python 3修复了这个问题,但如果你在旧系统上跑脚本,记得用 from __future__ import division

字符串与字节串

嵌入式里,字符串和字节串的转换是家常便饭。你想想看,串口发过来的数据,本质上是字节流,不是字符串。

# 字符串
uart_cmd = "AT+RESET\r\n"

# 字节串
uart_data = b'\x01\x02\x03\xFF'

# 转换
cmd_bytes = uart_cmd.encode('ascii')   # 字符串 -> 字节串
data_str = uart_data.decode('utf-8', errors='ignore')  # 字节串 -> 字符串

小技巧:处理串口数据时,我建议一律用字节串。字符串在编码解码时容易出乱码,尤其是遇到非ASCII字符时。我在一个GPS解析项目里就吃过这个亏,后来全改成字节串操作,问题迎刃而解。

列表与元组

列表可变,元组不可变。这个大家都知道。但嵌入式里,我推荐多用元组。为什么?因为元组可以作为字典的键,列表不行。而且元组更轻量,性能略好。

# 列表
sensor_readings = [25.3, 26.1, 24.8]
sensor_readings.append(25.0)  # 可以修改

# 元组
gpio_pins = (0, 1, 2, 3)
# gpio_pins[0] = 5  # 会报错!元组不可修改

# 元组作为字典键
pin_config = {
    (0, 'input'): 'GPIOA',
    (1, 'output'): 'GPIOB'
}

字典与集合

字典在嵌入式脚本里太常用了。配置参数、寄存器映射、设备状态,用字典管理起来特别清晰。

# 字典
device_config = {
    'baudrate': 115200,
    'data_bits': 8,
    'stop_bits': 1,
    'parity': 'none'
}

# 集合(去重利器)
error_codes = {0x01, 0x02, 0x03, 0x01}  # 自动去重,结果是 {0x01, 0x02, 0x03}

3.2 控制流:让代码「活」起来

控制流没什么高深的,就是条件判断和循环。但嵌入式场景下,有些写法更高效。

条件判断

if-elif-else,老生常谈了。但我有个习惯:把最可能成立的条件放在最前面。这样能减少判断次数,在循环里尤其明显。

status = read_device_status()

# 我习惯这样写
if status == 0x00:
    # 正常状态(最常见)
    handle_normal()
elif status == 0xFF:
    # 错误状态
    handle_error()
else:
    # 其他状态
    handle_unknown()

重点:Python没有switch-case语句。如果你有多个条件要判断,可以用字典映射代替长长的if-elif链。代码更简洁,性能也更好。

def handle_cmd_01():
    print("执行命令01")

def handle_cmd_02():
    print("执行命令02")

cmd_handlers = {
    0x01: handle_cmd_01,
    0x02: handle_cmd_02,
}

cmd = 0x01
handler = cmd_handlers.get(cmd)
if handler:
    handler()
else:
    print("未知命令")

循环

for循环和while循环。嵌入式里,for循环用得更多,尤其是遍历数据时。

# 遍历列表
for reading in sensor_readings:
    print(f"温度: {reading}°C")

# 遍历字典
for key, value in device_config.items():
    print(f"{key}: {value}")

# 带索引的遍历
for i, pin in enumerate(gpio_pins):
    print(f"引脚{i}: GPIO{pin}")

注意:在嵌入式脚本里,尽量避免无限循环。如果一定要用while True,记得加个退出条件或超时机制。我曾经写过一个串口监听脚本,忘了加退出条件,结果在服务器上跑了整整一个周末……

3.3 函数定义:把逻辑「封装」起来

函数是代码复用的基础。嵌入式脚本里,我建议把每个独立功能都封装成函数。这样调试起来方便,也容易移植。

基本函数定义

def read_sensor(sensor_id):
    """读取传感器数据"""
    # 模拟读取
    data = 25.0 + sensor_id * 0.1
    return data

def write_register(addr, value):
    """写入寄存器"""
    print(f"写入寄存器 0x{addr:08X}, 值 0x{value:02X}")
    # 实际写入操作...

参数传递

Python的参数传递很灵活。默认参数、关键字参数、可变参数,用好了能省不少事。

# 默认参数
def configure_uart(baudrate=115200, data_bits=8, parity='none'):
    print(f"配置UART: {baudrate} {data_bits} {parity}")

# 关键字参数
configure_uart(parity='even', baudrate=9600)

# 可变参数
def log_messages(*messages):
    for msg in messages:
        print(f"[LOG] {msg}")

log_messages("启动", "初始化完成", "等待命令")

个人经验:写函数时,尽量让每个函数只做一件事。如果一个函数超过20行,我通常会考虑拆分成更小的函数。这样测试起来方便,出错了也容易定位。

返回值

函数可以返回多个值,实际上返回的是一个元组。

def read_device_info():
    """读取设备信息"""
    device_id = 0x1234
    firmware_version = "2.1.0"
    status = 0x00
    return device_id, firmware_version, status

# 解包
dev_id, fw_ver, st = read_device_info()
print(f"设备ID: 0x{dev_id:04X}, 固件版本: {fw_ver}")

3.4 文件操作:读写配置与日志

嵌入式脚本里,文件操作主要用来读写配置文件、保存日志、导出数据。Python的文件操作很简单,但有几个细节要注意。

打开与关闭文件

我强烈推荐使用with语句。它能自动关闭文件,省心省力。

# 推荐写法
with open('config.ini', 'r') as f:
    content = f.read()
    print(content)
# 文件自动关闭

# 不推荐写法
f = open('config.ini', 'r')
content = f.read()
f.close()  # 容易忘记

读写模式

模式 说明 嵌入式常用场景
'r' 只读 读取配置文件
'w' 写入(覆盖) 保存新配置
'a' 追加 写入日志
'rb' 二进制只读 读取固件文件
'wb' 二进制写入 写入固件文件

逐行读取

处理日志文件时,逐行读取比一次性读取更省内存。

# 逐行读取日志
with open('system.log', 'r') as f:
    for line in f:
        if 'ERROR' in line:
            print(f"发现错误: {line.strip()}")

写入CSV文件

嵌入式测试中,经常需要把数据导出成CSV格式,方便用Excel分析。

import csv

# 写入CSV
data = [
    ['时间', '温度', '湿度'],
    ['2024-01-01 10:00', 25.3, 60],
    ['2024-01-01 10:01', 25.5, 61],
]

with open('sensor_data.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerows(data)

核心要点:文件操作时,记得处理异常。嵌入式环境里,文件可能被占用、磁盘可能满、路径可能不存在。用try-except包一下,能避免脚本意外崩溃。

try:
    with open('config.ini', 'r') as f:
        config = f.read()
except FileNotFoundError:
    print("配置文件不存在,使用默认配置")
    config = default_config
except PermissionError:
    print("没有权限读取配置文件")
    exit(1)

好了,Python基础就回顾到这里。这些内容看起来简单,但都是嵌入式脚本开发的基石。下一章咱们会把这些知识串起来,写一个完整的自动化测试脚本。到时候你会发现,这些基础点一个都跑不掉。