Python数据结构详解

数据结构是计算机科学中组织和存储数据的方式,它决定了数据如何被访问、修改和处理。Python提供了丰富的数据结构,包括内置的数据结构和标准库中的高级数据结构。本文将详细介绍Python数据结构的概念、特性、用法和最佳实践。

一、数据结构概述

1. 什么是数据结构?

数据结构是一种组织和存储数据的方式,它定义了数据之间的关系、操作数据的方法以及数据的访问效率。数据结构的选择直接影响程序的性能和可维护性。

2. Python数据结构的分类

Python数据结构可以分为以下几类:

2.1 内置基本数据结构

  • 数值类型(Numeric):整数、浮点数、复数、布尔值
  • 序列类型(Sequence):列表、元组、字符串、字节串
  • 映射类型(Mapping):字典
  • 集合类型(Set):集合、冻结集合

2.2 标准库中的高级数据结构

  • collections模块:OrderedDict、defaultdict、Counter、deque、namedtuple
  • heapq模块:堆(优先队列)
  • bisect模块:二分查找
  • array模块:数组
  • struct模块:结构体
  • queue模块:队列、栈、优先级队列
  • graphlib模块:图结构

3. 数据结构的重要性

选择合适的数据结构对于程序的性能和可维护性至关重要:

  • 性能:不同的数据结构在时间复杂度和空间复杂度上有很大差异
  • 可维护性:合适的数据结构可以使代码更加清晰、易读
  • 功能:不同的数据结构支持不同的操作,选择合适的数据结构可以简化代码

二、内置基本数据结构

1. 数值类型

1.1 整数(int)

整数是Python中最基本的数值类型,支持任意精度的整数运算。

# 整数示例
x = 10          # 十进制整数
y = 0b1010      # 二进制整数
z = 0o12        # 八进制整数
w = 0xa         # 十六进制整数

print(x)  # 输出:10
print(y)  # 输出:10
print(z)  # 输出:10
print(w)  # 输出:10

# 整数运算
print(x + y)  # 输出:20
print(x - y)  # 输出:0
print(x * y)  # 输出:100
print(x / y)  # 输出:1.0
print(x // y) # 输出:1
print(x % y)  # 输出:0
print(x ** y) # 输出:10000000000

1.2 浮点数(float)

浮点数用于表示实数,使用双精度浮点数(64位)存储。

# 浮点数示例
x = 3.14
y = 1.23e4  # 科学计数法,相当于12300.0
z = 1.23e-3 # 科学计数法,相当于0.00123

print(x)  # 输出:3.14
print(y)  # 输出:12300.0
print(z)  # 输出:0.00123

# 浮点数运算
print(x + y)  # 输出:12303.14
print(x - y)  # 输出:-12296.86
print(x * y)  # 输出:38622.0
print(x / y)  # 输出:0.00025528455284552846

1.3 复数(complex)

复数由实部和虚部组成,虚部使用j或J表示。

# 复数示例
x = 3 + 4j
print(x)       # 输出:(3+4j)
print(x.real)  # 输出:3.0(实部)
print(x.imag)  # 输出:4.0(虚部)
print(x.conjugate())  # 输出:(3-4j)(共轭复数)

# 复数运算
y = 5 + 6j
print(x + y)  # 输出:(8+10j)
print(x - y)  # 输出:(-2-2j)
print(x * y)  # 输出:(-9+38j)
print(x / y)  # 输出:(0.639344262295082+0.03278688524590164j)

1.4 布尔值(bool)

布尔值表示真或假,True对应整数1,False对应整数0。

# 布尔值示例
x = True
print(x)      # 输出:True
print(int(x)) # 输出:1

# 布尔运算
print(x and False)  # 输出:False
print(x or False)   # 输出:True
print(not x)        # 输出:False

# 比较运算
print(5 > 3)  # 输出:True
print(5 == 3) # 输出:False
print(5 < 3)  # 输出:False

2. 序列类型

序列类型是Python中最常用的数据结构之一,它们支持索引、切片、迭代等操作。

2.1 列表(list)

列表是Python中最灵活的序列类型,它可以存储任意类型的元素,并且支持动态修改。

# 列表示例
# 创建列表
empty_list = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]

