Python的列表操作把我坑惨了,原来append和extend的区别这么大

简介: 本文详解Python列表的`append()`与`extend()`核心区别:`append()`将对象整体作为单个元素添加(长度+1),易致嵌套结构;`extend()`则拆解可迭代对象、逐项追加(长度+其长度),实现扁平合并。配以生活比喻、代码对比、性能分析及常见陷阱(如字符串/字典误用),助你避开数据结构变形坑。

小陈那天差点把键盘摔了。

他写了一个数据处理脚本,需要把多个来源的数据合并到一个大列表里。逻辑很简单:先创建一个空列表all_data,然后遍历每个数据源,把里面的每条记录加进去。代码大概长这样:

all_data = []
source1 = [1, 2, 3]
source2 = [4, 5, 6]

all_data.append(source1)
all_data.append(source2)
print(all_data)

他以为会得到[1, 2, 3, 4, 5, 6]。结果终端打印出来的是:

[[1, 2, 3], [4, 5, 6]]

列表里套了两个列表,完全不是他想要的扁平结构。他盯着屏幕愣了几秒,然后改成extend,问题瞬间解决。他叹了口气:“就差了四个字母,结果天差地别。”

这个坑太常见了。append和extend看起来都是往列表里加东西,但加的方式完全不同。用错了,数据结构就会悄悄变形,而且往往不会报错,直到下游代码崩溃你才发现。

代理 IP 使用小技巧 让你的数据抓取效率翻倍 (96).png

先看一个最直观的对比

我们把两个方法放在一起跑一遍:

lst1 = [1, 2, 3]
lst2 = [4, 5, 6]

lst1.append(lst2)
print("append 之后:", lst1)

lst3 = [1, 2, 3]
lst4 = [4, 5, 6]

lst3.extend(lst4)
print("extend 之后:", lst3)

输出:

append 之后: [1, 2, 3, [4, 5, 6]]
extend 之后: [1, 2, 3, 4, 5, 6]

append把lst2当成一个整体,塞进了lst1的末尾。lst1的长度从3变成4,第四个元素是[4, 5, 6]这个列表对象。

extend把lst4里的每个元素逐个取出来,依次追加到lst3末尾。lst3的长度从3变成6,六个元素都是独立的整数。

用一个生活场景来理解:你有一个水果篮,里面已经装了苹果。现在你手里有一袋橘子。

append的做法是:把整袋橘子直接扔进水果篮。篮子里现在有一个苹果,和一袋橘子。

extend的做法是:把袋子打开,把里面的橘子一个一个拿出来,放进水果篮。篮子里现在有一个苹果,和若干个橘子。

区别一目了然。

append:只加一个东西

append的签名是list.append(x)。它接受任意一个对象x,把它作为单个元素追加到列表末尾。列表长度只增加1。

这个“任意对象”包括:整数、字符串、列表、字典、元组、自定义对象,甚至另一个列表。不管x是什么,append都把它当作一个整体。

lst = []
lst.append(1)           # [1]
lst.append("hello")     # [1, "hello"]
lst.append([2, 3])      # [1, "hello", [2, 3]]
lst.append({"a": 1})    # [1, "hello", [2, 3], {"a": 1}]

每次调用,列表长度加1。元素就是传入的那个对象本身。

extend:拆开可迭代对象,逐个添加

extend的签名是list.extend(iterable)。它接受一个可迭代对象,遍历它,把每个元素依次追加到列表末尾。列表长度增加的是可迭代对象的长度。

lst = []
lst.extend([1, 2, 3])        # [1, 2, 3]
lst.extend("abc")            # [1, 2, 3, 'a', 'b', 'c']
lst.extend((4, 5))           # [1, 2, 3, 'a', 'b', 'c', 4, 5]
lst.extend({"x": 1, "y": 2}) # 添加的是键:['x', 'y']

注意最后一行:字典是可迭代对象,但迭代它得到的是键,不是键值对。所以extend一个字典,会把它的键逐个加进去。如果你想要值,得用dict.values()。

extend要求参数必须是可迭代的。如果传一个整数,会直接报错:

lst = []
lst.extend(5)

TypeError: 'int' object is not iterable

