← 返回题目列表

Python list 有哪些特点?append、extend、insert 有什么区别?

高频 简单 第 4 / 21 题 更新于 2026/07/25
Pythonlist列表动态数组

简化版

Python 的 list 是有序、可变、可重复的序列,底层可以理解为动态数组。append 在末尾追加一个元素,extend 把可迭代对象中的多个元素逐个追加进去,insert 在指定位置插入元素,通常会移动后面的元素。

详细版

常见操作:

nums = [1, 2]

nums.append([3, 4])
print(nums)  # [1, 2, [3, 4]]

nums = [1, 2]
nums.extend([3, 4])
print(nums)  # [1, 2, 3, 4]

nums.insert(1, 99)
print(nums)  # [1, 99, 2, 3, 4]

区别:

方法含义示例结果
append(x)x 作为一个元素追加[1, [2, 3]]
extend(iterable)把可迭代对象逐个追加[1, 2, 3]
insert(i, x)在下标 i 前插入 x需要移动元素

还要注意:很多 list 原地修改方法返回 None

nums = [3, 1, 2]
result = nums.sort()
print(result)  # None
print(nums)    # [1, 2, 3]

这是为了提醒你:sort() 改的是原列表,不是返回新列表。

完整版教学

一、list 的核心特性

list 是 Python 中最常用的容器之一:

items = ["java", "python", "go"]

它的特点:

  • 有序:元素有位置,可以按下标访问。
  • 可变:可以增删改。
  • 可重复:同一个值可以出现多次。
  • 可混合:可以放不同类型对象,但工程上通常不建议混得太乱。
items[0] = "rust"
items.append("python")

list 保存的是对象引用,不是把对象内容“嵌入”进列表。一个列表里可以放 int、str、dict、对象实例,但工程上通常应该保持元素类型一致,否则后续处理和类型推断会很痛苦。面试回答可以说:list 是通用容器,但不是类型设计的借口。

二、为什么说 list 类似动态数组

list 支持按下标快速访问:

items[0]
items[10]

可以把它理解为一段连续的引用数组。列表保存的是对象引用,不是把对象内容全部嵌进去。

当列表容量不够时,解释器会扩容并迁移引用,所以末尾 append 通常很快,但中间插入或删除通常要移动后续元素。

nums.insert(0, 100)

这个操作要把原来的元素整体往后挪,数据量大时成本明显。

可以用数字例子理解:长度为 10000 的列表,在末尾 append 一个元素,通常只写入末尾空位;在下标 0 insert 一个元素,后面约 10000 个引用都要右移。CPython list 会做过度分配,让多次 append 不必每次都扩容,但中间插入和删除仍然绕不开移动成本。

insert(0, X)
插入前: [A, B, C, D]
插入后: [X, A, B, C, D]
          A/B/C/D 都要后移
操作常见成本直觉原因
items[i]O(1)直接按下标定位
append(x)摊还 O(1)尾部追加,容量不足才扩容
insert(0, x)O(n)后续元素移动
pop()O(1)删除末尾
pop(0)O(n)后续元素移动

三、appendextendinsert 的区别

append 是追加一个对象:

a = [1, 2]
a.append([3, 4])
print(a)  # [1, 2, [3, 4]]

extend 是展开可迭代对象:

a = [1, 2]
a.extend([3, 4])
print(a)  # [1, 2, 3, 4]

字符串也是可迭代对象:

a = []
a.extend("abc")
print(a)  # ['a', 'b', 'c']

如果你想把 "abc" 作为一个整体元素,应该用 append("abc")

insert(i, x) 是在下标 i 前插入一个对象,不会展开 x。如果 i 小于 0,会按边界处理到开头附近;如果 i 大于长度,会等价于尾部追加。实际开发里,频繁头部插入说明 list 可能不是合适结构,可以考虑 collections.deque

a = [1, 2]
a.insert(1, [9, 9])
print(a)  # [1, [9, 9], 2]

记忆钩子:append 把参数当“一个元素”,extend 把参数当“一个可迭代序列”,insert 把参数插到指定位置但要移动后面的元素。

四、removepopdel 怎么选

删除也有几种方式:

nums = [10, 20, 30, 20]
nums.remove(20)  # 删除第一个值为 20 的元素
nums = [10, 20, 30]
x = nums.pop()   # 删除并返回最后一个元素
y = nums.pop(0)  # 删除并返回下标 0 的元素
nums = [10, 20, 30]
del nums[1]

选择方式:

  • 按值删第一个匹配:remove
  • 删除并拿到元素:pop
  • 按下标或切片删除,不需要返回:del

还要注意异常边界:remove(x) 找不到值会抛 ValueErrorpop(i) 下标越界会抛 IndexErrordel nums[i] 下标越界也会报错。若只是想安全过滤元素,列表推导式可能更清楚。

nums = [1, 2, 3, 2]
nums = [x for x in nums if x != 2]
print(nums)  # [1, 3]

五、原地方法为什么返回 None

这些方法通常返回 None

list.sort()
list.reverse()
list.append(x)
list.extend(iterable)

这样设计是为了避免误以为它们创建了新列表。

错误写法:

nums = [3, 1, 2]
nums = nums.sort()
print(nums)  # None

正确写法:

nums = [3, 1, 2]
nums.sort()

如果需要新列表,用:

new_nums = sorted(nums)

这个约定是 Python 容器 API 的一个信号:会原地修改对象的方法,通常不返回修改后的对象,避免链式调用造成误解。appendextendsortreverse 都是这个思路。相对地,sorted(nums)reversed(nums)、切片等会产生新对象或迭代器,要看具体 API。

六、浅拷贝和共享引用边界

list 的拷贝也经常和方法题一起追问。a.copy()a[:]list(a) 通常只复制外层列表,里面的可变对象仍共享。列表保存的是引用,所以“新列表”不等于“里面每个对象都新建”。

a = [[1], [2]]
b = a.copy()
b[0].append(99)
print(a)  # [[1, 99], [2]]

这和 append/extend 的区别也有关:append([3]) 会把内部列表作为一个元素保存引用;extend([3]) 才会把元素逐个放进去。面试回答如果能把动态数组、原地方法、浅拷贝串起来,就比只背 API 更扎实。

七、常见误区与追问

  • 误区:append([3, 4]) 会追加两个元素。 append 把参数作为一个整体元素,结果会多一个嵌套列表。
  • 误区:extend("abc") 会追加字符串整体。 字符串可迭代,会逐个字符追加;整体追加要用 append("abc")
  • 误区:nums = nums.sort() 能得到排序列表。 sort() 原地排序并返回 None,新列表用 sorted(nums)
  • 追问:为什么头部插入比尾部 append 慢? list 类似动态数组,头部插入要移动后续大量引用。
  • 追问:removepop 区别是什么? remove 按值删第一个匹配且不返回,pop 按下标删并返回元素。
  • 追问:list 拷贝后内部对象还会共享吗? 浅拷贝只复制外层列表,嵌套可变对象仍共享。

八、加强记忆

把 list 记成“有序可变的引用数组”:下标访问快,尾部 append 摊还快,中间插入删除要移动元素。append 加一个整体,extend 展开可迭代对象,insert 插到指定位置;原地修改方法多返回 None,拷贝默认多是浅拷贝。回答这题时用 [1, 2].append([3, 4])extend([3, 4]) 的对比最直观。