# 访问元素
print(numbers[0])   # 输出:1(索引从0开始)
print(numbers[-1])  # 输出:5(负数索引从末尾开始)

# 切片
print(numbers[1:3])    # 输出:[2, 3](索引1到2,不包含3)
print(numbers[:3])     # 输出:[1, 2, 3](从开头到索引2)
print(numbers[3:])     # 输出:[4, 5](从索引3到末尾)
print(numbers[::2])    # 输出:[1, 3, 5](步长为2)

# 修改元素
numbers[0] = 10
print(numbers)  # 输出:[10, 2, 3, 4, 5]

# 添加元素
numbers.append(6)      # 在末尾添加元素
print(numbers)         # 输出:[10, 2, 3, 4, 5, 6]
numbers.insert(0, 0)   # 在指定位置插入元素
print(numbers)         # 输出:[0, 10, 2, 3, 4, 5, 6]

# 删除元素
numbers.remove(10)     # 删除指定值的元素
print(numbers)         # 输出:[0, 2, 3, 4, 5, 6]
popped = numbers.pop() # 移除并返回末尾元素
print(popped)          # 输出:6
print(numbers)         # 输出:[0, 2, 3, 4, 5]

# 其他操作
print(len(numbers))        # 输出:5(长度)
print(3 in numbers)        # 输出:True(元素是否存在)
print(numbers.index(3))    # 输出:2(元素的索引)
print(numbers.count(3))    # 输出:1(元素出现的次数)
numbers.sort()             # 排序
print(numbers)             # 输出:[0, 2, 3, 4, 5]
numbers.reverse()          # 反转
print(numbers)             # 输出:[5, 4, 3, 2, 0]

# 列表推导式
squares = [i ** 2 for i in range(10)]
print(squares)  # 输出:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 嵌套列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix[0][1])  # 输出:2(第一行第二列)

列表的性能分析:

  • 访问元素:O(1)
  • 插入/删除(末尾):O(1)
  • 插入/删除(中间):O(n)
  • 排序:O(n log n)

列表的应用场景:

  • 存储有序的元素集合
  • 需要频繁修改的元素集合
  • 实现栈或队列(使用append/pop)

2.2 元组(tuple)

元组是一种不可变的序列类型,一旦创建就不能修改。

# 元组示例
# 创建元组
empty_tuple = ()
numbers = (1, 2, 3, 4, 5)
mixed = (1, "hello", 3.14, True)
single_tuple = (5, )  # 单个元素的元组需要加逗号

# 访问元素
print(numbers[0])   # 输出:1
print(numbers[-1])  # 输出:5

# 切片
print(numbers[1:3])  # 输出:(2, 3)

# 元组解包
x, y, z = (1, 2, 3)
print(x, y, z)  # 输出:1 2 3

# 其他操作
print(len(numbers))        # 输出:5
print(3 in numbers)        # 输出:True
print(numbers.index(3))    # 输出:2
print(numbers.count(3))    # 输出:1

# 元组推导式(实际是生成器表达式)
tuple_comp = tuple(i ** 2 for i in range(10))
print(tuple_comp)  # 输出:(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

元组与列表的区别:

  • 元组是不可变的,列表是可变的
  • 元组使用小括号,列表使用方括号
  • 元组的性能通常比列表好
  • 元组可以作为字典的键,列表不行

元组的应用场景:

  • 存储不可修改的数据
  • 作为字典的键
  • 函数返回多个值
  • 保护数据不被修改

2.3 字符串(str)

字符串是一种不可变的序列类型,用于表示文本数据。

# 字符串示例
# 创建字符串
s1 = "Hello"
s2 = 'World'
s3 = """这是一个多行
字符串"""

# 访问字符
print(s1[0])   # 输出:H
print(s1[-1])  # 输出:o

# 切片
print(s1[1:3])  # 输出:el

# 字符串拼接
print(s1 + " " + s2)  # 输出:Hello World

# 字符串复制
print(s1 * 3)  # 输出:HelloHelloHello

