掌握Python核心数据结构,让代码效率翻倍

大家好,我是专注于Python技术的资深开发者。今天我们来深入探讨Python中最重要的知识点之一——容器数据类型。无论你是刚入门的新手,还是想要巩固基础的老手,这篇文章都能帮你彻底理解Python的四大核心容器:列表、元组、字典、集合。

一、什么是序列?

在Python中,序列是一种基本且核心的数据结构,它以有序的方式存储和操作数据。序列中的每个元素都有一个索引位置,我们可以通过索引来访问和修改这些元素。

Python中常见的序列类型包括:列表(List)元组(Tuple)字符串(String)

序列的通用操作

所有序列类型都支持以下操作:

python

# 索引 - 通过位置获取元素
sequence[0]      # 获取第一个元素
sequence[-1]     # 获取最后一个元素

# 切片 - 获取子序列
sequence[1:3]    # 获取索引1到2的元素
sequence[:3]     # 获取前3个元素
sequence[2:]     # 获取索引2之后的所有元素
sequence[::-1]   # 反转序列

# 相加 - 合并两个序列
sequence1 + sequence2

# 乘法 - 重复序列
sequence * 3

# 成员检查
x in sequence    # 检查x是否在序列中

# 内置函数
len(sequence)    # 长度
max(sequence)    # 最大值
min(sequence)    # 最小值

二、列表(List):最灵活的容器

列表是Python中使用最广泛的数据结构,它可变、有序,可以存储任意类型的元素。

2.1 创建列表

python

# 基本创建方式
list1 = [100, 200, 300, 400, 500]
list2 = []  # 空列表
list3 = list()  # 使用构造器创建空列表

# 列表可以包含不同类型的元素
mixed_list = [1, "hello", 3.14, True]

2.2 列表的索引机制

列表中的每个元素都有对应的索引,支持正向索引(从0开始)和负向索引(从-1开始):

text

索引:    0      1      2      3      4
列表: [100,   200,   300,   400,   500]
负索引:  -5     -4     -3     -2     -1

2.3 访问与切片

python

list1 = [100, 200, 300, 400, 500]

# 索引访问
print(list1[1])    # 输出: 200
print(list1[-2])   # 输出: 400

# 切片操作
print(list1[:])     # [100, 200, 300, 400, 500] - 复制整个列表
print(list1[2:4])   # [300, 400] - 索引2到3(不包含4)
print(list1[2:])    # [300, 400, 500] - 索引2到末尾
print(list1[:-2])   # [100, 200, 300] - 开头到索引-2(不包含)
print(list1[2:-1])  # [300, 400] - 索引2到-1(不包含)
print(list1[::-1])  # [500, 400, 300, 200, 100] - 倒序

2.4 添加元素

python

list1 = [100, 200, 300, 400, 500]

# append() - 在末尾添加
list1.append(600)
print(list1)  # [100, 200, 300, 400, 500, 600]

# insert() - 在指定位置插入
list1.insert(2, 700)  # 在索引2的位置插入700
print(list1)  # [100, 200, 700, 300, 400, 500, 600]

# extend() - 扩展列表
list2 = [1, 2, 3]
list1.extend(list2)  # 将list2的元素追加到list1末尾
print(list1)  # [100, 200, 700, 300, 400, 500, 600, 1, 2, 3]

2.5 修改元素

python

list1 = [100, 200, 300, 400, 500]

# 通过下标修改
list1[0] = -1
print(list1)  # [-1, 200, 300, 400, 500]

# 通过切片修改
list1[2:4] = ["a", "b", "c"]
print(list1)  # [-1, 200, 'a', 'b', 'c', 500]

2.6 删除元素

python

list1 = [100, 200, 300, 400, 500]

# del - 删除指定位置
del list1[2]
print(list1)  # [100, 200, 400, 500]

# remove() - 删除第一次出现的指定值
list1.remove(200)
print(list1)  # [100, 400, 500]

# pop() - 弹出指定位置元素(默认弹出最后一个)
popped = list1.pop()
print(popped)  # 500
print(list1)   # [100, 400]

# clear() - 清空列表
list1.clear()
print(list1)  # []

2.7 列表遍历

python

