广告

Python 列表 append 方法详解:使用场景、与 extend 的区别及性能分析

Python 列表 append 方法的基本用法

核心概念与调用方式

在 Python 中,列表的 append 方法用于将一个单独的对象追加到列表末尾,并直接修改原列表。它的调用签名是 list.append(obj),返回值是 None,因此通常不将结果赋值给新的变量。

需要注意的是,append 只接受单个对象,无论对象是整数、字符串、元组,还是另一个列表,都会作为一个单独的元素追加到末尾。若传入一个可迭代对象,结果仍然是将该对象作为一个整体添加,而不是将其展开。

# 基本用法示例
lst = [1, 2, 3]
lst.append(4)
print(lst)  # [1, 2, 3, 4]

如果你执行 lst.append([4,5]),结果会在列表末尾放入一个子列表,而不是将 4 和 5 作为单独的元素。这就是一个需要关注的点,因为它影响后续的迭代和访问模式。

# 将整个列表作为一个元素追加
lst = [1, 2, 3]
lst.append([4, 5])
print(lst)  # [1, 2, 3, [4, 5]]

在时间复杂度方面,append 的平均时间复杂度为常数时间 O(1),但当底层列表容量不足以容纳新元素时,Python 解释器会执行扩容操作,这会涉及将现有元素拷贝到新的内存区域,因此偶发的成本会高于常数时间,但总体是摊销常数时间

# 简单对比:对大量 append 的时间影响(单位:秒)可作为近似参考
import time
n = 1000000
lst = []
start = time.perf_counter()
for i in range(n):
    lst.append(i)
end = time.perf_counter()
print(end - start)

使用场景:什么时候选择 append

逐步构建数据结构

当你需要在循环或逐条处理数据时逐步构建列表,使用 append 将每一条记录追加到末尾,避免一次性创建中间列表。

此外,append 适合合并单个对象到现有集合,例如从数据源读取一条条记录后持续累积。

# 场景示例:逐条读取并累积
lines = []
with open('data.txt','r') as f:
    for line in f:
        lines.append(line.rstrip('\n'))

如果你需要把多个元素一次性加入,请考虑 extend 或列表连接,而不是多次调用 append,后者在大数据量时会有更高的函数调用开销。

# 不要用 append 循环拼接大量元素,示例用 extend 更高效
data = [1,2,3,4,5]
lst = []
lst.extend(data)
print(lst)  # [1, 2, 3, 4, 5]

append 与 extend 的区别

核心差异点

最关键的差异在于:append 向列表末尾添加单一对象,而 extend 迭代给定的可迭代对象,并把其中的元素逐个追加

另一个要点是:传入一个可迭代对象如列表、元组等,append 会把它作为一个整体添加,而 extend 会展开它的元素。

# append 示例
lst = [1, 2, 3]
lst.append([4, 5])
print(lst)  # [1, 2, 3, [4, 5]]

# extend 示例
lst = [1, 2, 3]
lst.extend([4, 5])
print(lst)  # [1, 2, 3, 4, 5]

对一个字符串的行为也值得留意:append 会将整个字符串作为一个元素,而 extend 会把字符串拆成单个字符来添加。

# 字符串示例
lst1 = []
lst1.append('ab')
print(lst1)  # ['ab']

lst2 = []
lst2.extend('ab')
print(lst2)  # ['a', 'b']

在性能角度,append 与 extend 的成本与迭代数量相关,通常在相同数量的元素添加下,两者耗时接近,但多次 append 可能带来更高的函数调用开销。

# 性能对比:简单对比节约度
import time

def bench_append(n):
    lst = []
    t0 = time.perf_counter()
    for i in range(n):
        lst.append(i)
    t1 = time.perf_counter()
    return t1 - t0

def bench_extend(n):
    lst = []
    t0 = time.perf_counter()
    lst.extend(range(n))
    t1 = time.perf_counter()
    return t1 - t0

print(bench_append(1000000), bench_extend(1000000))

性能分析:append 的时间复杂度与内存影响

时间胸褶与扩容机制

如前所述,append 的摊销时间复杂度为常数时间 O(1),但当现有容量不足时,解释器会触发扩容,涉及拷贝当前元素,成本在短时间内增加。

内存层面,Python 的列表使用连续内存块存放对象引用,扩容时通常会分配更大的空间以减少频繁重新分配。

# 简化说明:内部结构是一个对对象引用的数组
lst = [1,2,3]
print(len(lst), id(lst))

简单的微基准(风险:受硬件影响)可以帮助理解趋势:较高的数据量下,append 的耗时呈现出线性增长的模式,但单次操作维持在较低的常数时间

# 简单的微基准(风险:受硬件影响)
import time
n = 200000
lst = []
start = time.perf_counter()
for i in range(n):
    lst.append(i)
end = time.perf_counter()
print('append time:', end - start)

此外,使用 + 拼接列表会产生额外的拷贝成本,在大规模数据处理场景下往往显著高于单纯的 append 或 extend 的成本。

# 使用 + 拼接的代价示例
a = [1, 2, 3]
b = [4, 5]
c = a + b  # 产生新列表并拷贝元素
print(c)
广告

后端开发标签