# 字符串方法
print(s1.lower())      # 输出:hello(转换为小写)
print(s1.upper())      # 输出:HELLO(转换为大写)
print(s1.strip())      # 输出:Hello(移除首尾空白)
print(s1.replace("H", "J"))  # 输出:Jello(替换字符)
print(s1.split("e"))   # 输出:['H', 'llo'](分割字符串)
print("e" in s1)       # 输出:True(判断子串是否存在)
print(s1.find("e"))    # 输出:1(查找子串的索引)
print(s1.startswith("H"))  # 输出:True(判断是否以指定前缀开头)
print(s1.endswith("o"))    # 输出:True(判断是否以指定后缀结尾)

# 字符串格式化
name = "张三"
age = 30
print(f"{name}今年{age}岁")  # 输出:张三今年30岁
print("{}今年{}岁".format(name, age))  # 输出:张三今年30岁
print("%s今年%d岁" % (name, age))  # 输出:张三今年30岁

# 字符串编码与解码
s = "你好"
encoded = s.encode("utf-8")
print(encoded)      # 输出:b'\xe4\xbd\xa0\xe5\xa5\xbd'
print(encoded.decode("utf-8"))  # 输出:你好

字符串的性能分析:

  • 访问字符:O(1)
  • 切片:O(k),k是切片的长度
  • 拼接:O(n+m)
  • 查找:O(n)

字符串的应用场景:

  • 存储文本数据
  • 数据处理和分析
  • 网络通信
  • 文件操作

2.4 字节串(bytes)

字节串是一种不可变的序列类型,用于表示二进制数据。

# 字节串示例
# 创建字节串
b1 = b"Hello"
b2 = bytes([72, 101, 108, 108, 111])

# 访问字节
print(b1[0])   # 输出:72
print(b1[-1])  # 输出:111

# 字节串操作
print(b1 + b" " + b"World")  # 输出:b'Hello World'
print(b1.find(b"e"))          # 输出:1
print(b"e" in b1)            # 输出:True

# 字节串与字符串的转换
s = b1.decode("utf-8")
print(s)  # 输出:Hello

b = s.encode("utf-8")
print(b)  # 输出:b'Hello'

字节串与字符串的区别:

  • 字节串存储二进制数据,字符串存储文本数据
  • 字节串使用ASCII码,字符串使用Unicode
  • 字节串使用b前缀,字符串没有

字节串的应用场景:

  • 处理二进制数据
  • 文件操作(二进制模式)
  • 网络通信
  • 加密解密

3. 映射类型

3.1 字典(dict)

字典是Python中唯一的映射类型,它存储键值对,键必须是可哈希的。

# 字典示例
# 创建字典
empty_dict = {}
person = {"name": "张三", "age": 30, "city": "北京"}

# 访问值
print(person["name"])       # 输出:张三
print(person.get("age"))    # 输出:30
print(person.get("email", "无"))  # 输出:无(键不存在时返回默认值)

# 修改字典
person["age"] = 31         # 修改已有键的值
person["email"] = "zhangsan@example.com"  # 添加新键值对
print(person)               # 输出:{'name': '张三', 'age': 31, 'city': '北京', 'email': 'zhangsan@example.com'}

# 删除键值对
del person["email"]        # 删除指定键
print(person)               # 输出:{'name': '张三', 'age': 31, 'city': '北京'}
popped = person.pop("city")  # 删除并返回指定键的值
print(popped)               # 输出:北京
print(person)               # 输出:{'name': '张三', 'age': 31}

# 字典遍历
print(person.keys())        # 输出:dict_keys(['name', 'age'])(所有键)
print(person.values())      # 输出:dict_values(['张三', 31])(所有值)
print(person.items())       # 输出:dict_items([('name', '张三'), ('age', 31)])(所有键值对)

# 遍历键
for key in person:
    print(key, person[key])

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

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

# 合并字典
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = {**dict1, **dict2}
print(merged)  # 输出:{'a': 1, 'b': 3, 'c': 4}(dict2的键会覆盖dict1的)

字典的性能分析:

  • 访问元素:O(1)
  • 插入元素:O(1)
  • 删除元素:O(1)
  • 遍历:O(n)