list1 = [100, 200, 300, 400, 500]

# 方式1:直接遍历元素
for i in list1:
    print(i)

# 方式2:通过索引遍历
for i in range(len(list1)):
    print(i, list1[i])

# 方式3:使用enumerate()同时获取索引和值
for i, val in enumerate(list1):
    print(i, val)

2.8 列表推导式

列表推导式是Python中最优雅的特性之一,它让你用一行代码就能创建复杂的列表:

python

# 基础列表推导式 - 生成0-4的平方
squares = [x**2 for x in range(5)]
print(squares)  # [0, 1, 4, 9, 16]

# 带条件的列表推导式 - 只取偶数的平方
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# 使用现有列表
list1 = [1, 2, 3, 4, 5]
squares2 = [x**2 for x in list1]
print(squares2)  # [1, 4, 9, 16, 25]

# 多重循环 - 生成笛卡尔积
list1 = [1, 2, 3]
list2 = ["a", "b", "c"]
pairs = [(i, j) for i in list1 for j in list2]
print(pairs)  # [(1,'a'), (1,'b'), (1,'c'), (2,'a'), ...]

2.9 zip()函数

zip()函数可以将多个可迭代对象中对应位置的元素打包成元组:

python

list1 = [1, 2, 3, 4, 5]
list2 = ["a", "b", "c", "d", "e"]
zipped = zip(list1, list2)
print(list(zipped))  # [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')]

# 如果长度不同,zip()会以最短的为准
list3 = [1, 2, 3]
list4 = ["a", "b", "c", "d", "e"]
print(list(zip(list3, list4)))  # [(1, 'a'), (2, 'b'), (3, 'c')]

2.10 列表的常用函数汇总

函数 说明
list.append(x) 在列表末尾追加x
list.insert(index,x) 在指定位置插入x
list.extend(iterable) 将可迭代对象的所有元素追加到列表末尾
list.remove(x) 删除第一次出现的x
list.pop([index]) 删除指定位置的数据,默认为末尾数据
list.clear() 清空列表中元素
list.index(x[,start[,end]]) 返回x在列表中首次出现的位置
list.count(x) 返回x的数量
list.sort([reverse=True]) 对列表就地排序
list.reverse() 反转列表中的元素
list.copy() 拷贝列表

三、字符串(String):不可变的文本序列

字符串是Python中最常用的数据类型之一,它不可变、有序,用于存储文本数据。

3.1 字符串的特性

python

# 创建字符串
str1 = "hello world"
str2 = 'Python is great'
str3 = """多行
字符串"""

# 索引和切片
print(str1[0])      # h
print(str1[-1])     # d
print(str1[4:-3])   # o w

# 字符串拼接
str1 = "hello"
str2 = "world"
print(str1 + " " + str2)  # hello world

# 字符串重复
print("ha" * 3)  # hahaha

# 成员检查
print("lo" in "hello")  # True

3.2 原始字符串

当字符串中包含大量转义字符时,可以使用原始字符串(在引号前加rR):

python

# 普通字符串:\n会被解析为换行
print("hello\nworld")
# 输出:
# hello
# world

# 原始字符串:所有字符按字面意思处理
print(r"hello\nworld")
# 输出: hello\nworld

3.3 字符串常用方法

python

# 大小写转换
text = "Hello World"
print(text.upper())          # HELLO WORLD
print(text.lower())          # hello world
print(text.swapcase())       # hELLO wORLD
print(text.capitalize())     # Hello world
print(text.title())          # Hello World

# 查找与替换
text = "hello world, hello python"
print(text.find("world"))    # 6 - 返回第一次出现的位置
print(text.replace("hello", "hi"))  # hi world, hi python

# 分割与连接
words = text.split()         # 默认按空白分割
print(words)  # ['hello', 'world,', 'hello', 'python']

joined = " ".join(words)     # 用空格连接
print(joined)  # hello world, hello python

# 去除空白
text = "  hello  "
print(text.strip())          # "hello" - 去除两端空白
print(text.lstrip())         # "hello  " - 去除左侧空白
print(text.rstrip())         # "  hello" - 去除右侧空白

