系列 · Python 零基础到简单脚本 第 3 篇 / 共 11 篇 教程

Python 字符串、列表与字典图解:沿着路径找到订单字段

用三条真实订单看懂列表与字典怎样嵌套、索引与键怎样定位字段,以及修改、别名和浅拷贝会影响哪一层数据。

作者:黄撑 更新于 2026-08-27
本文目录 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 是有顺序的列表,里面的每一项都是一条订单字典
外层 · listorders · 3 项
  1. 索引 0 · 第 1 项订单字典 A001
    order_no
    ”A001”
    sku
    ”K161”
    amount
    129.0
    status
    ”已支付”
  2. 索引 1 · 第 2 项订单字典 A002
    order_no
    ”A002”
    sku
    ”K162”
    amount
    88.0
    status
    ”已退款”
  3. 索引 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"] 才从这条字典取出状态。

嵌套访问路径每一步只进入一层,下一步操作前一步的结果
  1. 输入 · 01整批订单orders类型:list · 3 项
  2. 当前操作 · 02按位置取第 1 项orders[0]结果:A001 的 dict
  3. 当前操作 · 03按字段名取状态[“status”]结果:字符串“已支付”
  4. 输出 · 04目标字段值已支付路径完成
ordersorders[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)
两种定位方式索引回答“第几个”,键回答“哪个字段”
list · 按位置
0A0011A0022A003

orders[0] → 第一条订单字典

Python 的第一个索引是 0,不是 1。
dict · 按字段名
”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_noraw_sku 没有被直接改写。

修改状态时,改变的是哪一层

现在把第二条订单 A002 的状态从“已退款”改为“已支付”:

orders[1]["status"] = "已支付"

print(orders[1]["order_no"])
print(orders[1]["status"])

输出:

A002
已支付

左侧路径仍然先定位:orders[1] 找到 A002 字典,["status"] 再找到这个字典里的状态位置,最后用新字符串替换原值。

状态修改前后外层列表和其他字段不变,只替换 A002 字典里的 status 值
之前
orders[1]A002 · K162 · 88.0
status”已退款”
当前行orders[1][“status”] = “已支付”
之后
orders[1]A002 · K162 · 88.0
status”已支付”
未改变列表仍有 3 项A001、A003 不变A002 的订单号、SKU、金额不变

列表和字典最常用的其他修改,也可以从“落在哪一层”来理解:

# 在外层列表末尾增加一条订单
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)

输出 Truealias = 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
True
别名与浅拷贝关系图外层列表是否相同,和里面的订单字典是否相同,是两个问题
orders外层列表 A
alias外层列表 A · 同一个
outer_copy外层列表 B · 新建
外层列表 Aorders / alias 共用
  1. [0]订单对象 #1 · A001
  2. [1]订单对象 #2 · A002
  3. [2]订单对象 #3 · A003
外层列表 Bouter_copy 独有
  1. [0]订单对象 #1 · 仍是同一个
  2. [1]订单对象 #2 · 仍是同一个
  3. [2]订单对象 #3 · 仍是同一个
外层结构 在 outer_copy 里添加或删除列表项,不改变 orders 的外层列表。嵌套字典 从 outer_copy 修改 A001 的 status,orders 看到的 A001 也会改变。

用两次修改验证两层关系:

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、状态
多条有顺序的数据listorders 订单列表
一条数据的多个命名字段dictA001 订单字典
固定组合(进阶)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
已支付
暂无物流