字典的应用场景:

  • 存储键值对数据
  • 快速查找和更新数据
  • 表示对象的属性
  • 缓存数据

4. 集合类型

集合类型用于存储唯一的元素,它们支持数学集合的操作。

4.1 集合(set)

集合是一种可变的集合类型,它存储唯一的、无序的元素。

# 集合示例
# 创建集合
empty_set = set()
numbers = {1, 2, 3, 4, 5}
unique_numbers = set([1, 2, 2, 3, 3, 3, 4, 5])  # 自动去重
print(unique_numbers)  # 输出:{1, 2, 3, 4, 5}

# 添加元素
numbers.add(6)
print(numbers)  # 输出:{1, 2, 3, 4, 5, 6}

# 删除元素
numbers.remove(6)  # 元素不存在会抛出KeyError异常
print(numbers)     # 输出:{1, 2, 3, 4, 5}
numbers.discard(6) # 元素不存在不会抛出异常

# 集合操作
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}

# 交集
print(set1 & set2)  # 输出:{4, 5}
print(set1.intersection(set2))  # 输出:{4, 5}

# 并集
print(set1 | set2)  # 输出:{1, 2, 3, 4, 5, 6, 7, 8}
print(set1.union(set2))  # 输出:{1, 2, 3, 4, 5, 6, 7, 8}

# 差集
print(set1 - set2)  # 输出:{1, 2, 3}
print(set1.difference(set2))  # 输出:{1, 2, 3}

# 对称差集
print(set1 ^ set2)  # 输出:{1, 2, 3, 6, 7, 8}
print(set1.symmetric_difference(set2))  # 输出:{1, 2, 3, 6, 7, 8}

# 子集和超集
print({1, 2, 3}.issubset(set1))  # 输出:True
print(set1.issuperset({1, 2, 3}))  # 输出:True

# 集合推导式
squares = {i ** 2 for i in range(10)}
print(squares)  # 输出:{0, 1, 4, 9, 16, 25, 36, 49, 64, 81}

4.2 冻结集合(frozenset)

冻结集合是一种不可变的集合类型,一旦创建就不能修改。

# 冻结集合示例
fs = frozenset([1, 2, 3, 4, 5])
print(fs)  # 输出:frozenset({1, 2, 3, 4, 5})

# 冻结集合的操作
set1 = frozenset([1, 2, 3, 4, 5])
set2 = frozenset([4, 5, 6, 7, 8])

print(set1 & set2)  # 输出:frozenset({4, 5})
print(set1 | set2)  # 输出:frozenset({1, 2, 3, 4, 5, 6, 7, 8})
print(set1 - set2)  # 输出:frozenset({1, 2, 3})

集合的应用场景:

  • 存储唯一元素
  • 去重
  • 数学集合运算(交集、并集、差集)
  • 快速判断元素是否存在

三、标准库中的高级数据结构

Python的标准库提供了许多高级数据结构,这些数据结构扩展了Python的内置功能,适用于更复杂的应用场景。

1. collections模块

collections模块提供了多种扩展的数据结构,包括:

1.1 OrderedDict

OrderedDict是一种有序的字典,它保留了键的插入顺序。

# OrderedDict示例
from collections import OrderedDict

# 创建OrderedDict
od = OrderedDict()
od["a"] = 1
od["b"] = 2
od["c"] = 3

# 遍历OrderedDict
for key, value in od.items():
    print(key, value)  # 输出:a 1, b 2, c 3(保持插入顺序)

# 重新插入已存在的键
od["a"] = 10
for key, value in od.items():
    print(key, value)  # 输出:a 10, b 2, c 3(键的位置不变)

# 移动键到末尾
od.move_to_end("a")
for key, value in od.items():
    print(key, value)  # 输出:b 2, c 3, a 10

1.2 defaultdict

defaultdict是一种字典,它为不存在的键提供默认值。

# defaultdict示例
from collections import defaultdict

# 创建defaultdict
# 使用int作为默认工厂函数
dd = defaultdict(int)

# 访问不存在的键
print(dd["a"])  # 输出:0(int()返回0)

