Files
coding-mentor-agent/kb/python-course-kb-practical-python/wiki/summaries/04_Sequences.md
T

8.4 KiB
Raw Blame History

doc_type, full_text
doc_type full_text
short sources/04_Sequences.md

04_Sequences 总结

本文介绍 Python 中的Python序列及其常见操作,包括字符串、列表、元组、切片、循环遍历、range()、enumerate()、元组解包与 zip()。重点是如何用更 Pythonic 的方式处理有序数据,尤其是在 CSV 数据处理中利用表头构造字典,从而写出更通用的程序。

序列数据类型

Python 有三种常见的序列类型:

  • 字符串:如 'Hello',是字符序列。
  • 列表:如 [1, 4, 5]。
  • 元组:如 ('GOOG', 100, 490.1)。

所有序列都具有以下共同特征:

  • 有序。
  • 可用整数索引访问元素。
  • 可用 len() 获取长度。
  • 支持负索引,例如 b[-1] 表示最后一个元素。

序列还支持复制与连接:

  • s * n:复制序列。
  • s + t:连接同类型序列。

需要注意,序列连接要求两边类型相同。例如元组只能与元组连接,不能直接与列表连接。

切片

Python切片用于从序列中提取子序列,语法为:

s[start:end]

关键规则:

  • start 和 end 是整数索引。
  • 切片包含 start,不包含 end,类似数学中的半开区间。
  • 省略 start 时默认为序列开头。
  • 省略 end 时默认为序列结尾。

示例:

a = [0,1,2,3,4,5,6,7,8]

a[2:5]    # [2,3,4]
a[-5:]    # [4,5,6,7,8]
a[:3]     # [0,1,2]

列表的切片赋值与删除

列表支持对切片重新赋值或删除:

a = [0,1,2,3,4,5,6,7,8]
a[2:4] = [10,11,12]

切片赋值不要求新旧片段长度相同,因此可以改变列表长度。

删除切片:

del a[2:4]

这体现了列表作为可变序列的特点,而字符串和元组则不可变。

序列归约操作

一些内置函数可以把序列归约为单个值:

  • sum(s):求和。
  • min(s):最小值。
  • max(s):最大值。

这些函数不仅可用于数字列表,也可用于字符串列表等可比较对象。例如字符串会按字典序比较。

遍历序列

for 循环可以直接遍历序列中的元素:

for x in s:
    ...

每次迭代时,当前元素会赋给迭代变量。循环结束后,迭代变量仍保留最后一次的值。

本文强调:如果只是遍历元素,应直接使用 for x in data,不要写成:

for n in range(len(data)):
    print(data[n])

这种写法不够 Pythonic,效率较低,也更难阅读。若需要索引,应使用 enumerate()。

break 与 continue

break 用于提前退出循环:

for name in namelist:
    if name == 'Jake':
        break

break 只退出最内层循环。

continue 用于跳过当前元素,进入下一次迭代:

for line in lines:
    if line == '\n':
        continue

这常用于忽略空行、无效数据或不需要处理的元素。

range():整数序列迭代

如果需要计数,应使用 range():

for i in range(100):
    ...

语法:

range([start,] end [,step])

规则:

  • end 不包含在结果中,与切片规则一致。
  • start 可选,默认是 0。
  • step 可选,默认是 1。
  • range() 按需生成值,不会实际存储完整的大范围数字。

示例:

range(100)       # 0 到 99
range(10, 20)    # 10 到 19
range(10, 50, 2) # 10, 12, ..., 48

enumerate():带计数器的遍历

enumerate函数用于在遍历序列时同时获得索引和值:

names = ['Elwood', 'Jake', 'Curtis']
for i, name in enumerate(names):
    ...

通用形式:

enumerate(sequence, start=0)

start 可指定计数起点。典型用途是在读取文件时跟踪行号:

with open(filename) as f:
    for lineno, line in enumerate(f, start=1):
        ...

这比手动维护计数器更简洁,也略快。

多变量迭代与元组解包

如果序列中的元素是元组,可以在 for 循环中直接解包:

points = [(1, 4), (10, 40), (23, 14)]

for x, y in points:
    ...

每个元组会被拆分到多个迭代变量中。变量数量必须与元组元素数量一致。

这属于Python解包的重要应用。

zip():组合多个序列

zip函数用于把多个序列按位置组合成元组迭代器:

columns = ['name', 'shares', 'price']
values = ['GOOG', 100, 490.1]
pairs = zip(columns, values)

得到的逻辑结果类似:

('name', 'GOOG'), ('shares', 100), ('price', 490.1)

zip() 返回迭代器,需要通过循环或 list() 消费。

常见用法是把列名和值组合起来构造字典:

d = dict(zip(columns, values))

这是处理 CSV 数据时非常重要的技巧。

使用 zip() 处理 CSV 表头

在 Data/portfolio.csv 中,第一行是列名:

headers = next(rows)

如果某一行数据为:

row = ['AA', '100', '32.20']

则可使用:

record = dict(zip(headers, row))

得到:

{'name': 'AA', 'shares': '100', 'price': '32.20'}

这样,程序就不再依赖固定列号,而是通过字段名读取数据:

nshares = int(record['shares'])
price = float(record['price'])

这一改动让 portfolio_cost() 能处理不同列顺序、甚至额外包含日期和时间列的 CSV 文件,只要文件中存在所需字段即可。这是从“固定格式解析”走向“基于字段名解析”的重要改进,属于CSV数据处理中的核心技巧。

用 enumerate() 改进错误报告

在处理包含缺失值的 CSV 文件时,可以用 enumerate(rows, start=1) 记录行号,并在转换失败时打印更有用的错误信息:

for rowno, row in enumerate(rows, start=1):
    try:
        ...
    except ValueError:
        print(f'Row {rowno}: Bad row: {row}')

这样可以定位坏数据所在行,例如:

Row 4: Couldn't convert: ['MSFT', '', '51.23']
Row 7: Couldn't convert: ['IBM', '', '70.44']

这体现了concepts/异常处理与迭代工具结合后在数据清洗中的实用价值。

反转字典数据

字典的 items() 返回 (key, value) 对。如果想得到 (value, key) 对,可以结合 values()、keys() 和 zip():

pricelist = list(zip(prices.values(), prices.keys()))

这样可以按价格进行比较、排序或求最大最小值:

min(pricelist)
max(pricelist)
sorted(pricelist)

这也说明了元组比较规则:元组按元素从左到右逐项比较。因此 (price, name) 会优先按价格比较,价格相同再比较名称。

zip() 的更多特性

zip() 不限于两个序列,可以组合任意多个序列:

list(zip(a, b, c))

如果输入序列长度不同,zip() 会在最短序列耗尽时停止:

a = [1, 2, 3, 4, 5, 6]
b = ['x', 'y', 'z']
list(zip(a, b))
# [(1, 'x'), (2, 'y'), (3, 'z')]

练习要点

本文练习围绕以下目标展开:

  1. 使用 range() 进行正向、反向、步进计数。
  2. 使用 min()、max()、sum() 对序列进行归约。
  3. 用普通 for 循环和 enumerate() 遍历数据。
  4. 避免 range(len(data)) 这类低效且不清晰的写法。
  5. 用 enumerate() 在错误提示中加入行号。
  6. 用 zip(headers, row) 构造记录字典,提高 CSV 处理代码的通用性。
  7. 用 zip(values, keys) 生成反转的字典视图,以便按值排序或比较。

核心思想

本文的核心贡献是展示 Python 序列生态中的几种基础但强大的模式:

  • 用切片表达子序列。
  • 用 for 直接遍历数据,而不是模仿 C 风格索引循环。
  • 用 enumerate() 在需要索引时保持代码清晰。
  • 用元组解包简化结构化数据遍历。
  • 用 zip() 把来自不同位置的数据配对,尤其是把 CSV 表头和值组合成字典。

这些技术共同构成了 Python 数据处理的基础风格:更少依赖位置编号,更多依赖清晰的数据结构和可读的迭代模式。