列表是Python中最常用的可变序列类型,基础增删改查之外,推导式、切片、高阶函数和嵌套结构处理更能决定代码是否简洁、稳定。下面围绕这些用法给出可直接运行的示例,并说明容易踩坑的细节。
一、列表推导式:用一行表达式生成列表
列表推导式的基本形式是[表达式 for 元素 in 可迭代对象],带条件时写成[表达式 for 元素 in 可迭代对象 if 条件判断]。它把循环和条件判断压缩到一行,在编译层面减少了append等函数调用,效率通常比普通for循环高20%-50%。- square_list = [i ** 2 for i in range(1, 11)]
- print(square_list) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- even_list = [i for i in range(1, 21) if i % 2 == 0]
- print(even_list) # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
- num_list = [i for i in range(1, 101) if i % 3 == 0 and i % 5 == 0]
- print(num_list) # [15, 30, 45, 60, 75, 90]
- two_d_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
- flat_list = [num for sublist in two_d_list for num in sublist]
- print(flat_list) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
- even_flat_list = [num for sublist in two_d_list for num in sublist if num % 2 == 0]
- print(even_flat_list) # [2, 4, 6, 8]
复制代码 嵌套推导式适合把二维列表扁平化,也适合在多层循环中直接带条件筛选。数据量较大时,优先考虑推导式而不是先建空列表再循环append。
二、切片进阶:步长、逆序和切片赋值
切片完整语法是list[start:end:step]。start默认0,end默认列表长度且不包含end本身,step默认1,可为正负;step为0会抛出ValueError。切片返回的是列表的浅拷贝,修改切片结果本身不会影响原列表;start或end超出索引范围时不会报错,只会取到列表边界。- lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- print(lst[::2]) # [0, 2, 4, 6, 8]
- print(lst[1::3]) # [1, 4, 7]
- print(lst[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
- print(lst[5:2:-1]) # [5, 4, 3]
- lst = [1, 2, 3, 4, 5]
- lst[1:4] = [10, 20, 30]
- print(lst) # [1, 10, 20, 30, 5]
- del lst[2:5]
- print(lst) # [1, 10]
- lst[1:1] = [5, 6, 7]
- print(lst) # [1, 5, 6, 7, 10]
复制代码 切片赋值可以批量替换元素,并且允许赋值长度与原切片长度不一致,列表会自动调整长度;空切片赋值则可以完成插入操作。
三、map、filter、reduce与列表配合
map把函数应用到每个元素上,filter保留判断结果为True的元素,二者返回迭代器,通常需要用list转换。reduce用于从左到右累积计算,Python3中需要从functools导入。- num_list = [1, 2, 3, 4, 5]
- str_list = list(map(str, num_list))
- print(str_list) # ['1', '2', '3', '4', '5']
- square_list = list(map(lambda x: x ** 2, num_list))
- print(square_list) # [1, 4, 9, 16, 25]
- list1 = [1, 2, 3]
- list2 = [4, 5, 6]
- sum_list = list(map(lambda x, y: x + y, list1, list2))
- print(sum_list) # [5, 7, 9]
- num_list = [-2, -1, 0, 1, 2, 3]
- positive_list = list(filter(lambda x: x > 0, num_list))
- print(positive_list) # [1, 2, 3]
- str_list = ['a', '', 'b', None, 'c', ' ']
- non_empty_list = list(filter(lambda x: x and x.strip(), str_list))
- print(non_empty_list) # ['a', 'b', 'c']
- from functools import reduce
- num_list = [1, 2, 3, 4, 5]
- total = reduce(lambda x, y: x + y, num_list)
- print(total) # 15
- product = reduce(lambda x, y: x * y, num_list)
- print(product) # 120
- total_with_init = reduce(lambda x, y: x + y, num_list, 10)
- print(total_with_init) # 25
复制代码 多列表映射时,map可以接收多个可迭代对象;reduce带初始值时,会先把初始值加入累积过程。
四、嵌套列表:遍历、推导式与深浅拷贝
嵌套列表可以用双层for循环遍历,也可以用推导式生成新结构。真正需要警惕的是拷贝:切片和copy()只拷贝外层,内层列表仍是引用;copy.deepcopy()才会连内外层一起复制。- import copy
- two_d_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
- new_list = [[num * 2 for num in sublist] for sublist in two_d_list]
- print(new_list) # [[2, 4, 6], [8, 10, 12], [14, 16, 18]]
- shallow_copy = two_d_list[:]
- shallow_copy[0][0] = 100
- print(two_d_list) # [[100, 2, 3], [4, 5, 6], [7, 8, 9]]
- deep_copy = copy.deepcopy(two_d_list)
- deep_copy[0][0] = 200
- print(two_d_list) # [[100, 2, 3], [4, 5, 6], [7, 8, 9]]
- print(deep_copy) # [[200, 2, 3], [4, 5, 6], [7, 8, 9]]
复制代码 浅拷贝后修改内层元素会影响原列表,这是嵌套结构中最容易误判的行为;需要完全独立的数据时,应使用deepcopy。
五、去重、排序和高效拼接
set去重效率高但不保证原顺序;dict.fromkeys()在Python3.7+可保持原顺序。sort()是原地排序,sorted()返回新列表,二者都支持自定义key函数。频繁用+拼接列表会不断生成新列表,批量扩展更推荐extend()。- lst = [2, 1, 3, 2, 4, 1, 5, 3]
- unique_list1 = list(set(lst))
- print(unique_list1) # 顺序可能变化
- unique_list2 = list(dict.fromkeys(lst))
- print(unique_list2) # [2, 1, 3, 4, 5]
- str_list = ['apple', 'banana', 'cherry', 'date']
- str_list.sort(key=len)
- print(str_list) # ['date', 'apple', 'banana', 'cherry']
- tuple_list = [(1, 3), (2, 1), (3, 2)]
- sorted_list = sorted(tuple_list, key=lambda x: x[1])
- print(sorted_list) # [(2, 1), (3, 2), (1, 3)]
- dict_list = [{'name': 'Alice', 'age': 28}, {'name': 'Bob', 'age': 25}, {'name': 'Charlie', 'age': 30}]
- dict_list.sort(key=lambda x: x['age'], reverse=True)
- print(dict_list) # [{'name': 'Charlie', 'age': 30}, {'name': 'Alice', 'age': 28}, {'name': 'Bob', 'age': 25}]
- lst1 = [1, 2, 3]
- lst2 = [4, 5, 6]
- lst1.extend(lst2)
- print(lst1) # [1, 2, 3, 4, 5, 6]
- lst = [1, 2, 3]
- lst.extend([0] * 5)
- print(lst) # [1, 2, 3, 0, 0, 0, 0, 0]
复制代码 按字符串长度排序、按元组第二个元素排序、按字典某个键排序,都是key函数的典型场景;reverse=True用于降序。
六、常见坑点与避坑方式
第一个坑是列表作为函数默认参数。列表是可变对象,默认参数会被缓存,多次调用会累积修改。正确做法是用None作为默认值,在函数内部再创建新列表。- def add_item_bad(item, lst=[]):
- lst.append(item)
- return lst
- print(add_item_bad(1)) # [1]
- print(add_item_bad(2)) # [1, 2] # 默认列表被复用
- def add_item(item, lst=None):
- if lst is None:
- lst = []
- lst.append(item)
- return lst
- print(add_item(1)) # [1]
- print(add_item(2)) # [2]
复制代码 第二个坑是遍历列表时直接删除元素,索引会错位,可能漏删。可以遍历列表拷贝,或者倒序遍历删除。- lst = [1, 2, 3, 4, 5]
- for num in lst.copy():
- if num % 2 == 0:
- lst.remove(num)
- print(lst) # [1, 3, 5]
- lst = [1, 2, 3, 4, 5]
- for i in range(len(lst) - 1, -1, -1):
- if lst[i] % 2 == 0:
- lst.pop(i)
- print(lst) # [1, 3, 5]
复制代码 第三个坑是浅拷贝与深拷贝混用。嵌套列表用切片或copy()后,内层仍是引用;修改内层会反向影响原数据。需要独立副本时使用deepcopy。
七、使用建议
大规模数据生成优先用列表推导式,批量元素处理用map/filter,累积计算用reduce,嵌套列表注意深浅拷贝差异;频繁拼接列表时避免反复使用+,优先extend()。把这些用法和坑点落实到日常脚本、数据处理和业务代码中,列表的简洁性和执行效率才能真正发挥出来。 |