# 统计单词频率
text = "hello world hello python"
word_count = defaultdict(int)
for word in text.split():
    word_count[word] += 1

print(dict(word_count))  # 输出:{'hello': 2, 'world': 1, 'python': 1}

# 使用list作为默认工厂函数
dd_list = defaultdict(list)
dd_list["a"].append(1)
dd_list["a"].append(2)
dd_list["b"].append(3)
print(dict(dd_list))  # 输出:{'a': [1, 2], 'b': [3]}

1.3 Counter

Counter是一种用于计数的字典子类,它可以快速统计元素的出现次数。

# Counter示例
from collections import Counter

# 创建Counter
c = Counter([1, 2, 2, 3, 3, 3, 4, 5])
print(c)  # 输出:Counter({3: 3, 2: 2, 1: 1, 4: 1, 5: 1})

# 统计单词频率
text = "hello world hello python"
word_count = Counter(text.split())
print(word_count)  # 输出:Counter({'hello': 2, 'world': 1, 'python': 1})

# 获取出现次数最多的元素
print(word_count.most_common(2))  # 输出:[('hello', 2), ('world', 1)]

# Counter运算
c1 = Counter([1, 2, 3, 4, 5])
c2 = Counter([4, 5, 6, 7, 8])
print(c1 + c2)  # 输出:Counter({4: 2, 5: 2, 1: 1, 2: 1, 3: 1, 6: 1, 7: 1, 8: 1})
print(c1 - c2)  # 输出:Counter({1: 1, 2: 1, 3: 1})
print(c1 & c2)  # 输出:Counter({4: 1, 5: 1})
print(c1 | c2)  # 输出:Counter({1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1})

1.4 deque

deque是一种双端队列,它支持在两端快速添加和删除元素。

# deque示例
from collections import deque

# 创建deque
d = deque([1, 2, 3, 4, 5])

# 两端操作
d.append(6)       # 在右侧添加元素
print(d)          # 输出:deque([1, 2, 3, 4, 5, 6])

d.appendleft(0)   # 在左侧添加元素
print(d)          # 输出:deque([0, 1, 2, 3, 4, 5, 6])

d.pop()           # 从右侧移除元素
print(d)          # 输出:deque([0, 1, 2, 3, 4, 5])

d.popleft()       # 从左侧移除元素
print(d)          # 输出:deque([1, 2, 3, 4, 5])

# 旋转
d.rotate(2)       # 向右旋转2位
print(d)          # 输出:deque([4, 5, 1, 2, 3])

d.rotate(-1)      # 向左旋转1位
print(d)          # 输出:deque([5, 1, 2, 3, 4])

# 限制deque的长度
d = deque([1, 2, 3], maxlen=3)
d.append(4)
print(d)  # 输出:deque([2, 3, 4])(超出maxlen的元素被自动移除)
d.appendleft(5)
print(d)  # 输出:deque([5, 2, 3])(超出maxlen的元素被自动移除)

deque的应用场景:

  • 实现栈或队列
  • 滑动窗口算法
  • 缓存
  • 多线程编程中的安全队列

1.5 namedtuple

namedtuple是一种命名元组,它为元组的元素提供了名称,提高了代码的可读性。

# namedtuple示例
from collections import namedtuple

# 创建namedtuple
Point = namedtuple("Point", ["x", "y"])

# 创建Point对象
p = Point(1, 2)

# 访问元素
print(p.x)  # 输出:1
print(p.y)  # 输出:2
print(p[0])  # 输出:1(仍然支持索引)

# 解包
x, y = p
print(x, y)  # 输出:1 2

# 转换为字典
print(p._asdict())  # 输出:OrderedDict([('x', 1), ('y', 2)])

# 替换元素
p2 = p._replace(x=3)
print(p2)  # 输出:Point(x=3, y=2)

# namedtuple的应用场景
# 表示二维点、三维向量等
# 替代简单的类
# 提高元组的可读性

2. heapq模块

heapq模块提供了堆(优先队列)的实现,堆是一种特殊的二叉树,每个节点的值都小于或等于其子节点的值。

# heapq示例
import heapq