而append不会报错,它接受任何对象。

为什么字符串是重灾区?

字符串是最容易被误用的可迭代对象。

假设你想把一个字符串作为一个整体添加到列表里:

lst = ["hello"]
lst.append("world")
print(lst)  # ['hello', 'world']

没问题。但如果你手滑用了extend:

lst = ["hello"]
lst.extend("world")
print(lst)  # ['hello', 'w', 'o', 'r', 'l', 'd']

字符串被拆成了单个字符。因为字符串是可迭代的,extend会遍历它,把每个字符当作一个元素加进去。

这种错误在处理文件路径、用户名、标签时特别常见。你以为加了一个字符串,结果列表里多了一堆字符。

字典也是坑

字典也是可迭代对象,但迭代的是键。所以:

lst = []
lst.extend({"name": "Alice", "age": 30})
print(lst)  # ['name', 'age']

如果你想要的是字典本身作为一个元素,应该用append:

lst = []
lst.append({"name": "Alice", "age": 30})
print(lst)  # [{'name': 'Alice', 'age': 30}]

如果你想把字典的值加进去,应该用extend(d.values())。

性能差异:extend通常更快

除了语义不同,extend在性能上也通常优于多次append。

因为extend是在C层面实现的,它知道要添加多少个元素,可以一次性调整列表的容量,减少内存重新分配的次数。而循环调用append,每次都可能触发容量检查,虽然Python的列表有动态扩容机制,但反复调用仍有开销。

我们来实测一下:

import time

# 用 append 逐个添加
start = time.time()
lst = []
for i in range(1000000):
   lst.append(i)
print("append 耗时:", time.time() - start)

# 用 extend 一次性添加
start = time.time()
lst = []
lst.extend(range(1000000))
print("extend 耗时:", time.time() - start)

在我的机器上,append循环大约0.08秒,extend大约0.02秒。差距明显。

当然,如果你已经有现成的列表,直接extend比写循环append更简洁也更快。

那 + 和 += 呢?

列表还支持+运算符和+=运算符,它们也能合并列表。

a = [1, 2]
b = [3, 4]
c = a + b       # [1, 2, 3, 4]
a += b          # a 变成 [1, 2, 3, 4]

+会创建一个新列表,原列表不变。+=是原地修改,等价于extend。

注意:+=和extend一样,会把右边的可迭代对象拆开。但+要求两边都是列表,不能直接加字符串或元组。

a = [1, 2]
a += "abc"      # 等价于 extend,得到 [1, 2, 'a', 'b', 'c']
a = a + "abc"   # 报错,列表不能和字符串相加

所以,如果你想把一个字符串拆成字符加进去,用+=或extend;如果你想把字符串作为一个整体,用append。

切片赋值也能实现类似效果

还有一种不常见但很强大的方式:切片赋值。

lst = [1, 2, 3]
lst[len(lst):] = [4, 5, 6]
print(lst)  # [1, 2, 3, 4, 5, 6]

这等价于extend。但可读性不如extend,一般不建议这样写。

一个容易忽略的细节:extend 可以接受任何可迭代对象

extend的参数不一定是列表。它可以是任何可迭代对象:元组、集合、生成器、range、字符串、字典的键视图,甚至是你自己实现的迭代器。

lst = []
lst.extend(range(3))        # [0, 1, 2]
lst.extend({10, 20})        # 顺序不确定,集合是无序的
lst.extend(x for x in [7, 8])  # [..., 7, 8]

这意味着你可以直接把一个生成器传给extend,它会边迭代边添加。这在处理大数据时很有用,不需要先构建一个中间列表。

append 和 extend 的返回值都是 None

这是一个小细节,但容易导致链式调用出错。

lst = [1, 2]
result = lst.append(3)
print(result)  # None

append和extend都是原地修改列表,不返回新列表。所以你不能这样写:

lst = [1, 2].append(3)  # lst 是 None

这是新手常犯的错误。记住:它们修改原列表,返回None。

什么时候用 append,什么时候用 extend?

