🍓 第六章 开发 Python 程序
📄 一句话概括

前面五节的数据都是"一个一个的"(一个数、一段文字)。本节开始处理"一串一串的"——一组任务、一句话、一批传感器读数。 两样东西贯穿整节:列表(一串可以改的东西)和字符串(一串不能改的文字)。它们共享同一套"下标与切片"规则,学一次两边通用。

📝 本节和前面几节的关系

6-3 学过一个字符串 str,当时只用了它的"拼接"和 f-string;本节把它的下标、切片、方法全展开。 6-4 学的 for 循环,当时只能 range() 数数字;本节给它真正的用武之地——遍历一串真实的数据(for task in todos:)。 6-5 学的函数,本节会把处理数据的逻辑包成函数。 读法建议:本节是本章最后一节,也是以后所有硬件实验的地基——传感器读回来的永远是一串数据,不是单个数字。

一、先建立一个概念:序列

Python 里有一类东西叫序列——它是"排好队的一串值",每个值都有一个位置编号(下标)。

本节讲三种:

类型 写法 能改吗 什么时候用
列表 list [1, 2, 3] 能改 一堆会变的数据(待办事项、传感器读数)
字符串 str "abc" 不能改 一段文字
元组 tuple (1, 2, 3) 不能改 一组固定的值(坐标、配置)
💡 记住这一句,本节就通了一半

三种都是"一排格子,每个格子有编号"。 下标(从第几个)和切片(取连续的一段)的规则,三种完全一样。 唯一的区别是:列表能改内容,字符串和元组不能。

二、下标:从 0 开始数

fruits = ["苹果", "香蕉", "橙子", "葡萄"]

想象成这样一排带编号的格子:

下标 0 1 2 3
值 苹果 香蕉 橙子 葡萄
负下标 -4 -3 -2 -1
>>> fruits[0]
'苹果'
>>> fruits[3]
'葡萄'
>>> fruits[-1]          # 负数从右边数:-1 是最后一个
'葡萄'
>>> fruits[-2]
'橙子'
💡 `-1` 是"最后一个",超级常用

在 C++ 里想拿到最后一个元素要写 arr[arr.size() - 1](还得判断空不空);Python 直接 fruits[-1]。 这个写法你以后会天天用——因为"最新的一条记录""最后读到的那个值"总是最后一个。

越界会报错(Python 不会像 C 那样"悄悄读后面的内存",这是它安全的地方):

>>> fruits[10]
IndexError: list index out of range

三、切片:一次取一串

规则:[起点:终点:步长]。

最重要的一条(和 range 完全一致):含头不含尾。

>>> nums = [10, 20, 30, 40, 50]

>>> nums[1:4]        # 下标 1、2、3(不含 4)
[20, 30, 40]

>>> nums[:3]         # 省略起点 = 从头开始
[10, 20, 30]

>>> nums[2:]         # 省略终点 = 一直到末尾
[30, 40, 50]

>>> nums[:]          # 全都要(可以理解成复制一份)
[10, 20, 30, 40, 50]

>>> nums[::2]        # 从头到尾,每隔一个取一个
[10, 30, 50]

>>> nums[::-1]       # 步长 -1 = 倒着取 → 反转!
[50, 40, 30, 20, 10]
💡 `[::-1]` 是 Python 的招牌写法

一行把一串东西倒过来,别的语言要写个循环。记住它,以后会经常用到("最新的消息排最前面")。

验证一下为什么是 [::-1]:起点省略(从头)、终点省略(到尾)、步长 -1(每次往左走一步)——走出来的顺序自然就是反的。

切片最实用的用途:提取一段。 比如从日期字符串里取年份:

>>> date = "2026-09-24"
>>> date[:4]          # 前四个字符
'2026'
>>> date[5:7]         # 第 5、6 个(跳过 '2026-' 的横线?不,看下面)
'09'
>>> date.split("-")   # 更好的办法:直接切开
['2026', '09', '24']
📝 `date[5:7]` 为什么是月份

