作者: andylin02
学习章节: 第 2 章 丰富的序列
关键词: 容器序列|扁平序列|可变序列|不可变序列|列表推导式|生成器表达式|元组拆包|具名元组|切片|增量赋值|bisect|array|deque
一、本章概述
《流畅的 Python》第 2 章详细讲解了 Python 中丰富的序列类型。Python 从 ABC 语言继承了对序列的统一处理方式——字符串、列表、字节序列、数组、XML 元素和数据库查询结果,这些序列在操作上有很多共通之处,都可以迭代、切片、排序和拼接。深入理解 Python 中不同的序列类型,不但能避免重新发明轮子,还可以从它们共通的接口上受到启发,在自己实现 API 时合理支持及利用现有和将来可能添加的序列类型。
Python 之父 Guido van Rossum 曾为 ABC 语言贡献过代码。在 Python 诞生以前,ABC 语言是一个历时 10 年的研究项目,旨在设计一种适合初学者的编程环境。现在来看,ABC 语言中的很多理念比较符合 Python 风格,例如对各种序列一视同仁、内置元组和映射类型、源码结构通过缩进实现、无须声明变量的强类型等。
二、内置序列类型概览
Python 标准库用 C 语言实现了丰富的序列类型,可以从两个维度进行分类:
2.1 按存储数据类型分类
-
容器序列(container sequences) :存放的是它们所包含的任意类型对象的引用,能存放不同类型的数据。容器序列包括:list、tuple 和 collections.deque。由于存储的是引用,它们对内存的使用相对灵活,但每个元素占用更大的内存空间。
-
扁平序列(flat sequences) :存放的是值,是一段连续的内存空间,只能容纳一种类型。扁平序列包括:str、bytes、bytearray、memoryview 和 array.array。由于元素是连续存储的,扁平序列更紧凑,内存占用更小,但只能存放字符、字节和数值这类基础类型。
# 容器序列示例:存储不同类型的引用
container = [42, "hello", [1, 2, 3]] # list 可以存储各种类型
# 扁平序列示例:存储相同类型的值
flat_array = array.array('I', [1, 2, 3, 4]) # 'I' 代表无符号整数
flat_bytes = b"hello" # 每个元素是 0-255 的整数
# 扁平序列只能存储同类型数据,不能混入文本
2.2 按可变性分类
- 可变序列(mutable sequences) :可以被修改,包括 list、bytearray、array.array、collections.deque 和 memoryview。
- 不可变序列(immutable sequences) :创建后不能被修改,包括 tuple、str 和 bytes。
2.3 序列类型继承层次图
#mermaid-svg-QPqFwXmSxQK2wZW6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QPqFwXmSxQK2wZW6 .error-icon{fill:#552222;}#mermaid-svg-QPqFwXmSxQK2wZW6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QPqFwXmSxQK2wZW6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .marker.cross{stroke:#333333;}#mermaid-svg-QPqFwXmSxQK2wZW6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QPqFwXmSxQK2wZW6 p{margin:0;}#mermaid-svg-QPqFwXmSxQK2wZW6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .cluster-label text{fill:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .cluster-label span{color:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .cluster-label span p{background-color:transparent;}#mermaid-svg-QPqFwXmSxQK2wZW6 .label text,#mermaid-svg-QPqFwXmSxQK2wZW6 span{fill:#333;color:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .node rect,#mermaid-svg-QPqFwXmSxQK2wZW6 .node circle,#mermaid-svg-QPqFwXmSxQK2wZW6 .node ellipse,#mermaid-svg-QPqFwXmSxQK2wZW6 .node polygon,#mermaid-svg-QPqFwXmSxQK2wZW6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .rough-node .label text,#mermaid-svg-QPqFwXmSxQK2wZW6 .node .label text,#mermaid-svg-QPqFwXmSxQK2wZW6 .image-shape .label,#mermaid-svg-QPqFwXmSxQK2wZW6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-QPqFwXmSxQK2wZW6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .rough-node .label,#mermaid-svg-QPqFwXmSxQK2wZW6 .node .label,#mermaid-svg-QPqFwXmSxQK2wZW6 .image-shape .label,#mermaid-svg-QPqFwXmSxQK2wZW6 .icon-shape .label{text-align:center;}#mermaid-svg-QPqFwXmSxQK2wZW6 .node.clickable{cursor:pointer;}#mermaid-svg-QPqFwXmSxQK2wZW6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .arrowheadPath{fill:#333333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QPqFwXmSxQK2wZW6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QPqFwXmSxQK2wZW6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QPqFwXmSxQK2wZW6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QPqFwXmSxQK2wZW6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .cluster text{fill:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 .cluster span{color:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QPqFwXmSxQK2wZW6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QPqFwXmSxQK2wZW6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-QPqFwXmSxQK2wZW6 .icon-shape,#mermaid-svg-QPqFwXmSxQK2wZW6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QPqFwXmSxQK2wZW6 .icon-shape p,#mermaid-svg-QPqFwXmSxQK2wZW6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QPqFwXmSxQK2wZW6 .icon-shape .label rect,#mermaid-svg-QPqFwXmSxQK2wZW6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QPqFwXmSxQK2wZW6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QPqFwXmSxQK2wZW6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QPqFwXmSxQK2wZW6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
扁平序列
容器序列
可变序列
不可变序列
抽象基类层
collections.abc.Sequence不可变序列协议
collections.abc.MutableSequence可变序列协议
tuple元组
str字符串
bytes字节串
list列表
bytearray字节数组
array.array数组
collections.deque双端队列
memoryview内存视图
list
tuple
deque
str
bytes
bytearray
array.array
memoryview
三、列表推导与生成器表达式
3.1 列表推导式(list comprehension)
列表推导是构建列表(list)的快捷方式,能够写出可读性更好且更高效的代码。使用列表推导的原则:保持简洁,如果代码超过两行,建议使用 for 循环代替。
# 基本用法
symbols = '$¢£¥€¤'
codes = [ord(symbol) for symbol in symbols]
print(codes) # [36, 162, 163, 165, 8364, 164]
# 带条件过滤的列表推导
odd_ascii = [ord(s) for s in symbols if ord(s) % 2 == 1]
print(odd_ascii) # [163, 165, 8364, 164]
# 三层嵌套循环与条件(保持简洁!)
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors for size in sizes]
print(tshirts) # 内层 size 循环先变化
# [('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')]
列表推导的执行顺序:第一个循环是外层循环,随后的循环是内层循环。如上例中,color 循环在外层,size 循环在内层,因此 size 先变化。
3.2 生成器表达式(generator expression)
生成器表达式的语法跟列表推导差不多,只不过把方括号换成圆括号。它遵守迭代器协议,可以逐个地产出元素,而不是先建立一个完整的列表,能节省大量内存。
# 生成器表达式
symbols = '$¢£¥€¤'
codes_gen = (ord(symbol) for symbol in symbols)
print(codes_gen) # <generator object <genexpr> at 0x…>
# 可直接传递给构造函数,避免额外括号
# 注意:生成器是函数调用的唯一参数时,不需要额外圆括号
codes_tuple = tuple(ord(symbol) for symbol in symbols)
print(codes_tuple) # (36, 162, 163, 165, 8364, 164)
# 生成器只能迭代一次!
codes_once = (x for x in range(3))
print(list(codes_once)) # [0, 1, 2]
print(list(codes_once)) # [] —— 空的!生成器已耗尽
3.3 列表推导 vs 生成器表达式对比
| 语法 | [表达式 for 变量 in 可迭代对象] | (表达式 for 变量 in 可迭代对象) |
| 返回类型 | list 对象 | generator 对象 |
| 内存占用 | 一次性创建整个列表,内存占用与元素数量成正比 | 惰性求值,逐一产出,内存占用极低 |
| 迭代次数 | 可以多次迭代 | 只能迭代一次,迭代完成后耗尽 |
| 执行时机 | 立即执行 | 延迟执行(迭代时才触发) |
| 适用场景 | 元素数量可控、需要多次复用、需要索引访问 | 大量数据处理、流式处理、无需索引访问 |
当输入的数据量较大时,列表推导可能会因为占用太多内存而出问题。由生成器表达式所返回的迭代器,可以逐次产生输出值,从而避免了内存用量问题。
四、元组不仅仅是不可变的列表
Python 中的元组有两个功能:
4.1 元组作为记录
元组拆包(unpacking)是元组充当记录的核心功能——通过拆包可以方便地提取字段到独立变量。
# 并行赋值——元组作为记录的基本方式
lax_coordinates = (33.9425, –118.408056)
latitude, longitude = lax_coordinates # 元组拆包
print(f"纬度:{latitude}, 经度:{longitude}")
# 城市记录
city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014)
print(f"{city}, {year}年, 人口:{pop}万")
# 交换变量值——最优雅的交换方式
a, b = 10, 15
b, a = a, b # 无需临时变量
print(a, b) # 15 10
4.2 拆包的强大功能
1. * 运算符处理剩余元素——扩展可迭代解包(PEP 3132)
# * 用于捕捉剩余元素
a, b, *rest = range(5)
print(a, b, rest) # 0 1 [2, 3, 4]
a, *rest, b = range(5)
print(a, rest, b) # 0 [1, 2, 3] 4
*rest, a, b = range(5)
print(rest, a, b) # [0, 1, 2] 3 4
# 不需要的字段用 _ 占位符
obj = [i for i in range(5)]
_, second, _, _, _ = obj
print(second) # 1
2. 函数返回多值
# Python 函数返回多个值,本质上返回一个元组
def div_mod(a, b):
"""返回商和余数"""
return a // b, a % b
quotient, remainder = div_mod(20, 8)
print(f"20 ÷ 8 = {quotient} 余 {remainder}")
3. 用 * 将可迭代对象拆开作为函数参数
# divmod 函数需要两个参数
t = (20, 8)
quotient, remainder = divmod(*t) # * 将元组拆分成两个参数
print(f"20 ÷ 8 = {quotient} 余 {remainder}")
# 获取文件路径中的文件名
import os
file_path = "/home/user/docs/notes.txt"
_, filename = os.path.split(file_path)
print(filename) # notes.txt
4. 嵌套元组拆包——递归解包
嵌套元组拆包是元组拆包的一个强大功能,可以一层层解包嵌套的元组。
metro_areas = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, –99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, –74.020386)),
('Sao Paulo', 'BR', 19.649, (–23.547778, –46.635833)),
]
# 嵌套拆包:name, cc, pop, (lat, lon) 结构完全匹配
for name, cc, pop, (lat, lon) in metro_areas:
if lon <= 0:
print(f"{name:15} | {lat:9.4f} | {lon:9.4f}")
4.3 元组作为不可变列表
元组的不可变性体现在:不能对元组本身进行改变元素的赋值操作——但元组中存储的对象如果是可变的(如列表),那么这个可变对象的内容是可以被改变的。
# 不可变约束仅作用于元组本身
a = (10, 'a', [1, 2])
# a[0] = 20 # TypeError: 'tuple' object does not support item assignment
# a[-1] = [2, 3] # TypeError: tuple doesn't support item assignment
# 但元组内包含的可变对象可以被修改!
a[–1].append(99)
print(a) # (10, 'a', [1, 2, 99]) —— 元组还是原来的元组,但里面的列表变了
4.4 具名元组(namedtuple)
collections.namedtuple 是一个工厂函数,可以用来构建一个带字段名的元组和一个有名字的类。这个带名字的类对调试程序有很大帮助。
from collections import namedtuple
# 创建具名元组的类定义
# 参数1:类名;参数2:字段名列表(空格分隔的字符串或可迭代对象)
City = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
print(tokyo) # City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
print(tokyo.population) # 36.933(可通过字段名访问)
print(tokyo[2]) # 36.933(也可通过索引访问)
# 专有属性和方法
print(City._fields) # ('name', 'country', 'population', 'coordinates')——所有字段名元组
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi = City._make(delhi_data) # _make() 从可迭代对象构建实例,等价于 City(*delhi_data)
print(delhi._asdict()) # OrderedDict([('name', 'Delhi NCR'), …])——转换为字典
用 namedtuple 构建的类的实例所消耗的内存跟普通元组是一样的,因为字段名都被存在对应的类里面,而不是每个实例的 __dict__ 中,这使得它比普通类实例更节省内存。
五、切片——深潜 Python 的切片操作
list、tuple、str 等序列类型都支持切片操作。切片操作比人们想象的要强大很多。
5.1 忽略最后一个元素的设计约定
Python 的切片和区间操作遵循左闭右开约定:包含起始索引,不包含结束索引。这种设计有几大好处:
- 可以清晰地表达区间长度:range(3) 和 my_list[:3] 都返回 3 个元素
- 分割区间时不会重叠:my_list[:x] 和 my_list[x:] 正好无缝拼接
- 使用 0 或负数索引时更容易计算
# 切片的基本使用
s = 'bicycle'
print(s[::3]) # 'bye'——步长 3
print(s[::–1]) # 'elcycib'——反转
print(s[::–2]) # 'eccb'——反向步长 2
5.2 切片赋值——就地修改序列
切片赋值是可变序列的一个强大特性,能修改原序列的值。
# 切片赋值——等长替换
a = list(range(10))
print(a) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 替换索引 2 到 4 的区间(不包括 4)为 [20, 30]
a[2:4] = [20, 30]
print(a) # [0, 1, 20, 30, 4, 5, 6, 7, 8, 9]
# 不等长替换——右边迭代的任意长度元素会被插入
a[2:4] = [100, 200, 300]
print(a) # [0, 1, 100, 200, 300, 4, 5, 6, 7, 8, 9]
# 删除切片
a[2:5] = [] # 等价于 del a[2:5]
print(a) # [0, 1, 4, 5, 6, 7, 8, 9]
5.3 背后原理:slice 对象
在切片表达式 my_seq[a:b:c] 中,Python 会创建一个 slice(a, b, c) 对象。这意味着切片可以存储起来重复使用,极大提高了代码的灵活性和可维护性。
# slice 对象可以命名复用
invoice = """
… 0…..6…………………….40……..52…55
… 1909 Pimoroni Pi3 Proto Board $ 8.99
… 1489 6mm Tactile Switch x20 $ 4.95
… """
# 定义字段位置
SKU = slice(0, 6)
DESCRIPTION = slice(6, 40)
UNIT_PRICE = slice(40, 52)
QTY = slice(52, 55)
# 解析每一行
line_items = invoice.split('\\n')[2:]
for item in line_items:
if item:
print(f"SKU: {item[SKU].strip()} | 描述: {item[DESCRIPTION].strip()} | 单价: {item[UNIT_PRICE].strip()} | 数量: {item[QTY].strip()}")
5.4 多维切片与省略号
在 NumPy 等科学计算库中,切片支持多个维度。…(省略号)表示省略任意多个维度。
# 多维切片示例(NumPy)
# x[i, …] 等价于 x[i, :, :, :]
# 切片支持逗号分隔的多维索引
# 背后的原理:Python 将多维索引打包成元组传递给 __getitem__
# 例如 a[1:2, 3:4] 会被转换为 a.__getitem__((slice(1,2), slice(3,4)))
5.5 切片原理流程图
#mermaid-svg-6lLjZ4rLcUK0DUOo{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6lLjZ4rLcUK0DUOo .error-icon{fill:#552222;}#mermaid-svg-6lLjZ4rLcUK0DUOo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6lLjZ4rLcUK0DUOo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .marker.cross{stroke:#333333;}#mermaid-svg-6lLjZ4rLcUK0DUOo svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6lLjZ4rLcUK0DUOo p{margin:0;}#mermaid-svg-6lLjZ4rLcUK0DUOo .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .cluster-label text{fill:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .cluster-label span{color:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .cluster-label span p{background-color:transparent;}#mermaid-svg-6lLjZ4rLcUK0DUOo .label text,#mermaid-svg-6lLjZ4rLcUK0DUOo span{fill:#333;color:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .node rect,#mermaid-svg-6lLjZ4rLcUK0DUOo .node circle,#mermaid-svg-6lLjZ4rLcUK0DUOo .node ellipse,#mermaid-svg-6lLjZ4rLcUK0DUOo .node polygon,#mermaid-svg-6lLjZ4rLcUK0DUOo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .rough-node .label text,#mermaid-svg-6lLjZ4rLcUK0DUOo .node .label text,#mermaid-svg-6lLjZ4rLcUK0DUOo .image-shape .label,#mermaid-svg-6lLjZ4rLcUK0DUOo .icon-shape .label{text-anchor:middle;}#mermaid-svg-6lLjZ4rLcUK0DUOo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .rough-node .label,#mermaid-svg-6lLjZ4rLcUK0DUOo .node .label,#mermaid-svg-6lLjZ4rLcUK0DUOo .image-shape .label,#mermaid-svg-6lLjZ4rLcUK0DUOo .icon-shape .label{text-align:center;}#mermaid-svg-6lLjZ4rLcUK0DUOo .node.clickable{cursor:pointer;}#mermaid-svg-6lLjZ4rLcUK0DUOo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .arrowheadPath{fill:#333333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6lLjZ4rLcUK0DUOo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6lLjZ4rLcUK0DUOo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6lLjZ4rLcUK0DUOo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6lLjZ4rLcUK0DUOo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .cluster text{fill:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo .cluster span{color:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6lLjZ4rLcUK0DUOo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6lLjZ4rLcUK0DUOo rect.text{fill:none;stroke-width:0;}#mermaid-svg-6lLjZ4rLcUK0DUOo .icon-shape,#mermaid-svg-6lLjZ4rLcUK0DUOo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6lLjZ4rLcUK0DUOo .icon-shape p,#mermaid-svg-6lLjZ4rLcUK0DUOo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6lLjZ4rLcUK0DUOo .icon-shape .label rect,#mermaid-svg-6lLjZ4rLcUK0DUOo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6lLjZ4rLcUK0DUOo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6lLjZ4rLcUK0DUOo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6lLjZ4rLcUK0DUOo :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
调用 __getitem__ / __setitem__
Python 转换过程
切片表达式语法
my_list[2]
my_list[2:5]
my_list[2:8:2]
my_list[:]
my_list[::-1]
整数键
slice(2,5,None)
slice(2,8,2)
slice(None,None,None)
slice(None,None,-1)
__getitem__(2)
__setitem__(slice(2,5), [a,b,c])
六、序列运算符
6.1 + 和 * 运算符
+ 和 * 都不修改原有的操作对象,而是构建一个全新的序列。
# 基本用法
a = [1, 2, 3]
b = a * 4
print(b) # [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
# 列表的列表——潜藏的陷阱
board = [['_'] * 3 for _ in range(3)] # ✅ 正确的三层方法
print(board) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
board[0][0] = 'X'
print(board) # [['X', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
# ❌ 错误的方式:内部的列表是同一个对象的引用拷贝
bad_board = [['_'] * 3] * 3
bad_board[0][0] = 'X'
print(bad_board) # 意外的结果:[['X', '_', '_'], ['X', '_', '_'], ['X', '_', '_']]
为什么 bad_board 会这样? ['_'] * 3 创建了一个列表,然后 [list] * 3 将同一个列表对象的引用复制了三遍。修改任意一个都会“传染”到所有行。
七、增量赋值:+= 和 *= 的陷阱
+= 背后的特殊方法是 __iadd__,如果一个类没有实现 __iadd__ 方法,Python 会退一步调用 __add__ 方法。
- 可变序列(如 list)实现了 __iadd__,因此 += 是就地修改(对象 ID 保持不变)。
- 不可变序列(如 tuple)没有实现 __iadd__,因此 += 会创建一个新对象。
7.1 经典陷阱:元组中的 +=
t = (1, 2, [30, 40])
try:
t[2] += [50, 60] # 试图修改元组内的列表
except TypeError as e:
print(f"TypeError: {e}") # 'tuple' object does not support item assignment
print(t) # (1, 2, [30, 40, 50, 60]) —— 列表确实修改成功!
为什么会这样? t[2] += [50, 60] 实际上分两步执行:第一步在列表上执行 __iadd__ 就地追加元素,列表成功被修改;第二步尝试将结果赋值回元组的第三个元素,此时 tuple 不支持元素赋值,所以抛出异常。但第一步的操作已经完成,这就是所谓的“意外成功”。
7.2 增量赋值工作流
#mermaid-svg-gf8A6aR1JSf0xLnJ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gf8A6aR1JSf0xLnJ .error-icon{fill:#552222;}#mermaid-svg-gf8A6aR1JSf0xLnJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gf8A6aR1JSf0xLnJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .marker.cross{stroke:#333333;}#mermaid-svg-gf8A6aR1JSf0xLnJ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gf8A6aR1JSf0xLnJ p{margin:0;}#mermaid-svg-gf8A6aR1JSf0xLnJ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .cluster-label text{fill:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .cluster-label span{color:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .cluster-label span p{background-color:transparent;}#mermaid-svg-gf8A6aR1JSf0xLnJ .label text,#mermaid-svg-gf8A6aR1JSf0xLnJ span{fill:#333;color:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .node rect,#mermaid-svg-gf8A6aR1JSf0xLnJ .node circle,#mermaid-svg-gf8A6aR1JSf0xLnJ .node ellipse,#mermaid-svg-gf8A6aR1JSf0xLnJ .node polygon,#mermaid-svg-gf8A6aR1JSf0xLnJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .rough-node .label text,#mermaid-svg-gf8A6aR1JSf0xLnJ .node .label text,#mermaid-svg-gf8A6aR1JSf0xLnJ .image-shape .label,#mermaid-svg-gf8A6aR1JSf0xLnJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-gf8A6aR1JSf0xLnJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .rough-node .label,#mermaid-svg-gf8A6aR1JSf0xLnJ .node .label,#mermaid-svg-gf8A6aR1JSf0xLnJ .image-shape .label,#mermaid-svg-gf8A6aR1JSf0xLnJ .icon-shape .label{text-align:center;}#mermaid-svg-gf8A6aR1JSf0xLnJ .node.clickable{cursor:pointer;}#mermaid-svg-gf8A6aR1JSf0xLnJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .arrowheadPath{fill:#333333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gf8A6aR1JSf0xLnJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gf8A6aR1JSf0xLnJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gf8A6aR1JSf0xLnJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gf8A6aR1JSf0xLnJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .cluster text{fill:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ .cluster span{color:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gf8A6aR1JSf0xLnJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gf8A6aR1JSf0xLnJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-gf8A6aR1JSf0xLnJ .icon-shape,#mermaid-svg-gf8A6aR1JSf0xLnJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gf8A6aR1JSf0xLnJ .icon-shape p,#mermaid-svg-gf8A6aR1JSf0xLnJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gf8A6aR1JSf0xLnJ .icon-shape .label rect,#mermaid-svg-gf8A6aR1JSf0xLnJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gf8A6aR1JSf0xLnJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gf8A6aR1JSf0xLnJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gf8A6aR1JSf0xLnJ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
执行 x += y
x 是否实现了 __iadd__?
调用 x.__iadd__(y)就地修改对象
将结果绑定回 x
x 的同一性 ID 不变
调用 x.__add__(y)创建新对象
将新对象赋值给 x
x 的 ID 发生变化
可变序列(list, array)
不可变序列(tuple, str, bytes)
八、高效管理序列:bisect、array 和 deque
8.1 用 bisect 管理已排序序列
bisect 模块包含两个主要函数:bisect 用于在有序序列中查找插入位置,insort 用于在有序序列中插入元素并保持有序。
import bisect
# bisect 查找插入点
haystack = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
needles = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]
for needle in needles:
position = bisect.bisect_left(haystack, needle) # 查找插入位置
print(f"{needle:2} 应插入到索引 {position}")
# insort 插入元素
my_list = [1, 3, 5, 7]
bisect.insort(my_list, 4)
print(my_list) # [1, 3, 4, 5, 7]
8.2 array:数值类型的高效存储
array.array 是在内存中紧凑存储同类型数值数据的方式。它比 list 更节省内存,尤其在处理数百万个数值时优势明显。
from array import array
from random import random
# 创建双精度浮点数数组
floats = array('d', (random() for _ in range(10**6)))
print(f"数组长度: {len(floats)}")
print(f"第一个元素: {floats[0]}")
print(f"最后一个元素: {floats[–1]}")
# 保存到文件
with open('floats.bin', 'wb') as f:
floats.tofile(f)
# 从文件读取
floats2 = array('d')
with open('floats.bin', 'rb') as f:
floats2.fromfile(f, 10**6)
print(floats == floats2) # True
8.3 deque:双端队列的高效操作
deque(双端队列)支持在两端进行快速的插入和删除操作,时间复杂度均为 O(1)。相比之下,在 list 头部插入元素的时间复杂度是 O(n)。
from collections import deque
# 创建双端队列,设置最大长度
dq = deque(range(10), maxlen=10)
print(dq) # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
# 两端操作
dq.append(10) # 尾部添加
print(dq) # deque([1, 2, …, 9, 10], maxlen=10) —— 旧元素自动丢弃
dq.appendleft(–1) # 头部添加
print(dq) # deque([-1, 1, 2, …, 9], maxlen=10)
# 旋转操作
dq.rotate(3) # 向右旋转 3 步
print(dq) # deque([7, 8, 9, -1, 1, 2, 3, 4, 5, 6], maxlen=10)
dq.rotate(–4) # 向左旋转 4 步
print(dq) # deque([3, 4, 5, 6, 7, 8, 9, -1, 1, 2], maxlen=10)
# 弹出操作
print(dq.pop()) # 从右端弹出
print(dq.popleft()) # 从左端弹出
九、内存管理与性能比较
本章介绍的几种序列类型在内存管理和性能上各有特点:
| list | 容器序列 | 可变 | 存储引用,内存开销大 | 通用动态数组 |
| tuple | 容器序列 | 不可变 | 存储引用,但结构不可变 | 不可变记录、字典键 |
| deque | 容器序列 | 可变 | 双端队列实现,两端操作 O(1) | 队列、栈、滑动窗口 |
| array | 扁平序列 | 可变 | 紧凑存储,内存占用低 | 大量同类型数值 |
| str | 扁平序列 | 不可变 | Unicode 字符紧凑存储 | 字符串处理 |
| bytes | 扁平序列 | 不可变 | 字节值紧凑存储 | 二进制数据处理 |
| memoryview | 扁平序列 | 可变 | 内存视图,无数据复制 | 处理大数据分片 |
memoryview 的核心价值:在不复制数据的前提下访问内存中的二进制数据。对于大型数组或文件,memoryview 可以显著减少内存拷贝开销。
十、本章思维导图
#mermaid-svg-skB8nHc3vWjwvJvS{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-skB8nHc3vWjwvJvS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-skB8nHc3vWjwvJvS .error-icon{fill:#552222;}#mermaid-svg-skB8nHc3vWjwvJvS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-skB8nHc3vWjwvJvS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-skB8nHc3vWjwvJvS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-skB8nHc3vWjwvJvS .marker.cross{stroke:#333333;}#mermaid-svg-skB8nHc3vWjwvJvS svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-skB8nHc3vWjwvJvS p{margin:0;}#mermaid-svg-skB8nHc3vWjwvJvS .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-skB8nHc3vWjwvJvS .cluster-label text{fill:#333;}#mermaid-svg-skB8nHc3vWjwvJvS .cluster-label span{color:#333;}#mermaid-svg-skB8nHc3vWjwvJvS .cluster-label span p{background-color:transparent;}#mermaid-svg-skB8nHc3vWjwvJvS .label text,#mermaid-svg-skB8nHc3vWjwvJvS span{fill:#333;color:#333;}#mermaid-svg-skB8nHc3vWjwvJvS .node rect,#mermaid-svg-skB8nHc3vWjwvJvS .node circle,#mermaid-svg-skB8nHc3vWjwvJvS .node ellipse,#mermaid-svg-skB8nHc3vWjwvJvS .node polygon,#mermaid-svg-skB8nHc3vWjwvJvS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-skB8nHc3vWjwvJvS .rough-node .label text,#mermaid-svg-skB8nHc3vWjwvJvS .node .label text,#mermaid-svg-skB8nHc3vWjwvJvS .image-shape .label,#mermaid-svg-skB8nHc3vWjwvJvS .icon-shape .label{text-anchor:middle;}#mermaid-svg-skB8nHc3vWjwvJvS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-skB8nHc3vWjwvJvS .rough-node .label,#mermaid-svg-skB8nHc3vWjwvJvS .node .label,#mermaid-svg-skB8nHc3vWjwvJvS .image-shape .label,#mermaid-svg-skB8nHc3vWjwvJvS .icon-shape .label{text-align:center;}#mermaid-svg-skB8nHc3vWjwvJvS .node.clickable{cursor:pointer;}#mermaid-svg-skB8nHc3vWjwvJvS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-skB8nHc3vWjwvJvS .arrowheadPath{fill:#333333;}#mermaid-svg-skB8nHc3vWjwvJvS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-skB8nHc3vWjwvJvS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-skB8nHc3vWjwvJvS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-skB8nHc3vWjwvJvS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-skB8nHc3vWjwvJvS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-skB8nHc3vWjwvJvS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-skB8nHc3vWjwvJvS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-skB8nHc3vWjwvJvS .cluster text{fill:#333;}#mermaid-svg-skB8nHc3vWjwvJvS .cluster span{color:#333;}#mermaid-svg-skB8nHc3vWjwvJvS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-skB8nHc3vWjwvJvS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-skB8nHc3vWjwvJvS rect.text{fill:none;stroke-width:0;}#mermaid-svg-skB8nHc3vWjwvJvS .icon-shape,#mermaid-svg-skB8nHc3vWjwvJvS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-skB8nHc3vWjwvJvS .icon-shape p,#mermaid-svg-skB8nHc3vWjwvJvS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-skB8nHc3vWjwvJvS .icon-shape .label rect,#mermaid-svg-skB8nHc3vWjwvJvS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-skB8nHc3vWjwvJvS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-skB8nHc3vWjwvJvS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-skB8nHc3vWjwvJvS :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
第2章 丰富的序列
序列分类
按存储分类
容器序列list, tuple, deque
扁平序列str, bytes, array
按可变性分类
可变序列list, bytearray, deque
不可变序列tuple, str, bytes
列表推导与生成器表达式
列表推导式
生成器表达式
内存占用对比
元组的双重身份
用作记录-拆包
用作不可变列表
基础拆包
* 扩展解包
嵌套拆包
具名元组namedtuple
切片
基本切片语法
切片赋值
slice对象复用
多维切片
序列运算符
+ 和 *
列表的列表-陷阱
增量赋值 +=
不可变序列中的陷阱
高效序列类型
bisect模块
array.array
deque双端队列
memoryview
十一、总结
第 2 章“丰富的序列”系统讲解了 Python 序列类型的全貌。学习后发现,Python 之所以让开发者爱不释手,很大程度源于它对序列的精妙设计:一致的接口、灵活的操作和强大的扩展能力。
- 序列分类是理解的基础:容器序列存储引用,扁平序列存储值;可变序列可修改,不可变序列保证数据安全。
- 列表推导和生成器表达式是代码精简的利器:但要注意生成器只能迭代一次,在大量数据处理时要优先使用生成器。
- 元组不仅是不可变的列表,更是数据的记录载体:拆包功能的强大让人惊叹。具名元组在保留元组内存效率的同时,赋予了代码更好的可读性。
- 切片是 Python 序列优雅设计的最佳体现:可以命名切片对象复用,切片赋值还能就地修改序列,效率惊人。
- 增量赋值(+=)涉及可变性的陷阱:在元组上对可变元素使用 += 可能导致“一半成功、一半报错”的局面,理解可变性有利于避免踩坑。
- 选择合适的序列类型至关重要:bisect 用于维护有序序列,array 存储大量数值节省内存,deque 用于双端高效操作。根据场景选择合适的类型,代码质量和性能都能明显提升。
十二、思考题
列表拼接的陷阱:a = [1, 2, 3]; a += (4, 5) 在 Python 中是合法的,执行后 a 变为 [1, 2, 3, 4, 5]。但为什么 a = a + (4, 5) 会抛出 TypeError?
自己实现 namedtuple:你能用自定义类实现一个类似 namedtuple 的功能吗?如何确保实例的内存占用与普通元组一样小?
list.sort() 与 sorted() 的区别:前者原地排序并返回 None,后者返回一个新列表。为什么 Python 设计这样的区分?在哪些场景下各具优势?
切片赋值的内容要求:执行 a[2:4] = [100] 时,右边的可迭代对象是否必须是序列?能否是生成器?为什么?
del 与切片删除的关系:del a[2:5] 与 a[2:5] = [] 本质上有什么区别和联系?它们的性能有什么差异?
十三、下一章预告
第 3 章《字典和集合》
下一章将从数组存储元素切换到 键值对映射和集合,这是 Python 中应用最广泛的数据结构之一。第 3 章将涵盖:
- 字典推导式:如何用简洁的语法快速构建字典和集合。
- defaultdict 自动处理缺失键:告别 KeyError,优雅构建分组。
- OrderedDict 保留插入顺序:在 Python 3.7+ 中,普通字典也保留顺序,但 OrderedDict 仍保留了重新排序等特殊方法。
- Counter 计数器:一行代码实现词频统计,内置许多统计方法。
- 散列表的内部原理:深入理解 Python 字典的底层实现——是理解 Set 和 Dict 内存布局与性能的基石。
- 可散列对象:理解什么样的对象可以作为字典的键。
学完第 3 章,你会发现 Python 字典远比想象中强大——它不仅是基础容器,更是 Python 语言本身的骨架!
本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!




