查看: 330|回复: 0

Python列表增元素:append、extend与insert区别

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
Python列表中增加元素看似简单,但 append、insert、extend 和切片赋值在添加位置、添加数量、时间复杂度和返回值上差异很大。很多问题不是 API 不会用,而是选错方式导致结果不符合预期,甚至出现性能瓶颈。

一、append:末尾追加单个元素
append 把一个对象追加到列表末尾,是最常用的方式。它只添加一个元素,即使参数是列表,也只会把整个列表当成一个元素。
  1. fruits = ['苹果', '香蕉']
  2. fruits.append('橘子')
  3. print(fruits)  # ['苹果', '香蕉', '橘子']
  4. fruits.append(42)
  5. fruits.append([1, 2, 3])
  6. fruits.append({'key': 'value'})
  7. print(fruits)
  8. # ['苹果', '香蕉', '橘子', 42, [1, 2, 3], {'key': 'value'}]
复制代码
append 的关键:append(x) 等价于 lst[len(lst):] = [x],总是在末尾添加一个元素。不要用 append 添加多个元素:
  1. nums = [1, 2, 3]
  2. nums.append([4, 5])
  3. print(nums)      # [1, 2, 3, [4, 5]]
  4. print(len(nums)) # 4
复制代码
另外,append 返回 None,不是修改后的列表。写成 nums = nums.append(5) 会让 nums 变成 None。
  1. nums = [1, 2, 3]
  2. result = nums.append(4)
  3. print(result) # None
  4. print(nums)   # [1, 2, 3, 4]
复制代码
append 平均时间复杂度为 O(1)。Python 列表采用过度分配策略,不是每次追加都重新申请内存。

二、insert:在指定索引前插入
insert(i, x) 把 x 插入到当前索引 i 的元素之前,插入后 x 的索引为 i,原来的 lst[i:] 向后移动。
  1. fruits = ['苹果', '香蕉', '橘子']
  2. fruits.insert(1, '葡萄')
  3. print(fruits) # ['苹果', '葡萄', '香蕉', '橘子']
  4. fruits.insert(0, '西瓜')
  5. print(fruits) # ['西瓜', '苹果', '葡萄', '香蕉', '橘子']
复制代码
索引规则:i 超过长度时插到末尾;i 小于 -len(lst) 时插到开头。
  1. lst = [10, 20, 30, 40, 50]
  2. lst.insert(2, 99)
  3. print(lst) # [10, 20, 99, 30, 40, 50]
  4. lst.insert(100, 999)
  5. print(lst) # [10, 20, 99, 30, 40, 50, 999]
  6. lst.insert(-100, -999)
  7. print(lst) # [-999, 10, 20, 99, 30, 40, 50, 999]
复制代码
insert 的时间复杂度是 O(n),因为插入位置后面的元素都要移动。频繁在头部插入时,性能会很差。需要头部频繁插入时,应使用 collections.deque:
  1. from collections import deque
  2. dq = deque()
  3. dq.appendleft('a')
  4. dq.appendleft('b')
  5. dq.appendleft('c')
  6. print(list(dq)) # ['c', 'b', 'a']
复制代码

三、extend:批量追加可迭代对象
extend(iterable) 把可迭代对象中的每个元素逐个追加到列表末尾。列表、元组、字符串、range、集合、字典、生成器都可以。
  1. nums = [1, 2, 3]
  2. nums.extend([4, 5, 6])
  3. print(nums) # [1, 2, 3, 4, 5, 6]
  4. nums.extend((7, 8))
  5. print(nums) # [1, 2, 3, 4, 5, 6, 7, 8]
  6. nums.extend('abc')
  7. print(nums) # [1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c']
  8. nums.extend(range(10, 13))
  9. print(nums) # [1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c', 10, 11, 12]
复制代码
extend 与 append 的区别:
  1. lst1 = [1, 2, 3]
  2. lst1.append([4, 5])
  3. print(lst1) # [1, 2, 3, [4, 5]]
  4. lst2 = [1, 2, 3]
  5. lst2.extend([4, 5])
  6. print(lst2) # [1, 2, 3, 4, 5]
复制代码
extend 原地修改列表;+ 运算符会创建新列表;+= 通常等价于 extend,调用 __iadd__,而 lst = lst + iterable 调用 __add__ 创建新列表。
  1. lst3 = [1, 2, 3]
  2. lst4 = lst3 + [4, 5]
  3. print(lst3) # [1, 2, 3]
  4. print(lst4) # [1, 2, 3, 4, 5]
  5. lst3.extend([4, 5])
  6. print(lst3) # [1, 2, 3, 4, 5]
复制代码
性能上,extend 通常比循环 append 快,因为循环在 C 层面完成;但如果是从头创建列表,列表推导式往往更快。