2026-09-24 的下标是:2(0) 0(1) 2(2) 6(3) -(4) 0(5) 9(6) -(7) 2(8) 4(9)。 所以 [5:7] 取的是下标 5、6 两个字符 → "09" ✅ 但要小心:这种"数格子"的写法很脆(换个格式就崩)。格式规整的数据,优先用 split() 切开(下面讲)。

四、列表:能改的一串

4.1 创建、查看、修改、增删

todos = ["买菜", "洗衣服"]           # 创建

todos[0] = "买菜和水果"               # 修改(列表能改)
print(todos)                         # ['买菜和水果', '洗衣服']

todos.append("遛狗")                  # 末尾追加(最常用)
print(todos)                         # ['买菜和水果', '洗衣服', '遛狗']

todos.insert(0, "起床")               # 插到指定位置
print(todos)                         # ['起床', '买菜和水果', '洗衣服', '遛狗']

todos.remove("洗衣服")                # 按内容删(删掉第一个匹配的)
print(todos)                         # ['起床', '买菜和水果', '遛狗']

last = todos.pop()                    # 弹出最后一个(并把它交给你)
print(last)                          # 遛狗
print(todos)                         # ['起床', '买菜和水果']

print(len(todos))                    # 2 —— 数个数
print(todos.count("起床"))            # 1 —— 数某个值出现几次
print("起床" in todos)                # True —— 在不在里面

最常用的六个方法(先记这四个就行):

方法 作用 例子
.append(x) 末尾追加一个 todos.append("倒垃圾")
.remove(x) 按内容删掉一个 todos.remove("起床")
.pop() 弹出末尾(或指定位置) last = todos.pop()
.sort() 排序(原地改) nums.sort()
.reverse() 反转(原地改) nums.reverse()
.clear() 清空 todos.clear()

另外三个"函数"形式(不是方法,写在括号外面):

函数 作用 例子
len(x) 有几个元素 len(todos)
sorted(x) 排序(返回新的,不动原列表) sorted(nums)
sum(x) 求和(元素都是数字时) sum([1, 2, 3]) → 6
⚠️ ⚠️ 本节最容易踩的坑:`.append()` 返回的是 `None`
>>> todos = ["买菜"]
>>> todos = todos.append("洗衣服")     # ❌ 想"更新一下"
>>> print(todos)
None                                   # 列表没了!

为什么:.append() 是"原地修改"——它直接改 todos 本身,不返回任何东西(返回 None)。 你把它的返回值(None)赋给了 todos,原来的列表就被覆盖掉了。

正确写法(不加 =):

todos.append("洗衣服")                 # ✅ 直接调用,不要赋值

规律:"原地改"的列表方法(append / remove / sort / reverse / clear)一律返回 None。 相反,sorted()(函数形式)返回新列表——名字带 ed 的通常是"返回新的",不带的是"原地改的",这个规律很实用。

4.2 遍历列表(for 的真正用武之地)

todos = ["起床", "买菜", "做饭"]

for task in todos:
    print(f"○ {task}")

输出:

○ 起床
○ 买菜
○ 做饭

想要序号怎么办?用 enumerate():

for i, task in enumerate(todos, 1):       # 第二个参数 1 表示"从 1 开始编号"
    print(f"{i}. {task}")

输出:

1. 起床
2. 买菜
3. 做饭
💡 `enumerate` 是什么

它的意思是"边遍历边报号"——每轮给你两个东西:序号 和 元素本身。 左边写两个变量接住(i, task),就是 6-5 学的解包。 默认序号从 0 开始,加个 1 就变成从 1 开始——这才是给人看的编号。

4.3 .sort() 的一个小脾气

nums = [30, 5, 100, 20]
nums.sort()
print(nums)              # [5, 20, 30, 100]  ✅ 数字会按大小排

words = ["banana", "Apple", "cherry"]
words.sort()
print(words)             # ['Apple', 'banana', 'cherry']  ← 大写排前面

字符串排序是按"字符编码"排的,所以大写字母全部排在小写字母前面(A=65,a=97)。别以为它坏了——想忽略大小写写 words.sort(key=str.lower)。