# 检查开头/结尾
filename = "document.pdf"
print(filename.startswith("doc"))  # True
print(filename.endswith(".pdf"))   # True

3.4 字符串格式化

python

# f-string(Python 3.6+)
name = "Alice"
age = 18
print(f"{name} is {age} years old")

# format()方法
print("{} is {} years old".format(name, age))

# %格式化(传统方式)
print("%s is %d years old" % (name, age))

四、元组(Tuple):不可变的序列

元组与列表非常相似,但一旦创建就不能修改。这种不可变性让元组在某些场景下更安全、更高效。

4.1 创建元组

python

# 基本创建
tuple1 = (100, 200, 300, 400, 500)
tuple2 = ()  # 空元组
tuple3 = tuple()  # 使用构造器

# 特别注意:单元素元组需要加逗号
tuple4 = (100,)   # 正确:这是一个元组
not_tuple = (100) # 错误:这是一个整数

# 元组推导式返回的是生成器
tuple_generator = (x for x in range(5))
print(type(tuple_generator))  # <class 'generator'>
tuple5 = tuple(tuple_generator)  # 转换为元组
print(tuple5)  # (0, 1, 2, 3, 4)

4.2 元组的操作

元组支持的操作与列表类似,但由于不可变性,没有添加、修改、删除元素的方法:

python

tuple1 = (100, 200, 300, 400, 500)

# 索引和切片
print(tuple1[2])     # 300
print(tuple1[-1])    # 500
print(tuple1[2:4])   # (300, 400)

# 拼接和重复
tuple2 = ("a", "b", "c")
print(tuple1 + tuple2)  # (100, 200, 300, 400, 500, 'a', 'b', 'c')
print(tuple1 * 2)       # (100, 200, 300, 400, 500, 100, 200, 300, 400, 500)

# 成员检查
print(300 in tuple1)    # True

# 内置函数
print(len(tuple1))      # 5
print(max(tuple1))      # 500
print(min(tuple1))      # 100
print(sum(tuple1))      # 1500

4.3 元组的"可变"陷阱

元组的不可变性是指元组对象本身不可变,但如果元组包含可变对象(如列表),这些可变对象的内容是可以修改的:

python

tuple1 = (100, 200, 300, [1, 2, 3])
print(id(tuple1))  # 查看内存地址

# 修改元组中的列表
tuple1[3].append(4)
print(tuple1)  # (100, 200, 300, [1, 2, 3, 4])
print(id(tuple1))  # 内存地址不变

# 但尝试修改元组的元素会报错
# tuple1[0] = 999  # TypeError: 'tuple' object does not support item assignment

4.4 元组的实际应用

python

# 1. 函数返回多个值
def get_user_info():
    name = "Alice"
    age = 18
    city = "Beijing"
    return name, age, city  # 实际返回的是元组

info = get_user_info()
print(info)  # ('Alice', 18, 'Beijing')
name, age, city = get_user_info()  # 解包
print(name)  # Alice

# 2. 作为字典的键(列表不能作为字典键)
locations = {}
locations[("Beijing", "China")] = "Capital"  # 元组作为键

# 3. 保护数据不被修改
COORDINATES = (39.9042, 116.4074)  # 坐标常量

五、集合(Set):无序的不重复元素集

集合是一个无序、元素唯一的数据结构,非常适合去重和集合运算。

5.1 创建集合

python

# 使用花括号
set1 = {1, 2, 3, 4, 5}

# 使用set()函数
set2 = set([1, 2, 3, 3, 4, 5])  # 自动去重,结果: {1, 2, 3, 4, 5}
set3 = set("hello")  # 字符串转换为集合,结果: {'h', 'e', 'l', 'o'}

# 空集合必须用set(),{}是空字典
empty_set = set()
empty_dict = {}

# 集合推导式
set4 = {x for x in range(10) if x % 2 == 0}
print(set4)  # {0, 2, 4, 6, 8}

5.2 集合的基本操作

python

# 添加元素
set1 = {1, 2, 3}
set1.add(4)
print(set1)  # {1, 2, 3, 4}

# update() - 添加多个元素
set1.update([5, 6, 7])
print(set1)  # {1, 2, 3, 4, 5, 6, 7}