四、切片赋值:最灵活的插入方式
切片赋值可以在任意位置插入任意数量元素,语法是 lst[a:a] = iterable。
  1. nums = [1, 5, 9]
  2. nums[1:1] = [2, 3, 4]
  3. print(nums) # [1, 2, 3, 4, 5, 9]
  4. nums[:0] = [-2, -1, 0]
  5. print(nums) # [-2, -1, 0, 1, 2, 3, 4, 5, 9]
  6. nums[len(nums):] = [10, 11, 12]
  7. print(nums) # [-2, -1, 0, 1, 2, 3, 4, 5, 9, 10, 11, 12]
复制代码
切片插入和 extend 一样接受任何可迭代对象,也可以插入生成器;插入空序列等于什么都不做。
  1. lst = [1, 6]
  2. lst[1:1] = (2, 3, 4, 5)
  3. print(lst) # [1, 2, 3, 4, 5, 6]
  4. lst[1:1] = (x ** 2 for x in range(3))
  5. print(lst) # [1, 0, 1, 4, 2, 3, 4, 5, 6]
复制代码

五、四种方式对比
功能对比:
  1. goal = [1, 2, 3, 4, 5]
  2. result1 = [1, 2, 3]
  3. result1.append(4)
  4. result1.append(5)
  5. print(f'append: {result1}')
  6. result2 = [1, 3, 4, 5]
  7. result2.insert(1, 2)
  8. print(f'insert: {result2}')
  9. result3 = [1, 2]
  10. result3.extend([3, 4, 5])
  11. print(f'extend: {result3}')
  12. result4 = [1, 5]
  13. result4[1:1] = [2, 3, 4]
  14. print(f'切片: {result4}')
复制代码
对比表:
方法 | 添加位置 | 添加数量 | 时间复杂度 | 返回值
append(x) | 末尾 | 1个(x本身) | O(1) | None
insert(i,x) | 指定索引i之前 | 1个(x本身) | O(n) | None
extend(it) | 末尾 | N个(展开) | O(k) | None
lst[a:a]=it | 指定位置 | 任意 | O(n) | None

性能方面,尾部追加用 append 循环和 extend 一次性追加,extend 通常更快;头部插入 list.insert(0) 比 deque.appendleft 慢很多。原文示例中 100000 次尾部追加和 10000 次头部插入都显示了明显差距。

六、实战:构建列表与分页累积
用不同方式构建 [0, 1, 4, 9, 16, 25]:
  1. squares1 = []
  2. for i in range(6):
  3.     squares1.append(i ** 2)
  4. print(squares1) # [0, 1, 4, 9, 16, 25]
  5. squares2 = []
  6. squares2.extend(i ** 2 for i in range(6))
  7. print(squares2) # [0, 1, 4, 9, 16, 25]
  8. squares5 = [i ** 2 for i in range(6)]
  9. print(squares5) # [0, 1, 4, 9, 16, 25]
复制代码
Builder 模式可以用 append 和 extend 逐步构建列表:
  1. class ListBuilder:
  2.     '''用append模式逐步构建列表——类似StringBuilder'''
  3.     def __init__(self):
  4.         self._items = []
  5.     def add(self, item):
  6.         self._items.append(item)
  7.         return self
  8.     def add_all(self, items):
  9.         self._items.extend(items)
  10.         return self
  11.     def build(self):
  12.         return self._items[:]
  13. result = (ListBuilder()
  14.           .add('表头')
  15.           .add_all(range(1, 5))
  16.           .add('表尾')
  17.           .build())
  18. print(result) # ['表头', 1, 2, 3, 4, '表尾']
复制代码
分页查询结果累积适合用 extend:
  1. def query_users(db_connection, conditions):
  2.     all_results = []
  3.     page = 1
  4.     page_size = 100
  5.     while True:
  6.         start = (page - 1) * page_size + 1
  7.         end = start + page_size
  8.         results = [f'用户{i}' for i in range(start, min(end, start + 1000))]
  9.         if not results:
  10.             break
  11.         all_results.extend(results)
  12.         page += 1
  13.         if page > 3:
  14.             break
  15.     return all_results
  16. users = query_users(None, {})
  17. print(f'共查询到 {len(users)} 个用户')
复制代码

七、常见错误与最佳实践
错误一:append 和 extend 混淆。append([4,5]) 添加整个列表,extend([4,5]) 才展开添加两个元素。
错误二:在循环中用 insert 构建列表,会变成 O(n^2)。应先用 append,最后 reverse,或者使用 deque。
错误三:忘记 append 返回 None。nums = [1,2].append(3) 会让 nums 变成 None。
错误四:对元组调用 append。元组不可变,会抛 AttributeError。
错误五:多线程中复合操作不加锁。append 本身受 GIL 保护,但“检查再添加”等复合操作仍需要 lock。

最佳实践:
添加单个元素到末尾:append()
添加多个元素到末尾:extend() 或 +=
在指定位置插入一个元素:insert()
在指定位置插入多个元素:切片赋值
频繁头部插入:collections.deque
从零构建列表:列表推导式

这四种方式都返回 None,因为都是原地修改列表。理解添加位置、添加数量和复杂度后,选型就不容易出错。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-24 15:58 , Processed in 0.022767 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部