判断标准很简单:你想加一个东西,还是想加一堆东西?

  • 加一个东西:用append。无论这个东西是整数、字符串、列表还是字典,都作为一个整体。
  • 加一堆东西:用extend。这一堆东西必须是一个可迭代对象,里面的每个元素都会被逐个添加。

如果你有一个列表source,想把它里面的元素合并到target里,用target.extend(source)。

如果你想把source本身作为target的一个元素,用target.append(source)。

回到小陈的代码

小陈原本的代码:

all_data = []
source1 = [1, 2, 3]
source2 = [4, 5, 6]

all_data.append(source1)
all_data.append(source2)

得到的是[[1, 2, 3], [4, 5, 6]]。他想要的是扁平列表,所以应该用extend:

all_data = []
all_data.extend(source1)
all_data.extend(source2)

得到[1, 2, 3, 4, 5, 6]。

如果数据源很多,也可以写成循环:

all_data = []
for source in sources:
   all_data.extend(source)

或者更Pythonic的写法:

all_data = [item for source in sources for item in source]

但extend在性能上通常更好,尤其是数据量大时。

一个更隐蔽的坑:append 添加的是引用

最后提一个和append相关但容易被忽略的问题:append添加的是对象的引用,不是副本。

lst = []
inner = [1, 2]
lst.append(inner)
inner.append(3)
print(lst)  # [[1, 2, 3]]

修改inner之后,lst里的那个元素也跟着变了,因为它们指向同一个列表对象。如果你想要一个独立的副本,需要显式复制:

lst.append(inner.copy())

这个问题在循环中批量append时尤其危险。比如:

lst = []
for i in range(3):
   row = [i] * 3
   lst.append(row)

每次循环都新建了row,所以没问题。但如果你重用同一个列表:

lst = []
row = [0, 0]
for i in range(3):
   row[0] = i
   lst.append(row)
print(lst)

输出是[[2, 0], [2, 0], [2, 0]],因为三个元素都指向同一个row。这是另一个常见的列表陷阱,和append/extend的区别无关,但同样值得警惕。

总结

append和extend的区别,本质上是“加一个”和“加一串”的区别。

append(x):把x作为单个元素追加,列表长度加1。x可以是任何对象。

extend(iterable):遍历iterable,把每个元素逐个追加,列表长度增加len(iterable)。iterable必须是可迭代对象。

用错了,列表结构就会变形。append一个列表会得到嵌套列表,extend一个字符串会得到一堆字符。

记住那个水果篮的比喻:append是整袋扔进去,extend是倒出来一个个放。想清楚你要的是哪种,就不会再被坑了。

小陈后来在代码注释里写了一句话:“合并列表用extend,添加单个用append。别再用append加列表了。” 这个教训,值一下午的调试时间。

目录
相关文章
|
9天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
7646 13
|
7天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1629 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
4天前
|
人工智能 JavaScript 芯片
DeepSeek 官方偷偷上传 Harness 桌面端安装包,我已经用上了。。附最新下载地址
DeepSeek Harness 官方的桌面端安装包被网友扒出来了,2 分钟讲明白如何使用,体验如何,适合作为 AI 编程工具么?附最新 Windows 和 Mac 双端的下载地址
1377 1
|
7天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
1131 9
|
21天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3659 10
|
5天前
|
编解码 缓存 PyTorch
16G 显卡能跑 Qwen-Image 2.1 吗?
9月20日,阿里Qwen开源Qwen-Image-2.1:7B DiT图像模型+8B文本编码器+VAE,单模型支持文生图与图像编辑,原生输出2K PNG(含Alpha通道),支持10张参考图。在自建Qwen-Image-Bench达60.28分(开源模型第一),GenAI Showdown文生图排名7/15。16G显存可跑1024×1024(需INT8量化+ComfyUI优化),但2K需24G以上。注意其Qwen Research License限非商业用途。
588 1
|
6天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
15天前
|
缓存 IDE Java
【保姆级】Android Studio下载、安装和汉化教程(2026最新)
Android Studio 是 Google 官方推出的免费 Android 应用开发集成环境,基于 IntelliJ IDEA,内置模拟器、调试器、性能分析及 Compose 界面工具,功能全面,文档丰富,是安卓开发首选工具。(239字)
1689 1

热门文章

最新文章