# 删除元素
set1.remove(4)   # 元素不存在时抛出KeyError
set1.discard(10) # 元素不存在时什么也不做
print(set1.pop())  # 随机弹出并返回一个元素
set1.clear()  # 清空集合

5.3 集合运算

集合支持数学上的集合运算,非常直观:

python

A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}

# 并集(union)
print(A | B)        # {1, 2, 3, 4, 5, 6, 7, 8}
print(A.union(B))   # 同上

# 交集(intersection)
print(A & B)        # {4, 5}
print(A.intersection(B))  # 同上

# 差集(difference)
print(A - B)        # {1, 2, 3} - 在A中但不在B中
print(B - A)        # {6, 7, 8} - 在B中但不在A中

# 对称差集(symmetric difference)
print(A ^ B)        # {1, 2, 3, 6, 7, 8} - 不在交集中的所有元素
print(A.symmetric_difference(B))  # 同上

# 子集判断
C = {1, 2}
print(C <= A)   # True - C是A的子集
print(A >= C)   # True - A是C的超集
print(A.isdisjoint(B))  # False - 有交集

5.4 集合的实用场景

python

# 1. 列表去重
list_with_duplicates = [1, 2, 2, 3, 3, 3, 4, 5, 5]
unique_list = list(set(list_with_duplicates))
print(unique_list)  # [1, 2, 3, 4, 5]

# 2. 快速成员检查
valid_ids = {1001, 1002, 1003, 1004, 1005}
if 1003 in valid_ids:  # O(1)时间复杂度,比列表快得多
    print("ID有效")

# 3. 找出两个列表的共同元素
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
common = set(list1) & set(list2)
print(common)  # {4, 5}

六、字典(Dictionary):键值对的映射

字典是Python中实现键值对映射的数据结构,键唯一且不可变,值可以是任意类型。

6.1 创建字典

python

# 使用花括号
dict1 = {"name": "Alice", "age": 18, "gender": "male"}

# 使用dict()构造器
dict2 = dict(name="Bob", age=20, gender="female")
dict3 = dict([("name", "Tom"), ("age", 22), ("gender", "male")])

# 空字典
empty_dict1 = {}
empty_dict2 = dict()

# 字典推导式
squares = {x: x**2 for x in range(5)}
print(squares)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

6.2 访问和修改

python

dict1 = {"name": "Alice", "age": 18, "gender": "male"}

# 通过键访问
print(dict1["name"])     # Alice
# print(dict1["address"]) # 键不存在时抛出KeyError

# get()方法 - 更安全
print(dict1.get("name"))          # Alice
print(dict1.get("address"))       # None
print(dict1.get("address", "earth"))  # earth - 指定默认值

# 添加/修改元素
dict1["address"] = "earth"  # 键不存在则添加
dict1["age"] = 19           # 键存在则修改
print(dict1)  # {'name': 'Alice', 'age': 19, 'gender': 'male', 'address': 'earth'}

# update() - 批量更新
dict1.update({"city": "Beijing", "age": 20})
print(dict1)  # 更新age,添加city

6.3 删除元素

python

my_dict = {'Name': 'Tom', 'Age': 17, 'City': 'Beijing'}

# del - 删除指定键
del my_dict['Age']
print(my_dict)  # {'Name': 'Tom', 'City': 'Beijing'}

# pop() - 弹出并返回值
name = my_dict.pop('Name')
print(name)  # Tom
print(my_dict)  # {'City': 'Beijing'}

# popitem() - 弹出最后插入的键值对(Python 3.7+)
my_dict['Age'] = 17
my_dict['Gender'] = 'male'
last_item = my_dict.popitem()
print(last_item)  # ('Gender', 'male')
print(my_dict)    # {'City': 'Beijing', 'Age': 17}

# clear() - 清空
my_dict.clear()
print(my_dict)  # {}

6.4 字典遍历

python

my_dict = {"name": "Alice", "age": 18, "city": "Beijing"}

# 遍历所有键
for key in my_dict.keys():
    print(key)

# 遍历所有值
for value in my_dict.values():
    print(value)

# 遍历键值对
for key, value in my_dict.items():
    print(f"{key}: {value}")