倒序:nums.sort(reverse=True)。

4.4 元组:不能改的列表

point = (3, 5)               # 元组用小括号
x, y = point                 # 解包
print(x, y)                  # 3 5

point[0] = 10                # ❌ TypeError: 'tuple' object does not support item assignment

什么时候用元组:当这组值本来就不该被改(坐标、RGB 颜色、一条数据库记录)。用元组是一种"声明":告诉读代码的人"这是固定的"。

五、字符串:不能改的一串字符

5.1 字符串也有下标和切片(规则完全一样)

>>> s = "Raspberry"
>>> s[0]
'R'
>>> s[-1]
'y'
>>> s[:9]
'Raspberry'
>>> s[::-1]
'yrettopsaR'          # 反转

5.2 字符串不能改(这是它和列表的根本区别)

>>> s = "hello"
>>> s[0] = "H"
TypeError: 'str' object does not support item assignment

想改怎么办?造一个新的:

>>> s = "hello"
>>> s = "H" + s[1:]          # 拼一个新的,赋回给 s
>>> s
'Hello'
📝 为什么 Python 要这样设计

因为字符串用得太多、改得太少。让"不能改"的东西存在,Python 才能放心地把它共享给多处使用(省内存、也更安全)。 记住这条规矩:字符串的所有"修改类"方法,都是返回一个新字符串,原字符串不动。

>>> s = "  hello  "
>>> s.strip()          # 去掉两头空格
'hello'
>>> s                  # 原字符串没变!
'  hello  '

想生效就必须接住返回值:s = s.strip()。这一条和上一节那个 .append() 的坑刚好相反——一个是"必须接",一个是"千万别接",特别容易搞混。

5.3 七个最常用的字符串方法

s = "  Hello, Raspberry Pi  "

s.strip()                  # 'Hello, Raspberry Pi'   ← 去掉两头空白(最常用!)
s.strip().lower()          # 'hello, raspberry pi'   ← 全变小写
s.strip().upper()          # 'HELLO, RASPBERRY PI'   ← 全变大写
s.strip().replace("Pi", "树莓派")   # 'Hello, Raspberry 树莓派'  ← 替换(全部替换)
s.strip().find("Rasp")     # 7   ← 找子串的位置,找不到返回 -1
s.strip().startswith("He") # True
s.strip().split(",")       # ['Hello', ' Raspberry Pi']  ← 按分隔符切成列表
💡 `.strip()` 是配合 `input()` 的必备动作

用户在输入框里手一滑多加了个空格,是最常见的数据污染:

answer = input("继续吗?(y/n) ").strip().lower()
if answer == "y":
    ...

这一行里塞了两个"清洗"动作:去掉两头空白、统一成小写。这样用户敲 Y、y、y、Y 都能通过。 养成习惯:input() 拿到的东西,用之前先 strip() 一下。

5.4 split() 和 join():字符串与列表之间的双向门

>>> line = "苹果,香蕉,橙子"
>>> parts = line.split(",")          # 字符串 → 列表
>>> parts
['苹果', '香蕉', '橙子']

>>> "-".join(parts)                  # 列表 → 字符串
'苹果-香蕉-橙子'

>>> " ".join(["你好", "世界"])        # 用空格拼
'你好 世界'
⚠️ `join` 的写法很反直觉,但必须记

是"分隔符".join(列表),不是"列表".join(分隔符)。 一开始都觉得别扭(凭什么分隔符写前面),但记住它的固定形态就行:

"分隔符".join(列表名)

这个写法在硬件项目里超常用——把一串传感器读数拼成一行日志就靠它。

5.5 好消息:Python 处理中文不用怕

这一点是 Python 相对 C++ 的一大优势,值得单独说。

在 字符数组 里踩过一个坑:UTF-8 下一个汉字占 3 个字节,用 substr 按字节切会把汉字切成两半、输出乱码。

在 Python 里完全没有这个问题:

>>> s = "你好树莓派"
>>> len(s)
5                      # ← 是 5 个"字",不是 15 个字节!
>>> s[2]
'树'
>>> s[0:2]
'你好'
>>> list(s)
['你', '好', '树', '莓', '派']
💡 为什么 Python 能这样

Python 3 的字符串是按"字符"算的,不是按字节算的——编码转换(UTF-8 ↔ 字符)Python 在背后悄悄处理掉了。 所以:以后处理中文文本,用 Python 就是对的。 想按字切、想数有几个字、想反转中文句子(s[::-1] 得到 派莓树好你),都不会乱码。 这一条你以后会感恩很久——写处理文本的小工具,Python 能省掉一半的坑。

六、动手 Demo:一个真正的待办清单

把本节所有东西串起来。在命令行里写:

cd ~/projects
nano todo.py

内容:

todos = []                                  # 空列表:一开始什么事都没有

print("=== 待办清单 ===")
print("一行一条,直接回车就结束。\n")

while True:
    task = input("任务:").strip()           # 先清洗(去掉两头空格)
    if not task:                            # 空字符串 → 结束录入
        break
    todos.append(task)                      # 追加到列表

if not todos:                               # 一条都没录
    print("\n什么都没录,走了。")
else:
    todos.sort()                            # 排个序,看着舒服
    print(f"\n你一共列了 {len(todos)} 件事:")
    for i, task in enumerate(todos, 1):     # 带序号遍历
        print(f"  {i}. {task}")

    print("\n最长的一条是:", max(todos, key=len))
    print("平均每条", round(sum(len(t) for t in todos) / len(todos), 1), "个字")

跑:

python3 todo.py

输入三件事,看到什么算成功:

=== 待办清单 ===
一行一条,直接回车就结束。

任务:买菜
任务:遛狗
任务:交电费
任务:

你一共列了 3 件事:
  1. 交电费
  2. 买菜
  3. 遛狗

最长的一条是: 交电费
平均每条 2.3 个字

读结果 —— 逐条对一遍:

输出 为什么
排序后是 交电费 / 买菜 / 遛狗 todos.sort() 按字符编码排;交(U+4EA4) < 买(U+4E70) < 遛(U+905B) ✅
序号从 1 开始 enumerate(todos, 1) 那个 1 的作用
最长的是 交电费 交电费 是 3 个字,另外两条都是 2 个字,所以它最长(没有并列)
平均 2.3 3 + 2 + 2 = 7 字,7 ÷ 3 = 2.333…,round(..., 1) 得 2.3 ✅

这个 Demo 用到了本章几乎所有知识点:

代码 来自哪一节
input().strip() 6-6(方法链)、6-3(input 是字符串)
if not task: break 6-4(空字符串是假、break)
todos.append(task) 6-6(注意:没接返回值,对的)
todos.sort() 6-6
len(todos)、enumerate、max(..., key=len) 6-6
f" {i}. {task}" 6-3(f-string)
round(...) 6-3
📝 `sum(len(t) for t in todos)` 这一句是进阶写法

意思是"把每个任务的字符数加起来"——这行用了"生成器表达式",本章不展开,你看得懂它在干什么就够了(本课后面讲到数据处理时会正式遇到)。写不出来不影响你读懂别人的代码。

七、30 秒实验

实验一:三种序列,规则完全一样

>>> lst = [10, 20, 30, 40]
>>> s = "abcd"
>>> t = (10, 20, 30, 40)
>>> lst[1], s[1], t[1]
(20, 'b', 20)
>>> lst[-1], s[-1], t[-1]
(40, 'd', 40)
>>> lst[1:3], s[1:3], t[1:3]
([20, 30], 'bc', (20, 30))
>>> lst[::-1], s[::-1], t[::-1]
([40, 30, 20, 10], 'dcba', (40, 30, 20, 10))

下标、负下标、切片、反转——三种一模一样。 学一套,用三处。

实验二:中文长度,直观对比

>>> s = "你好树莓派"
>>> len(s)
5                 # Python 数的是"字"
>>> s[2]
'树'
>>> s[::-1]
'派莓树好你'       # 中文也能干净地反转

