3. Python基础回顾(上):数据类型与结构、函数与作用域、文件I/O操作

各位同学,欢迎来到第三章。我知道你们可能觉得Python基础没什么好讲的,但请相信我——在边缘AI这个领域,很多坑恰恰就埋在这些基础里。我见过太多人在模型部署时因为数据类型搞错,或者文件读取路径写错,白白浪费一整天。

今天咱们就快速过一遍,重点放在那些你真正会用到的部分。尤其是图片文件读取,这是咱们后续所有视觉模型的基础。

3.1 数据类型与结构:list、dict、set

Python的数据结构,说白了就是装数据的容器。在边缘AI开发中,最常用的就是list、dict和set。我个人的习惯是:能用dict就别用list,能用set就别用list——为什么?往下看。

3.1.1 list(列表)

list是最基础的数据结构。它有序、可重复、可修改。在模型推理时,我们经常用它来存储批量数据。

# 一个典型的list用法
image_paths = ['img_001.jpg', 'img_002.jpg', 'img_003.jpg']
# 添加元素
image_paths.append('img_004.jpg')
# 切片操作
first_three = image_paths[:3]
我的经验:在读取大量图片时,我习惯先把所有路径存到一个list里,再批量处理。这样方便调试,也方便做数据增强。

3.1.2 dict(字典)

dict是Python里最强大的数据结构,没有之一。它用键值对存储数据,查找速度极快。在模型配置、参数传递中,dict无处不在。

# 模型配置字典
model_config = {
    'input_shape': (224, 224, 3),
    'num_classes': 10,
    'learning_rate': 0.001,
    'batch_size': 32
}
# 访问配置
print(model_config['input_shape'])  # 输出 (224, 224, 3)
注意:dict的键必须是不可变类型(如字符串、数字、元组)。我曾经在项目中用list做键,结果程序直接报错——嗯,这个坑我替你们踩过了。

3.1.3 set(集合)

set是一个无序、不重复的元素集合。它的最大用途是去重和集合运算。在数据预处理中,我经常用它来检查标签是否有重复。

# 检查标签是否唯一
labels = ['cat', 'dog', 'cat', 'bird', 'dog']
unique_labels = set(labels)
print(unique_labels)  # 输出 {'cat', 'dog', 'bird'}
# 集合运算
train_labels = {'cat', 'dog', 'bird'}
test_labels = {'cat', 'fish'}
common = train_labels & test_labels  # 交集
print(common)  # 输出 {'cat'}

核心要点:

  • list:有序、可重复、可修改,适合存储序列数据
  • dict:键值对、查找快,适合配置和映射
  • set:无序、不重复,适合去重和集合运算

3.2 函数与作用域

函数是代码复用的基本单元。在边缘AI开发中,我们经常把数据预处理、模型推理封装成函数。这里我重点讲两个容易出错的地方:作用域和默认参数。

3.2.1 函数定义与调用

def preprocess_image(image_path, target_size=(224, 224)):
    """图片预处理函数"""
    # 读取图片
    img = read_image(image_path)
    # 调整大小
    img = resize_image(img, target_size)
    # 归一化
    img = img / 255.0
    return img

# 调用函数
processed_img = preprocess_image('test.jpg')

3.2.2 作用域:局部 vs 全局

Python的作用域规则其实很简单:函数内部定义的变量是局部变量,函数外部定义的是全局变量。但有一个坑——如果你在函数内部修改全局变量,必须用global关键字声明。

# 全局变量
model = None

def load_model():
    global model  # 声明要修改全局变量
    model = load_weights('model.h5')

def predict(image):
    # 这里只是读取全局变量,不需要global
    result = model.predict(image)
    return result
我曾经踩过的坑:在函数内部忘记加global,结果创建了一个同名局部变量,全局变量根本没变。调试了半小时才发现——嗯,这种低级错误,犯一次就记住了。

3.2.3 默认参数的陷阱

Python的默认参数只在函数定义时计算一次。如果默认参数是可变对象(如list、dict),就会出问题。

# 错误的写法
def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item(1))  # 输出 [1]
print(add_item(2))  # 输出 [1, 2]  —— 这里出问题了!

# 正确的写法
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items
我的建议:默认参数永远用不可变类型(None、数字、字符串)。如果必须用可变类型,在函数内部初始化。

3.3 文件I/O操作(重点:图片文件读取)

文件读写是边缘AI开发的基础。你想想看,模型训练需要读数据,模型推理需要读图片,模型部署需要写日志——几乎每一步都离不开文件操作。

3.3.1 文本文件读写

# 写入文件
with open('labels.txt', 'w') as f:
    f.write('cat\n')
    f.write('dog\n')

# 读取文件
with open('labels.txt', 'r') as f:
    lines = f.readlines()
    print(lines)  # 输出 ['cat\n', 'dog\n']

关键点:使用with语句可以自动关闭文件,避免资源泄漏。我见过太多人忘记f.close(),导致文件句柄耗尽——用with就永远不会出这个问题。

3.3.2 图片文件读取(重点)

图片读取是咱们课程的核心。在边缘AI中,常用的图片读取方式有三种:OpenCV、PIL、以及TensorFlow/PyTorch自带的读取方法。我个人的习惯是:开发调试用OpenCV,部署时用PIL(因为依赖更少)。

方法一:使用OpenCV

import cv2

# 读取图片
img = cv2.imread('image.jpg')
# OpenCV默认读取BGR格式,需要转成RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 获取图片信息
height, width, channels = img.shape
print(f'图片尺寸: {width}x{height}, 通道数: {channels}')

方法二:使用PIL(Pillow)

from PIL import Image

# 读取图片
img = Image.open('image.jpg')
# 转换为RGB(确保一致性)
img_rgb = img.convert('RGB')
# 调整大小
img_resized = img_rgb.resize((224, 224))
# 转换为numpy数组
import numpy as np
img_array = np.array(img_resized)

方法三:使用TensorFlow

import tensorflow as tf

# 读取并解码图片
img_raw = tf.io.read_file('image.jpg')
img = tf.image.decode_jpeg(img_raw, channels=3)
# 调整大小
img_resized = tf.image.resize(img, [224, 224])
# 归一化
img_normalized = img_resized / 255.0
避坑指南:
  • OpenCV读取的是BGR格式,PIL读取的是RGB格式——这个区别我至少踩过5次坑
  • 图片路径不要用中文,不要有空格——在嵌入式设备上会出各种奇怪问题
  • 批量读取时,注意内存占用。一张224x224的RGB图片约150KB,1000张就是150MB

3.3.3 二进制文件读写

模型权重文件通常是二进制格式。在边缘AI部署时,我们经常需要读取.bin或.h5文件。

# 读取二进制文件
with open('model_weights.bin', 'rb') as f:
    data = f.read()

# 写入二进制文件
with open('output.bin', 'wb') as f:
    f.write(data)
注意:二进制文件读写一定要用'rb''wb'模式。用文本模式读写二进制文件会导致数据损坏——我曾经因为这个原因,模型加载一直报错,查了整整一天才发现是模式写错了。

3.4 本章小结

好了,这一章的内容就到这里。咱们回顾一下重点:

  • 数据结构:list、dict、set各有用途,选对数据结构能让代码效率翻倍
  • 函数与作用域:注意全局变量和默认参数的陷阱,这些是新手最容易犯错的地方
  • 文件I/O:图片读取是重点,记住OpenCV和PIL的区别,用with语句管理文件

下一章我们会继续讲Python基础——面向对象编程和异常处理。这些在构建大型AI系统时非常重要。咱们下节课见。