# 检查键是否存在
if "name" in my_dict:
    print("name exists")

6.5 字典的常用方法

方法 说明
dict.keys() 返回所有键的视图对象
dict.values() 返回所有值的视图对象
dict.items() 返回所有键值对的视图对象
dict.get(key[,default]) 安全获取值,可设默认值
dict.setdefault(key[,default]) 获取值,键不存在则设置默认值
dict.update(other) 用其他字典更新当前字典
dict.pop(key[,default]) 弹出指定键的值
dict.popitem() 弹出最后插入的键值对
dict.clear() 清空字典
dict.copy() 浅拷贝字典

七、四大容器的对比与选择

数据结构 是否可变 是否允许重复 是否有序 定义符号 适用场景
列表 可变 允许 有序 []list() 存储有序数据,需要频繁增删改查
元组 不可变 允许 有序 ()tuple() 存储不变的数据,作为字典键
字典 可变 键不允许,值允许 键有序* {}dict() 键值对映射,快速查找
集合 可变 不允许 无序 {}set() 去重、成员检查、集合运算

*注:Python 3.7+版本中,字典保持插入顺序;Python 3.6中作为实现细节,3.7后成为语言特性。

如何选择合适的数据结构?

  1. 需要保持顺序? → 选择列表或元组

  2. 需要修改数据? → 选择列表、字典或集合(元组不可变)

  3. 需要快速查找? → 选择字典或集合(哈希表,O(1)时间复杂度)

  4. 需要去除重复? → 选择集合

  5. 需要键值映射? → 选择字典

  6. 数据需要作为字典键? → 选择元组(不可变类型)

  7. 需要存储同类型数据且需要排序? → 选择列表

八、高级技巧与最佳实践

8.1 列表 vs 元组性能

元组通常比列表更高效,因为不可变性允许Python进行优化:

python

import sys

list_obj = [1, 2, 3, 4, 5]
tuple_obj = (1, 2, 3, 4, 5)

print(sys.getsizeof(list_obj))   # 约 56 字节
print(sys.getsizeof(tuple_obj))  # 约 48 字节

8.2 深拷贝与浅拷贝

python

import copy

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

# 浅拷贝 - 只复制外层,内层列表仍是引用
shallow = original.copy()
shallow[0][0] = 999
print(original)  # [[999, 2, 3], [4, 5, 6]] - 原数据被修改

# 深拷贝 - 完全复制所有层级
original = [[1, 2, 3], [4, 5, 6]]
deep = copy.deepcopy(original)
deep[0][0] = 999
print(original)  # [[1, 2, 3], [4, 5, 6]] - 原数据不变

8.3 字典的默认值处理

python

from collections import defaultdict

# 传统方式
my_dict = {}
if 'count' not in my_dict:
    my_dict['count'] = 0
my_dict['count'] += 1

# 使用setdefault
my_dict.setdefault('count', 0)
my_dict['count'] += 1

# 使用defaultdict
my_dict = defaultdict(int)
my_dict['count'] += 1  # 自动初始化为0

8.4 列表推导式 vs map/filter

python

# 列表推导式(Pythonic)
squares = [x**2 for x in range(10) if x % 2 == 0]

# 传统map/filter
squares = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, range(10))))

# 列表推导式更简洁易读

总结

Python的容器数据类型是编程的基础,熟练掌握它们能让你的代码更加优雅高效:

  1. 列表是最灵活的容器,适合需要频繁修改的有序数据

  2. 元组是不可变的安全选择,适合保护数据不被意外修改

  3. 字典提供了最快的键值查找,适合映射关系

  4. 集合擅长去重和数学集合运算

在实际开发中,选择合适的数据结构能显著提升代码性能和可读性。建议根据数据的特性和操作需求来选择:

  • 需要顺序、允许重复 → 列表

  • 需要顺序、不允许修改 → 元组

  • 需要快速查找、键值映射 → 字典

  • 需要去重、集合运算 → 集合

希望这篇文章能帮助你彻底掌握Python的容器数据类型。如果觉得有用,欢迎点赞收藏,也欢迎在评论区留言讨论!

*本文内容基于Python 3.10+版本,不同版本间可能有细微差异。*

更多推荐