# 创建最小堆
heap = [5, 3, 8, 1, 2, 9]
heapq.heapify(heap)
print(heap)  # 输出:[1, 2, 8, 3, 5, 9](堆化后的列表)

# 插入元素
heapq.heappush(heap, 4)
print(heap)  # 输出:[1, 2, 8, 3, 5, 9, 4]

# 弹出最小元素
print(heapq.heappop(heap))  # 输出:1
print(heap)  # 输出:[2, 3, 8, 4, 5, 9]

# 获取最小元素但不弹出
print(heap[0])  # 输出:2

# 合并多个有序列表
list1 = [1, 3, 5, 7]
list2 = [2, 4, 6, 8]
merged = list(heapq.merge(list1, list2))
print(merged)  # 输出:[1, 2, 3, 4, 5, 6, 7, 8]

# 获取最大或最小的n个元素
numbers = [5, 3, 8, 1, 2, 9]
print(heapq.nlargest(3, numbers))  # 输出:[9, 8, 5](最大的3个元素)
print(heapq.nsmallest(3, numbers))  # 输出:[1, 2, 3](最小的3个元素)

堆的应用场景:

  • 优先队列
  • 堆排序
  • 找最大或最小的n个元素
  • 任务调度

3. bisect模块

bisect模块提供了二分查找的实现,它可以在有序列表中快速插入元素或查找元素的位置。

# bisect示例
import bisect

# 有序列表
numbers = [1, 3, 5, 7, 9]

# 查找元素的插入位置(保持列表有序)
print(bisect.bisect(numbers, 4))  # 输出:2(插入到索引2的位置)
print(bisect.bisect_left(numbers, 3))  # 输出:1(元素存在时返回最左边的位置)
print(bisect.bisect_right(numbers, 3))  # 输出:2(元素存在时返回最右边的位置)

# 插入元素(保持列表有序)
bisect.insort(numbers, 4)
print(numbers)  # 输出:[1, 3, 4, 5, 7, 9]

bisect.insort_left(numbers, 3)
print(numbers)  # 输出:[1, 3, 3, 4, 5, 7, 9]

bisect.insort_right(numbers, 3)
print(numbers)  # 输出:[1, 3, 3, 3, 4, 5, 7, 9]

# 二分查找元素是否存在
def binary_search(arr, x):
    idx = bisect.bisect_left(arr, x)
    return idx < len(arr) and arr[idx] == x

print(binary_search(numbers, 5))  # 输出:True
print(binary_search(numbers, 6))  # 输出:False

bisect模块的应用场景:

  • 有序列表的维护
  • 二分查找
  • 区间查找
  • 插入排序的优化

4. array模块

array模块提供了数组数据结构,它类似于列表,但只能存储同类型的元素,比列表更节省内存。

# array示例
import array

# 创建数组
# 'i'表示整数类型
arr = array.array('i', [1, 2, 3, 4, 5])
print(arr)  # 输出:array('i', [1, 2, 3, 4, 5])

# 访问元素
print(arr[0])  # 输出:1

# 修改元素
arr[0] = 10
print(arr)  # 输出:array('i', [10, 2, 3, 4, 5])

# 添加元素
arr.append(6)
print(arr)  # 输出:array('i', [10, 2, 3, 4, 5, 6])

# 删除元素
arr.pop()
print(arr)  # 输出:array('i', [10, 2, 3, 4, 5])

# 数组类型码
# 'b':signed char
# 'B':unsigned char
# 'h':signed short
# 'H':unsigned short
# 'i':signed int
# 'I':unsigned int
# 'l':signed long
# 'L':unsigned long
# 'f':float
# 'd':double

array的应用场景:

  • 存储大量同类型的数据
  • 节省内存
  • 与C语言接口交互
  • 二进制文件操作

5. struct模块

struct模块提供了将Python数据类型转换为C语言数据类型的功能,用于处理二进制数据。

# struct示例
import struct

# 打包数据
# 'i'表示int类型,'f'表示float类型
packed_data = struct.pack('if', 10, 3.14)
print(packed_data)  # 输出:b'\n\x00\x00\x00\xc3\xf5\x88@'(二进制数据)

