Python 字符串、列表与字典图解:沿着路径找到订单字段
用三条真实订单看懂列表与字典怎样嵌套、索引与键怎样定位字段,以及修改、别名和浅拷贝会影响哪一层数据。
本文目录 12 节 · 点击展开
一条订单有订单号、SKU、金额和状态;三条订单又要保持各自完整。Python 通常用两层结构装下它们:列表保存一批订单,字典保存一条订单的多个字段,字符串则是订单号、SKU、状态等具体值。
这章不背容器方法清单。重点是学会看数据的形状,并沿着路径准确回答:现在进入哪一项、使用哪个字段、修改会落在哪一层。
一批订单是列表,一条订单是字典
后续章节会一直使用这三条数据:
orders = [
{"order_no": "A001", "sku": "K161", "amount": 129.0, "status": "已支付"},
{"order_no": "A002", "sku": "K162", "amount": 88.0, "status": "已退款"},
{"order_no": "A003", "sku": "K161", "amount": 199.0, "status": "已支付"},
]
先不做任何处理,只看它的形状:
orders 是有顺序的列表,里面的每一项都是一条订单字典- 索引 0 · 第 1 项订单字典 A001
- order_no
- ”A001”
- sku
- ”K161”
- amount
- 129.0
- status
- ”已支付”
- 索引 1 · 第 2 项订单字典 A002
- order_no
- ”A002”
- sku
- ”K162”
- amount
- 88.0
- status
- ”已退款”
- 索引 2 · 第 3 项订单字典 A003
- order_no
- ”A003”
- sku
- ”K161”
- amount
- 199.0
- status
- ”已支付”
最外面的 [...] 表示列表,适合保存“很多条有顺序的数据”。每一项的 {...} 表示字典,适合把一条订单的多个字段按名字放在一起。引号里的 "A001"、"K161"、"已支付" 都是字符串值。
沿着 orders → 第 1 项 → status 找值
要读取第一条订单的状态,代码可以写成:
status = orders[0]["status"]
print(status)
输出:
已支付
这串代码应该从左往右、一层一层读。orders[0] 先得到第一条订单字典,接着 ["status"] 才从这条字典取出状态。
- 输入 · 01整批订单
orders类型:list · 3 项 - 当前操作 · 02按位置取第 1 项
orders[0]结果:A001 的 dict - 当前操作 · 03按字段名取状态
[“status”]结果:字符串“已支付” - 输出 · 04目标字段值路径完成
orders→orders[0]→orders[0][“status”]→“已支付”长路径看不懂时,可以拆成中间变量,结果完全一样:
first_order = orders[0]
status = first_order["status"]
print(status)
拆开以后,每一步的形状更容易检查:first_order 是字典,status 是字符串。
列表用索引,字典用键
列表和字典都写方括号,但括号里的定位方式不同:
first_order = orders[0]
sku = first_order["sku"]
print(sku)
A0011A0022A003orders[0] → 第一条订单字典
- ”order_no"
- "A001"
- "sku"
- "K161"
- "status"
- "已支付”
first_order[“sku”] → “K161”
越界或字段不存在时,失败位置也不同:
print(orders[3]) # IndexError:没有索引 3
print(orders[0]["tracking_no"]) # KeyError:没有 tracking_no 键
如果一个字段本来就允许缺省,可以使用 .get() 提供默认值:
tracking_no = orders[0].get("tracking_no", "暂无物流")
print(tracking_no)
输出 暂无物流。但业务上必须存在的字段不应无条件改用 .get(),否则真实的数据问题可能被默认值遮住。
字符串只保留高频读取与清理
订单号、SKU 和状态通常是字符串。入门阶段最常见的动作是清理两端空格、统一英文大小写、按分隔符拆开:
raw_order_no = " A001 "
raw_sku = "k161-ph"
order_no = raw_order_no.strip()
sku = raw_sku.upper()
sku_parts = sku.split("-")
print(order_no)
print(sku)
print(sku_parts)
输出:
A001
K161-PH
['K161', 'PH']
这里有两次形状变化值得注意:strip()、upper() 产生新的字符串;split() 则把一个字符串拆成了列表。原来的 raw_order_no 和 raw_sku 没有被直接改写。
修改状态时,改变的是哪一层
现在把第二条订单 A002 的状态从“已退款”改为“已支付”:
orders[1]["status"] = "已支付"
print(orders[1]["order_no"])
print(orders[1]["status"])
输出:
A002
已支付
左侧路径仍然先定位:orders[1] 找到 A002 字典,["status"] 再找到这个字典里的状态位置,最后用新字符串替换原值。
A002 · K162 · 88.0”已退款”orders[1][“status”] = “已支付”A002 · K162 · 88.0”已支付”列表和字典最常用的其他修改,也可以从“落在哪一层”来理解:
# 在外层列表末尾增加一条订单
orders.append(
{"order_no": "A004", "sku": "K163", "amount": 59.0, "status": "已支付"}
)
# 在第一条订单字典里增加一个字段
orders[0]["channel"] = "官网"
print(len(orders))
print(orders[0]["channel"])
append() 改变列表包含多少项;按键赋值改变某一条字典包含哪些字段。两种修改的目标层级不同。
进阶补充元组、集合、列表推导式与浅拷贝主线已经结束;需要理解复制关系时再展开
元组:固定组合
元组使用圆括号,可以按位置读取,但不能直接改写其中某一项:
warehouse_location = (120, 30)
print(warehouse_location[0])看到 (120, 30) 时,先把它理解成“不希望单独修改的固定组合”就够了。
集合:去重与成员判断
集合适合去重,不适合按位置读取:
skus = ["K161", "K162", "K161"]
unique_skus = set(skus)
print(unique_skus)集合输出的顺序不应被当作业务顺序。仍需稳定先后关系时,继续使用列表。
列表推导式:简单转换的紧凑写法
已经熟悉普通读取后,可以认识一行生成新列表的写法:
skus = [order["sku"] for order in orders]
print(skus)它适合短而清楚的转换。逻辑一旦包含多个步骤,拆成普通代码通常更容易追踪。
赋值不是复制:两个名字可以指向同一列表
orders = [
{"order_no": "A001", "sku": "K161", "amount": 129.0, "status": "已支付"},
{"order_no": "A002", "sku": "K162", "amount": 88.0, "status": "已退款"},
{"order_no": "A003", "sku": "K161", "amount": 199.0, "status": "已支付"},
]
alias = orders
print(alias is orders)输出 True。alias = orders 没有创建新列表,只是增加了一个能找到同一列表的名字。因此从 alias 修改任何一层,都能从 orders 看见。
.copy() 只创建新的外层列表
alias = orders
outer_copy = orders.copy()
print(alias is orders)
print(outer_copy is orders)
print(outer_copy[0] is orders[0])输出:
True
False
Trueorders→外层列表 Aalias→外层列表 A · 同一个outer_copy→外层列表 B · 新建[0]→订单对象 #1 · A001[1]→订单对象 #2 · A002[2]→订单对象 #3 · A003
[0]→订单对象 #1 · 仍是同一个[1]→订单对象 #2 · 仍是同一个[2]→订单对象 #3 · 仍是同一个
用两次修改验证两层关系:
outer_copy.append(
{"order_no": "A004", "sku": "K163", "amount": 59.0, "status": "已支付"}
)
print(len(orders)) # 3
print(len(outer_copy)) # 4
outer_copy[0]["status"] = "已完成"
print(orders[0]["status"]) # 已完成第一次只改了新外层列表 B,所以 orders 仍是 3 项。第二次改的是两边共同指向的订单对象 #1,所以从 orders 也能看到“已完成”。这就是浅拷贝:复制外层容器,不递归复制里面的可变对象。
选择容器时先看数据关系
| 数据关系 | 优先想到 | 本章例子 |
|---|---|---|
| 一段文本 | str | 订单号、SKU、状态 |
| 多条有顺序的数据 | list | orders 订单列表 |
| 一条数据的多个命名字段 | dict | A001 订单字典 |
| 固定组合(进阶) | tuple | 仓库坐标 |
| 去重或成员判断(进阶) | set | 不重复 SKU |
不需要为“更高级”而换容器。只要形状匹配任务,列表里放字典就是处理表格、JSON 和接口数据时非常实用的基础结构。
动手验证访问与修改路径
继续使用本章开头的三条订单,完成下面四步:
orders = [
{"order_no": "A001", "sku": "K161", "amount": 129.0, "status": "已支付"},
{"order_no": "A002", "sku": "K162", "amount": 88.0, "status": "已退款"},
{"order_no": "A003", "sku": "K161", "amount": 199.0, "status": "已支付"},
]
first_status = orders[0]["status"]
second_sku = orders[1]["sku"]
orders[1]["status"] = "已支付"
tracking_no = orders[2].get("tracking_no", "暂无物流")
print(first_status)
print(second_sku)
print(orders[1]["status"])
print(tracking_no)
运行前先沿路径预测,结果应该依次是:
已支付
K162
已支付
暂无物流