对照 C++ 的印象:如果按 UTF-8 字节算,这串是 15 字节,s[2] 会取到"好"字的中间那一个字节 → 乱码。Python 按字符算,所以不会。

实验三:字符串方法返回新值,原串不动

>>> s = "  hello  "
>>> s.strip()
'hello'
>>> s
'  hello  '          # ← 原串纹丝不动
>>> s = s.strip()    # 想生效,必须接回来
>>> s
'hello'

顺便对比列表:

>>> a = [3, 1]
>>> a.sort()         # 原地改,别接返回值
>>> a
[1, 3]

一个必须"接"、一个千万别"接"——这就是本节最容易混的一对。

实验四:split / join 双向门

>>> "a,b,c".split(",")
['a', 'b', 'c']
>>> ",".join(['a', 'b', 'c'])
'a,b,c'
>>> "".join(['你', '好'])
'你好'                 # 空分隔符 = 直接粘起来

八、常见误区

  1. "a = a.append(x) 能更新列表" —— 不能,而且会把列表变成 None。原地修改的方法返回 None,直接调用就好,别接返回值。
  2. "字符串也能像列表那样改" —— 不能。字符串不可变,所有方法都返回新值。想生效必须 s = s.strip() 这样接住(和上一条刚好相反,最容易混)。
  3. "切片 [1:4] 会取到下标 4" —— 不会,含头不含尾,取的是 1、2、3。这条规则和 range 完全一致,记一次通用。
  4. "len("你好") 是 6" —— 那是字节数(C++ 的算法)。Python 返回 2(字符数)。Python 处理中文不会乱码,这是它相对 C++ 的一大优势。
  5. "排序把大写和小写混着排" —— 不是。默认按字符编码排,大写字母(65~90)全在小写(97~122)前面,所以 Apple 会排在 banana 前面。要忽略大小写:sort(key=str.lower)。
  6. """.join(列表) 能用,那 列表.join("") 应该也行" —— 反了。join 是字符串的方法,不是列表的。只有一种写法:"分隔符".join(列表)(分隔符写前面、join 挂在它身上)。写成 列表.join(...) 会报 AttributeError: 'list' object has no attribute 'join'。
  7. "input() 拿到的就是干净的内容" —— 可能带两头空格。永远记得 .strip(),比较文字时再顺手 .lower()("用户输入不可信"是这一章反复出现的主题)。
自测三问(点开对答案) 1. `[10, 20, 30, 40, 50][1:4]` 得到什么?为什么? 2. `todos.append("买菜")` 和 `todos = todos.append("买菜")`,哪个对?为什么? 3. `len("你好树莓派")` 在 Python 里返回几?如果在 C++ 里按 UTF-8 算会是几? **参考答案** 1. **`[20, 30, 40]`**。切片是**含头不含尾**:从下标 1 开始、到下标 4 之前停,所以取 1、2、3 三个。**这条规则和 `range(1, 4)` 完全一致。** 2. **第一种对**。`.append()` 是**原地修改**,直接改列表本身,**返回 `None`**——第二种写法等于把 `None` 赋给了 `todos`,**整个列表就没了**。(规律:`append`/`remove`/`sort`/`reverse`/`clear` 这类"原地改"的方法,**一律别接返回值**。) 3. **Python 返回 5**(数的是字符);**按 UTF-8 字节算会是 15**(一个汉字 3 字节)。**这正是 Python 处理中文不乱码的原因——它按字符索引,不按字节。**
📄 第六章读完了,回头看这条线

6-1 认识它 → 6-2 会在机器上写它 → 6-3 给它数据 → 6-4 给它判断力和耐力 → 6-5 给它结构 → 6-6 给它批量处理数据的能力。 走完这一章,你手上终于有了在树莓派上"自己造东西"的工具。 这也是本课的一个转折点:前面五章你一直在"把机器搭起来、把服务装上去";从这一章起,你可以开始写自己的程序了。

相关笔记