# 解包数据
unpacked_data = struct.unpack('if', packed_data)
print(unpacked_data)  # 输出:(10, 3.140000104904175)

# 打包多个数据
packed_data = struct.pack('2i3sf', 1, 2, b'abc', 3.14)
print(packed_data)  # 输出:b'\x01\x00\x00\x00\x02\x00\x00\x00abc\x00\xc3\xf5\x88@'

# 解包多个数据
unpacked_data = struct.unpack('2i3sf', packed_data)
print(unpacked_data)  # 输出:(1, 2, b'abc', 3.140000104904175)

# 格式字符
# '=':使用本地字节序
# '<':使用小端字节序
# '>':使用大端字节序
# '!':使用网络字节序(大端)

struct模块的应用场景:

  • 二进制文件操作
  • 网络通信
  • 与C语言接口交互
  • 解析二进制数据格式

四、数据结构的选择与比较

选择合适的数据结构对于程序的性能和可维护性至关重要。以下是一些常见场景下的数据结构选择建议:

1. 数据结构比较表

数据结构 可变 有序 允许重复 查找速度 插入/删除速度 主要操作
列表 O(n) O(1)(末尾)/O(n)(中间) 索引、切片、迭代
元组 O(n) - 索引、切片、迭代
字符串 O(n) - 索引、切片、字符串操作
字典 否(Python 3.7+有序) 键不允许重复 O(1) O(1) 键值对操作
集合 O(1) O(1) 集合运算
OrderedDict 键不允许重复 O(1) O(1) 有序键值对操作
defaultdict 键不允许重复 O(1) O(1) 带默认值的键值对操作
Counter O(1) O(1) 计数操作
deque O(n) O(1)(两端) 双端队列操作
heapq 是(堆顺序) O(1)(最小值) O(log n) 优先队列操作
array O(n) O(1)(末尾)/O(n)(中间) 同类型元素操作

2. 数据结构选择建议

  • 需要有序集合且频繁修改:使用列表(list)
  • 需要有序集合且不修改:使用元组(tuple)
  • 需要键值对映射:使用字典(dict)
  • 需要唯一元素:使用集合(set)
  • 需要保持插入顺序的字典:使用OrderedDict(Python 3.7+的dict也保持插入顺序)
  • 需要为不存在的键提供默认值:使用defaultdict
  • 需要计数:使用Counter
  • 需要双端队列:使用deque
  • 需要优先队列:使用heapq
  • 需要存储大量同类型数据:使用array
  • 需要处理二进制数据:使用struct

五、数据结构的最佳实践

1. 选择合适的数据结构

根据应用场景选择合适的数据结构,考虑以下因素:

  • 数据的类型和大小
  • 需要的操作(插入、删除、查找、遍历等)
  • 性能要求
  • 内存限制

2. 优化数据结构的使用

  • 列表:使用列表推导式代替循环创建列表,使用extend()代替+运算符拼接列表
  • 字典:使用字典推导式创建字典,使用get()方法访问可能不存在的键
  • 集合:使用集合去重,使用集合运算代替循环
  • deque:对于频繁在两端操作的场景,使用deque替代列表
  • heapq:对于需要频繁获取最小或最大元素的场景,使用heapq

3. 避免常见错误

  • 列表与元组:不要修改元组,不要将列表作为字典的键
  • 字典:不要在遍历字典时修改字典的大小(添加或删除键)
  • 集合:不要将可变类型(如列表)作为集合的元素
  • 性能:避免在循环中使用O(n)时间复杂度的操作

六、总结

Python提供了丰富的数据结构,包括内置的数据结构(列表、元组、字符串、字典、集合等)和标准库中的高级数据结构(OrderedDict、defaultdict、Counter、deque、堆等)。每种数据结构都有其特点和适用场景,选择合适的数据结构对于程序的性能和可维护性至关重要。

本文详细介绍了Python的各种数据结构,包括它们的定义、特点、基本操作、高级用法、应用场景和性能分析。通过学习这些数据结构,您可以更好地理解Python的工作原理,并在实际应用中选择合适的数据结构来解决问题。


发布网站:荣殿教程(zhangrongdian.com)

作者:张荣殿