上篇把Python基础语法过了一遍。今天深入聊聊数据结构——list、dict、tuple、set这四个内置容器的选型。为什么要专门聊选型因为在C里你选vector还是map考虑的是底层实现——连续内存还是红黑树。Python里也一样只是关注点不太一样。先说个面试场景。面试官问假设你要存一批传感器数据每个数据有ID、时间戳和数值你会用什么数据结构有个候选人想都没想用list。面试官追问如果要根据ID快速查找某条数据呢候选人遍历list找ID匹配的。面试官如果有一万条数据呢候选人沉默了。这就是数据结构选型的重要性。选错了容器算法复杂度可能差几个数量级。list最通用的容器Python的list对应C的vector底层是动态数组。它的特点是有序、可变、支持索引和切片。适合的场景需要保持插入顺序、需要按索引访问、数据量不大或者操作以遍历为主。在机器人开发中list最常见的用途是存储传感器读数序列、轨迹点列表、日志记录等。# 存储最近10帧的激光雷达距离 recent_distances [] for scan in lidar_scans: recent_distances.append(scan.avg_distance) if len(recent_distances) 10: recent_distances.pop(0)注意list的头部插入和删除是O(n)的因为后面的元素都要移动。如果你需要频繁的头部操作应该用collections.deque它是双端链表实现两端操作都是O(1)。from collections import deque buffer deque(maxlen100) # 固定长度的缓冲区 buffer.appendleft(new_data) # O(1)这个在机器人数据处理中特别有用——滑动窗口、FIFO缓冲区用deque比list高效得多。dict键值查找的利器dict是Python里最重要的数据结构之一底层是哈希表查找、插入、删除平均都是O(1)。适合的场景需要根据键快速查找、数据有明确的键值关系、不需要保持顺序Python 3.7的dict保持插入顺序但别依赖这个特性做算法设计。# 传感器配置 sensor_params { lidar: {max_range: 30.0, min_range: 0.1}, camera: {width: 640, height: 480}, imu: {sample_rate: 200} } # O(1)查找 lidar_max_range sensor_params[lidar][max_range]面试中常问的一个点dict的key必须是不可变类型immutable。字符串、数字、tuple都可以做key但list不行。因为dict底层是哈希表key的哈希值必须稳定。还有一个容易忽略的细节dict.get()和直接用[]访问的区别。d[key]在key不存在时会抛KeyErrord.get(key, default)会返回默认值。在不确定key是否存在的场景下用get更安全# 不安全 max_range sensor_params[lidar_2][max_range] # KeyError! # 安全 max_range sensor_params.get(lidar_2, {}).get(max_range, 30.0)tuple不可变的价值tuple看起来就是一个不可变的list好像没什么特别的。但它的不可变性带来了两个重要特性。第一可以做dict的key。比如用坐标(x, y)做键grid_map { (0, 0): free, (0, 1): occupied, (1, 0): unknown }第二函数返回多个值时实际上返回的就是tuple。Python的解包语法让多返回值用起来很自然def get_pose(): return 1.0, 2.0, 0.5 # x, y, theta x, y, theta get_pose()还有一个性能上的优势tuple比list占用更少的内存创建速度也更快。如果你的数据确实不需要修改用tuple是更好的选择。Python内部还会缓存小的tuple对象进一步减少内存分配。set去重和集合运算set底层也是哈希表和dict类似但只存key不存value。查找O(1)自动去重。适合的场景成员检查比list快得多、去重、集合运算交并差。# 成员检查 known_objects {cup, bottle, keyboard} if detected_name in known_objects: grasp(detected_name) # 去重 unique_ids list(set(all_ids)) # 集合运算 detected {cup, bottle, phone} known {cup, bottle, keyboard} new_objects detected - known # {phone} common detected known # {cup, bottle}在机器人开发中set常用于已知物体的类别集合、已探索的区域ID集合、需要排除的障碍物ID等。顺便提一下collections模块里的两个实用工具。defaultdict——访问不存在的key时自动创建默认值不用每次判断key是否存在from collections import defaultdict sensor_readings defaultdict(list) sensor_readings[lidar].append(3.5) # key不存在时自动创建空listCounter——统计元素出现次数做数据分析时特别好用from collections import Counter labels [cup, bottle, cup, phone, cup] counts Counter(labels) # {cup: 3, bottle: 1, phone: 1}选型决策树面试的时候面对一个数据存储的需求可以按这个思路选需要键值查找用dict。需要去重或成员检查用set。数据不需要修改用tuple。其他情况用list。需要频繁的头部插入删除用deque。这个决策过程如果能有条理地讲出来面试官会觉得你的基础很扎实。另外补充一点Python 3.6之后dict的插入顺序保持已经是语言规范的一部分了不再是实现细节可以放心依赖这个特性。给正在准备面试的你数据结构选型在Python面试中出现频率很高因为它直接反映你对语言特性的理解深度。建议你把这四个容器的时间复杂度、适用场景、常见陷阱都理清楚。面试中回答数据结构选型问题时关键是展示你的决策过程。比如为什么用dict而不是list来存储物体识别结果——因为需要按类别名称快速查找dict的O(1)查找比list的O(n)快得多。为什么用set管理已探索区域——因为需要频繁检查某个区域是否已经访问过set的O(1)成员检查是最佳选择。这种有理有据的回答比单纯说我用dict强太多。另外补充一点Python 3.6之后dict的插入顺序保持已经是语言规范的一部分了不再是实现细节可以放心依赖这个特性。建议你动手写一个小项目来练习选型比如用dict做一个物体识别的结果缓存用set管理已探索的区域ID用deque处理传感器数据的滑动窗口。实际用过之后对各个容器的优缺点会有更深的理解。下篇聊Python的面向对象——和C的对比理解。Python的类和C的类有很多不同搞清楚这些差异面试的时候才能答得准确。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第67篇 Python基础语法速览——给C程序员的快速入门 下一篇预告第69篇 Python面向对象——和C的对比理解有任何问题欢迎评论区留言我会尽量回复。