用Python自动化枯燥的工作 第4篇:数据结构与数据处理

摘要

本文将带你深入理解Python核心数据结构和文本处理技术,帮助你掌握高效组织数据和处理文本信息的能力。你将学到列表的高级操作、字典与集合的应用、字符串处理技巧、正则表达式基础,以及数据结构选择的最佳实践。

学习目标

阅读完本文后,你将能够:

  • 列表操作:熟练使用列表的创建、访问、修改、切片等操作,理解列表推导式的强大功能
  • 字典应用:掌握字典的创建、访问、遍历、嵌套等操作,能够使用字典解决实际问题
  • 集合使用:理解集合的特性,能够使用集合进行去重、交集、并集等操作
  • 字符串处理:熟练运用字符串的切片、查找、替换、格式化等操作
  • 正则表达式:掌握正则表达式的基本语法,能够编写模式匹配复杂的文本
  • 数据结构选择:根据实际需求选择最合适的数据结构,优化程序性能

一、列表:有序的集合数据结构

1.1 列表基础

列表是Python中最常用的数据结构之一,它是一个有序的、可变的集合,可以包含任意类型的元素。

# 创建列表
fruits = ['apple', 'banana', 'cherry']
numbers = [1, 2, 3, 4, 5]
mixed = [1, 'hello', 3.14, True]
empty = []
 
# 访问元素
print(fruits[0])    # 'apple'
print(fruits[-1])   # 'cherry'(最后一个元素)
 
# 获取列表长度
print(len(fruits))  # 3

列表与字符串的一个重要区别是列表是可变的,而字符串是不可变的。这意味着我们可以修改列表的内容,但不能修改字符串中的字符。

1.2 列表的基本操作

列表支持多种操作来修改和管理其中的元素。

colors = ['red', 'green', 'blue']
 
# 修改元素
colors[1] = 'yellow'
print(colors)  # ['red', 'yellow', 'blue']
 
# 添加元素
colors.append('purple')      # 在末尾添加
colors.insert(1, 'orange')   # 在指定位置插入
print(colors)
 
# 删除元素
colors.remove('blue')        # 删除指定值的元素
popped = colors.pop()        # 删除并返回最后一个元素
del colors[0]                # 删除指定位置的元素
print(colors)

下面的序列图展示了列表常用操作的执行过程:

sequenceDiagram
    participant Main as 主程序
    participant List as 列表对象

    Note over Main,List: 创建列表
    Main->>List: fruits = ['apple', 'banana']
    Note right of List: 列表初始状态<br/>['apple', 'banana']

    Note over Main,List: 添加元素
    Main->>List: append('cherry')
    Note right of List: ['apple', 'banana', 'cherry']

    Main->>List: insert(1, 'orange')
    Note right of List: ['apple', 'orange',<br/>'banana', 'cherry']

    Note over Main,List: 修改元素
    Main->>List: fruits[0] = 'grape'
    Note right of List: ['grape', 'orange',<br/>'banana', 'cherry']

    Note over Main,List: 删除元素
    Main->>List: remove('banana')
    Note right of List: ['grape', 'orange', 'cherry']

    Main->>List: pop()
    Note right of List: 返回 'cherry'<br/>['grape', 'orange']

图表讲解:这个序列图详细展示了列表对象如何响应各种操作命令,这是理解列表可变性的关键。

首先看创建操作:主程序使用字面量语法创建一个包含两个元素的列表fruits。列表对象在内存中被创建,初始包含'apple''banana'两个元素。列表记住元素的顺序,每个位置都有一个索引(从0开始)。

然后是添加元素操作:append('cherry')命令在列表末尾添加新元素,列表变为['apple', 'banana', 'cherry']insert(1, 'orange')命令在索引1的位置插入新元素,原来索引1及之后的元素都向后移动一位,列表变为['apple', 'orange', 'banana', 'cherry']。这两个操作的区别在于:append总是在末尾添加,insert可以在任意位置插入。

修改元素操作展示了列表的可变性:fruits[0] = 'grape'直接修改索引0位置的元素,将'apple'替换为'grape'。这个操作不会改变列表的大小,只是修改了特定位置的值。

删除操作有几种方式:remove('banana')按值删除,找到第一个匹配的值并删除;pop()删除并返回最后一个元素,可以同时获取被删除的值。删除后列表会自动收缩,后面的元素向前移动填补空位。

理解这些操作对于有效使用列表非常重要,特别是在处理动态数据集合时。

1.3 列表切片

切片是Python中非常强大的功能,允许获取列表的子集。

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
# 基本切片 [开始:结束](不包含结束位置)
print(numbers[2:5])   # [2, 3, 4]
 
# 省略开始或结束
print(numbers[:5])    # [0, 1, 2, 3, 4](从头开始)
print(numbers[5:])    # [5, 6, 7, 8, 9](到末尾)
print(numbers[:])     # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9](整个列表)
 
# 使用步长
print(numbers[::2])   # [0, 2, 4, 6, 8](每隔一个)
print(numbers[::-1])  # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0](反转)
 
# 负数索引
print(numbers[-3:])   # [7, 8, 9](最后三个)
print(numbers[:-3])   # [0, 1, 2, 3, 4, 5, 6](除最后三个)

切片的语法是[开始:结束:步长],所有参数都是可选的。切片返回一个新的列表,不会修改原列表。

1.4 列表推导式

列表推导式是Python中创建列表的简洁方式,结合了循环和条件判断。

# 基本列表推导式
squares = [x**2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 
# 带条件的列表推导式
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]
 
# 处理字符串
words = ['hello', 'world', 'python', 'programming']
lengths = [len(word) for word in words]
print(lengths)  # [5, 5, 6, 11]
 
# 嵌套列表推导式
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

列表推导式的语法结构是[表达式 for 项 in 可迭代对象 if 条件],其中if部分是可选的。相比传统的循环,列表推导式更加简洁易读。

下面的流程图展示了列表推导式的执行流程:

flowchart TD
    A[开始: 列表推导式] --> B[遍历可迭代对象]
    B --> C{还有元素?}
    C -->|否| D[创建完成列表]
    C -->|是| E[获取下一个元素]

    E --> F{条件满足?}
    F -->|否| B
    F -->|是| G[计算表达式值]

    G --> H[将值添加到结果列表]
    H --> B

    D --> I[返回结果列表]

    style A fill:#e1f5e1
    style I fill:#e1f5ff
    style F fill:#fff5e1

图表讲解:这个流程图展示了列表推导式内部的工作机制,理解它有助于编写更复杂的推导式。

列表推导式从创建一个空的结果列表开始(绿色区域)。然后进入主循环,遍历输入的可迭代对象(如range(10)或另一个列表)。对于可迭代对象中的每个元素,都需要判断是否满足条件(黄色决策点)。

如果条件不满足(使用if子句时),跳过当前元素,继续处理下一个元素。这样实现了过滤功能,只有符合条件的元素才会被处理。

如果条件满足,或者没有条件子句,就计算表达式的值。表达式中可以使用当前元素进行各种运算,如x**2len(word)等。计算出的值被添加到结果列表中。

循环继续,直到可迭代对象中的所有元素都被处理完毕。最后返回填充好的结果列表(蓝色区域)。

这个流程展示了列表推导式的两个核心功能:映射(通过表达式转换每个元素)和过滤(通过条件选择特定元素)。理解这个流程,就能灵活运用列表推导式处理各种数据转换任务。


二、字典:键值对的数据结构

2.1 字典基础

字典是Python中另一种重要的数据结构,它存储键值对(key-value pairs),通过键来快速访问对应的值。

# 创建字典
person = {
    'name': '张三',
    'age': 25,
    'city': '北京'
}
 
# 访问值
print(person['name'])        # '张三'
print(person.get('age'))     # 25
 
# 添加键值对
person['email'] = '[email protected]'
 
# 修改值
person['age'] = 26
 
# 删除键值对
del person['city']
age = person.pop('age')
 
print(person)

字典的键必须是不可变类型(如字符串、数字、元组),而值可以是任意类型。字典是可变的,可以随时添加、修改或删除键值对。

2.2 字典的常用方法

字典提供了丰富的方法来操作和访问数据。

student = {
    'name': '李四',
    'age': 20,
    'grade': '大二',
    'courses': ['数学', '英语', '计算机']
}
 
# 获取所有键、值或键值对
print(student.keys())    # dict_keys(['name', 'age', 'grade', 'courses'])
print(student.values())  # dict_values(['李四', 20, '大二', ['数学', '英语', '计算机']])
print(student.items())   # dict_items([('name', '李四'), ('age', 20), ...])
 
# 检查键是否存在
print('name' in student)      # True
print('email' in student)     # False
 
# 获取值(带默认值)
print(student.get('name'))           # '李四'
print(student.get('email', '未知'))  # '未知'
 
# 更新字典
student.update({'age': 21, 'email': '[email protected]'})
 
# 清空字典
student.clear()

2.3 遍历字典

有多种方式遍历字典中的数据。

scores = {
    '张三': 95,
    '李四': 87,
    '王五': 92,
    '赵六': 88
}
 
# 遍历键
print("学生名单:")
for name in scores.keys():
    print(f"- {name}")
 
# 遍历值
print("\n所有分数:")
for score in scores.values():
    print(f"- {score}")
 
# 遍历键值对
print("\n成绩单:")
for name, score in scores.items():
    print(f"{name}: {score}分")

2.4 字典的嵌套

字典可以嵌套,创建复杂的数据结构。

# 嵌套字典示例
employees = {
    'EMP001': {
        'name': '张三',
        'department': '技术部',
        'skills': ['Python', 'Java', 'SQL']
    },
    'EMP002': {
        'name': '李四',
        'department': '市场部',
        'skills': ['营销', '文案', '数据分析']
    }
}
 
# 访问嵌套数据
print(employees['EMP001']['name'])              # '张三'
print(employees['EMP002']['skills'][0])         # '营销'
 
# 遍历嵌套字典
for emp_id, info in employees.items():
    print(f"\n员工ID: {emp_id}")
    print(f"姓名: {info['name']}")
    print(f"部门: {info['department']}")
    print(f"技能: {', '.join(info['skills'])}")

下面的类图展示了字典数据结构的组织方式:

classDiagram
    class Dict {
        +keys(): list
        +values(): list
        +items(): list
        +get(key, default): value
        +pop(key): value
        +update(other_dict): void
        +clear(): void
    }

    class Key {
        «不可变»
        +str
        +int
        +float
        +tuple
    }

    class Value {
        «任意类型»
        +str
        +int
        +float
        +list
        +dict
        +object
    }

    class NestedDict {
        «嵌套结构»
        +outer_key: Dict
        +inner_key: value
    }

    Dict "1" --> "*" Key : contains
    Dict "1" --> "*" Value : stores
    NestedDict --> Dict : extends
    Value --> Dict : can be

图表讲解:这个类图展示了字典数据结构的完整设计和能力,有助于理解字典的工作原理和适用场景。

Dict类是字典的核心,提供了丰富的方法来操作键值对。keys()方法返回所有键的集合,values()方法返回所有值的集合,items()方法返回所有键值对的元组集合。这些方法使得遍历字典变得非常方便。get()方法提供了安全访问值的方式,可以指定默认值;pop()方法删除并返回指定键的值;update()方法可以批量更新字典;clear()方法清空整个字典。

Key类展示了字典键的约束(绿色区域):键必须是不可变类型。可用的键类型包括字符串(最常用)、数字、浮点数和元组。这些类型都是不可变的,即创建后不能修改,这确保了键的稳定性。如果使用可变类型(如列表)作为键,会导致错误。

Value类展示了字典值的灵活性(蓝色区域):值可以是任意类型,包括基本类型(字符串、数字)、容器类型(列表、字典)和自定义对象。这种灵活性使得字典可以存储复杂的数据结构。值甚至可以是另一个字典,这就形成了嵌套结构。

NestedDict类表示嵌套字典(紫色区域):这是字典的一种高级用法,字典的值本身又是字典。这种结构非常适合表示层次化数据,如配置文件、JSON数据、树形结构等。通过嵌套,可以构建任意复杂度的数据结构。

理解这个设计有助于在实际编程中选择合适的数据结构。字典特别适合需要通过键快速查找值的场景,如缓存、配置管理、数据映射等。


三、集合:唯一值的数据结构

3.1 集合基础

集合是Python中存储唯一值的数据结构,类似于数学中的集合概念。集合中的元素是无序的,且不能重复。

# 创建集合
numbers = {1, 2, 3, 4, 5}
letters = set('hello')  # {'h', 'e', 'l', 'o'}(重复的'l'只保留一个)
empty = set()          # 空集合(注意:{}是空字典,不是空集合)
 
# 添加元素
numbers.add(6)
 
# 删除元素
numbers.remove(3)      # 如果元素不存在会报错
numbers.discard(10)    # 如果元素不存在不会报错
 
# 清空集合
numbers.clear()

集合的一个重要特性是自动去重,这使得它非常适合需要唯一值的场景。

3.2 集合运算

集合支持数学中的各种集合运算。

set_a = {1, 2, 3, 4, 5}
set_b = {4, 5, 6, 7, 8}
 
# 并集(两个集合的所有元素)
union = set_a | set_b
# 或 union = set_a.union(set_b)
print(union)  # {1, 2, 3, 4, 5, 6, 7, 8}
 
# 交集(两个集合共有的元素)
intersection = set_a & set_b
# 或 intersection = set_a.intersection(set_b)
print(intersection)  # {4, 5}
 
# 差集(在a中但不在b中的元素)
difference = set_a - set_b
# 或 difference = set_a.difference(set_b)
print(difference)  # {1, 2, 3}
 
# 对称差集(在任一集合中但不在两者中的元素)
symmetric_diff = set_a ^ set_b
# 或 symmetric_diff = set_a.symmetric_difference(set_b)
print(symmetric_diff)  # {1, 2, 3, 6, 7, 8}

3.3 集合的关系判断

可以判断集合之间的包含关系。

set_a = {1, 2, 3}
set_b = {1, 2, 3, 4, 5}
set_c = {1, 2}
 
# 子集判断
print(set_a.issubset(set_b))  # True(set_a是set_b的子集)
print(set_c <= set_a)         # True
 
# 超集判断
print(set_b.issuperset(set_a))  # True(set_b是set_a的超集)
print(set_a >= set_c)           # True
 
# 判断是否相交
print(set_a.isdisjoint({6, 7, 8}))  # True(没有共同元素)
print(set_a.isdisjoint({3, 4}))     # False(有共同元素)

3.4 集合的应用场景

集合在许多实际场景中都非常有用。

# 去重
def remove_duplicates(items):
    """从列表中移除重复项"""
    return list(set(items))
 
duplicated = [1, 2, 2, 3, 3, 3, 4, 5]
unique = remove_duplicates(duplicated)
print(unique)  # [1, 2, 3, 4, 5](顺序可能不同)
 
# 查找共同好友
user1_friends = {'张三', '李四', '王五', '赵六'}
user2_friends = {'李四', '王五', '孙七', '周八'}
 
common_friends = user1_friends & user2_friends
print(f"共同好友: {common_friends}")
 
# 权限检查
admin_permissions = {'read', 'write', 'delete', 'admin'}
user_permissions = {'read', 'write'}
 
def has_permission(required_permission, user_perms):
    """检查用户是否拥有指定权限"""
    return required_permission in user_perms
 
print(has_permission('write', user_permissions))  # True
print(has_permission('delete', user_permissions))  # False

下面的状态图展示了集合运算的各种操作和结果:

stateDiagram-v2
    [*] --> 初始化两个集合
    初始化两个集合 --> 并集运算: A | B
    初始化两个集合 --> 交集运算: A & B
    初始化两个集合 --> 差集运算: A - B
    初始化两个集合 --> 对称差运算: A ^ B

    并集运算 --> 返回所有元素: 两个集合的所有元素
    交集运算 --> 返回共有元素: 同时在两个集合中的元素
    差集运算 --> 返回独有元素: 只在第一个集合中的元素
    对称差运算 --> 返回非共有元素: 只在一个集合中的元素

    返回所有元素 --> [*]
    返回共有元素 --> [*]
    返回独有元素 --> [*]
    返回非共有元素 --> [*]

    note right of 初始化两个集合
        A = {1, 2, 3, 4, 5}
        B = {4, 5, 6, 7, 8}
    end note

    note right of 返回所有元素
        结果: {1, 2, 3, 4, 5, 6, 7, 8}
    end note

    note right of 返回共有元素
        结果: {4, 5}
    end note

    note right of 返回独有元素
        结果: {1, 2, 3}
    end note

    note right of 返回非共有元素
        结果: {1, 2, 3, 6, 7, 8}
    end note

图表讲解:这个状态图展示了集合四大运算的功能和效果,理解这些运算对于使用集合解决实际问题非常重要。

集合运算从初始化两个集合开始(顶部节点):假设我们有两个集合A和B,A包含{1, 2, 3, 4, 5},B包含{4, 5, 6, 7, 8}。这是所有运算的起点。

并集运算(|操作符或union()方法)返回两个集合的所有元素(绿色区域)。就像把两个集合的元素都放在一起,重复的只保留一个。结果为{1, 2, 3, 4, 5, 6, 7, 8},包含了A和B的所有唯一元素。这就像合并两个列表但去除了重复项。

交集运算(&操作符或intersection()方法)返回同时存在于两个集合中的元素(蓝色区域)。这就像找两个集合的”共同点”。结果为{4, 5},因为只有4和5同时在A和B中。这对于查找共同好友、共同标签等场景非常有用。

差集运算(-操作符或difference()方法)返回只存在于第一个集合但不在第二个集合中的元素(紫色区域)。这就像”A有什么是B没有的”。结果为{1, 2, 3},这些元素在A中但不在B中。这对于比较差异、排除特定项等场景很有用。

对称差运算(^操作符或symmetric_difference()方法)返回只存在于一个集合中的元素(粉色区域)。这就像”两个集合的不对称部分”。结果为{1, 2, 3, 6, 7, 8},这些元素要么在A中,要么在B中,但不会同时在两者中。这对于找出两个集合的差异很有帮助。

理解这些运算的实际含义,就能在遇到相关问题时快速选择合适的集合运算。


四、字符串处理:文本操作的艺术

4.1 字符串基础操作

字符串是Python中处理文本数据的基本类型,提供了丰富的操作方法。

# 创建字符串
text = "Hello, World!"
 
# 访问字符
print(text[0])     # 'H'
print(text[-1])    # '!'
 
# 字符串长度
print(len(text))   # 13
 
# 拼接字符串
greeting = "Hello"
name = "Alice"
message = greeting + ", " + name + "!"
print(message)  # "Hello, Alice!"
 
# 重复字符串
dashed_line = "-" * 20
print(dashed_line)  # "--------------------"

4.2 字符串方法

Python的字符串对象提供了大量有用的方法。

text = "  Hello, Python Programming!  "
 
# 大小写转换
print(text.upper())        # "  HELLO, PYTHON PROGRAMMING!  "
print(text.lower())        # "  hello, python programming!  "
print(text.title())        # "  Hello, Python Programming!  "
print(text.capitalize())   # "  hello, python programming!  "
 
# 去除空白
print(text.strip())        # "Hello, Python Programming!"
print(text.lstrip())       # "Hello, Python Programming!  "
print(text.rstrip())       # "  Hello, Python Programming!"
 
# 查找和替换
print(text.find("Python"))     # 8(找到的位置)
print(text.replace("Python", "World"))  # "  Hello, World Programming!  "
 
# 分割和连接
words = text.split()
print(words)  # ['Hello,', 'Python', 'Programming!']
 
sentence = " ".join(words)
print(sentence)  # "Hello, Python Programming!"

4.3 字符串格式化

字符串格式化是将变量值插入字符串的重要技术。

# 方法1: 使用f-string(Python 3.6+推荐)
name = "张三"
age = 25
message = f"姓名: {name}, 年龄: {age}"
print(message)
 
# 方法2: 使用format()方法
message = "姓名: {}, 年龄: {}".format(name, age)
print(message)
 
# 方法3: 使用%操作符(旧式)
message = "姓名: %s, 年龄: %d" % (name, age)
print(message)
 
# 格式化选项
price = 1234.56789
print(f"价格: {price:.2f}")      # "价格: 1234.57"(保留两位小数)
print(f"价格: {price:>10.2f}")   # "价格:    1234.57"(右对齐,宽度10)
print(f"价格: {price:<10.2f}")   # "价格: 1234.57    "(左对齐,宽度10)
print(f"百分比: {0.85:.2%}")     # "百分比: 85.00%"(百分比格式)

4.4 字符串切片

字符串也支持切片操作,与列表类似。

text = "Python Programming"
 
# 基本切片
print(text[0:6])      # "Python"
print(text[7:])       # "Programming"
print(text[:6])       # "Python"
print(text[::2])      # "Pto rgamn"(每隔一个字符)
print(text[::-1])     # "gnimmargorP nohtyP"(反转)
 
# 常用模式
text = "[email protected]"
 
# 获取用户名和域名
username = text[:text.index('@')]
domain = text[text.index('@') + 1:]
print(f"用户名: {username}")
print(f"域名: {domain}")

下面的流程图展示了字符串处理的常用方法和应用场景:

flowchart TD
    A[字符串数据] --> B{需要做什么?}

    B -->|转换大小写| C[upper/lower/title<br/>转换方法]
    B -->|去除空白| D[strip/lstrip/rstrip<br/>去除方法]
    B -->|查找内容| E[find/index/count<br/>查找方法]
    B -->|替换内容| F[replace<br/>替换方法]
    B -->|分割组合| G[split/join<br/>分割连接方法]
    B -->|格式化输出| H[f-string/format<br/>格式化方法]

    C --> I[返回新字符串]
    D --> I
    E --> J[返回位置或数量]
    F --> I
    G --> K[返回列表或字符串]
    H --> I

    I --> L[用于显示或存储]
    J --> M[用于条件判断]
    K --> L

    style A fill:#e1f5e1
    style I fill:#e1f5ff
    style M fill:#fff5e1

图表讲解:这个流程图展示了字符串处理的主要方法和应用方向,帮助理解如何选择合适的字符串操作方法。

字符串处理从原始字符串数据开始(绿色区域):首先需要明确操作目标,根据目标选择相应的方法类别。

如果需要转换大小写(蓝色区域):upper()将所有字符转为大写,lower()转为小写,title()将每个单词首字母大写,capitalize()将首字母大写其余小写。这些方法都返回新的字符串,不修改原字符串(因为字符串是不可变的)。常用于标准化文本格式,如统一大小写后比较。

如果需要去除空白:strip()去除两端的空白,lstrip()只去除左端,rstrip()只去除右端。这在处理用户输入或读取文件时特别有用,可以去除意外的空格、换行符等。

如果需要查找内容(黄色区域):find()返回子串的位置(找不到返回-1),index()类似但找不到会抛出异常,count()返回子串出现的次数。查找结果通常用于条件判断,如验证字符串是否包含特定内容。

如果需要替换内容:replace(old, new)将所有出现的旧子串替换为新子串。这可以用于修改文本内容,如替换敏感词、修正错误拼写等。

如果需要分割组合:split()按分隔符将字符串分割为列表,join()将列表元素连接为字符串。这两个是互补操作,常用于解析结构化文本(如CSV)或构建格式化输出。

如果需要格式化输出:f-string(推荐)、format()方法、%操作符都能将变量值插入字符串。f-string语法最简洁,支持表达式和格式化选项,是Python 3.6+的首选方法。

理解这些方法的适用场景,就能高效地处理各种文本操作任务。


五、正则表达式:强大的文本匹配工具

5.1 正则表达式基础

正则表达式是一种强大的文本模式匹配工具,可以用来查找、替换符合特定模式的文本。

import re
 
# 基本匹配
text = "我的电话号码是 138-1234-5678,请回电。"
pattern = r'\d{3}-\d{4}-\d{4}'
 
# 查找匹配
match = re.search(pattern, text)
if match:
    print(f"找到电话号码: {match.group()}")
 
# 查找所有匹配
phones = re.findall(pattern, text)
print(f"所有电话号码: {phones}")
 
# 替换匹配
new_text = re.sub(pattern, "[号码已隐藏]", text)
print(new_text)

5.2 常用正则表达式模式

正则表达式使用特殊字符来定义匹配模式。

import re
 
# 基本字符匹配
patterns = {
    r'\d': '匹配任意数字',      # [0-9]
    r'\D': '匹配非数字字符',
    r'\w': '匹配字母数字下划线', # [a-zA-Z0-9_]
    r'\W': '匹配非字母数字下划线',
    r'\s': '匹配空白字符',      # 空格、制表符、换行等
    r'\S': '匹配非空白字符',
    r'.': '匹配任意字符(除换行外)'
}
 
# 量词
patterns.update({
    r'abc*': '匹配ab,后跟0个或多个c',
    r'abc+': '匹配ab,后跟1个或多个c',
    r'abc?': '匹配ab,后跟0个或1个c',
    r'abc{3}': '匹配ab,后跟恰好3个c',
    r'abc{3,5}': '匹配ab,后跟3到5个c'
})
 
# 字符类
patterns.update({
    r'[abc]': '匹配a、b或c',
    r'[a-z]': '匹配任意小写字母',
    r'[^abc]': '匹配除a、b、c外的任意字符',
    r'(abc|def)': '匹配abc或def'
})

5.3 实用正则表达式示例

一些常见任务的实用正则表达式模式。

import re
 
# 验证邮箱地址
def is_valid_email(email):
    """验证邮箱地址格式"""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None
 
print(is_valid_email("[email protected]"))     # True
print(is_valid_email("invalid.email"))         # False
 
# 提取URL
def extract_urls(text):
    """从文本中提取URL"""
    pattern = r'https?://[^\s]+'
    return re.findall(pattern, text)
 
text = "访问 https://www.example.com 或 http://test.org 获取更多信息"
urls = extract_urls(text)
print(urls)  # ['https://www.example.com', 'http://test.org']
 
# 提取日期
def extract_dates(text):
    """提取各种格式的日期"""
    patterns = [
        r'\d{4}-\d{2}-\d{2}',   # 2024-03-15
        r'\d{4}/\d{2}/\d{2}',   # 2024/03/15
        r'\d{2}-\d{2}-\d{4}',   # 15-03-2024
    ]
    dates = []
    for pattern in patterns:
        dates.extend(re.findall(pattern, text))
    return dates
 
text = "会议日期是 2024-03-15,或 15/03/2024,或 2024/03/15"
dates = extract_dates(text)
print(dates)

5.4 正则表达式的编译和优化

对于重复使用的正则表达式,可以先编译以提高性能。

import re
 
# 编译正则表达式
phone_pattern = re.compile(r'\d{3}-\d{4}-\d{4}')
 
# 使用编译后的模式
text = "电话1: 138-1234-5678,电话2: 139-8765-4321"
phones = phone_pattern.findall(text)
print(phones)  # ['138-1234-5678', '139-8765-4321']
 
# 带标志的编译
case_insensitive = re.compile(r'python', re.IGNORECASE)
text = "Python is great. PYTHON is powerful."
matches = case_insensitive.findall(text)
print(matches)  # ['Python', 'PYTHON']
 
# 使用捕获组提取信息
date_pattern = re.compile(r'(\d{4})-(\d{2})-(\d{2})')
text = "出生日期: 1990-05-15"
match = date_pattern.search(text)
if match:
    year, month, day = match.groups()
    print(f"年: {year}, 月: {month}, 日: {day}")

下面的流程图展示了正则表达式匹配的完整过程:

flowchart TD
    A[开始正则匹配] --> B[编译正则表达式]
    B --> C[从文本起始位置开始]
    C --> D{当前位置匹配?}

    D -->|是| E{还有更多模式?}
    D -->|否| F{模式可选?}

    F -->|是| E
    F -->|否| G[匹配失败]

    E -->|否| H[匹配成功]
    E -->|是| I[尝试匹配下一部分]

    I --> J{当前位置匹配?}
    J -->|是| I
    J -->|否| K{可以回溯?}

    K -->|是| L[回溯到之前位置]
    K -->|否| M[匹配失败]

    L --> I

    H --> N[返回匹配结果]
    G --> O[继续搜索或返回失败]
    M --> O

    style A fill:#e1f5e1
    style H fill:#cffcfc
    style G fill:#ffcfcf
    style M fill:#ffcfcf
    style O fill:#ffcfcf

图表讲解:这个流程图展示了正则表达式引擎的工作机制,虽然复杂,但理解它有助于编写更高效的正则表达式。

正则匹配从编译正则表达式开始(绿色区域):正则表达式首先被编译成内部表示,这个过程分析模式的结构,优化匹配算法。编译后的模式可以重复使用,提高性能。

然后从文本的起始位置开始匹配:引擎逐个字符地尝试匹配模式。当前位置的字符是否与模式匹配(第一个黄色决策)?

如果当前位置匹配,检查是否还有更多模式需要匹配(第二个黄色决策)。如果没有更多模式,匹配成功(绿色区域)。如果还有更多模式,尝试匹配下一部分,这是一个递归过程。

如果当前位置不匹配,检查当前模式部分是否可选(如用?*{0,}量词修饰的部分)。如果可选,可以跳过这部分,继续尝试匹配后续模式。如果不可选,匹配失败(红色区域)。

在匹配过程中,引擎可能需要”回溯”(蓝色区域)。这是正则表达式的重要特性:当某个匹配路径失败时,引擎可以回退到之前的选择点,尝试其他可能的匹配方式。例如,模式a.*b匹配"abcb"时,.*首先匹配"abc",但随后找不到b,所以回溯,.*改为匹配"ab",然后成功。

理解回溯有助于编写高效的正则表达式:过多的回溯会导致性能问题,特别是在处理复杂模式和大量文本时。使用具体字符类代替.、使用非贪婪量词(*?+?)、避免嵌套量词(如(a+)+)都可以减少不必要的回溯。


六、数据结构选择与性能优化

6.1 数据结构比较

不同的数据结构有不同的性能特点,选择合适的结构对程序性能至关重要。

# 性能测试示例
import time
 
# 列表 vs 集合的成员测试
def test_list_membership():
    items = list(range(100000))
    start = time.time()
    for i in range(50000, 100000):
        _ = i in items
    end = time.time()
    return end - start
 
def test_set_membership():
    items = set(range(100000))
    start = time.time()
    for i in range(50000, 100000):
        _ = i in items
    end = time.time()
    return end - start
 
list_time = test_list_membership()
set_time = test_set_membership()
print(f"列表成员测试时间: {list_time:.4f}秒")
print(f"集合成员测试时间: {set_time:.4f}秒")
print(f"集合比列表快 {list_time/set_time:.1f} 倍")

6.2 操作复杂度对比

了解各种操作的复杂度有助于选择合适的数据结构。

"""
数据结构操作复杂度对比
 
列表 (list):
- 索引访问: O(1)
- 末尾追加: O(1)
- 中间插入: O(n)
- 中间删除: O(n)
- 成员测试: O(n)
- 排序: O(n log n)
 
字典 (dict):
- 键查找: O(1) 平均
- 键插入: O(1) 平均
- 键删除: O(1) 平均
- 成员测试: O(1) 平均
 
集合 (set):
- 添加元素: O(1) 平均
- 删除元素: O(1) 平均
- 成员测试: O(1) 平均
- 交集/并集: O(min(len(s), len(t)))
 
元组 (tuple):
- 索引访问: O(1)
- 成员测试: O(n)
- 不可变(创建后不能修改)
"""

6.3 选择建议

根据实际需求选择最合适的数据结构。

# 场景1: 需要按索引快速访问,数据量小
# 使用列表
data = [10, 20, 30, 40, 50]
print(data[2])  # O(1) 快速访问
 
# 场景2: 需要频繁的成员测试
# 使用集合
allowed_users = {'alice', 'bob', 'charlie'}
if username in allowed_users:  # O(1) 快速测试
    print("允许访问")
 
# 场景3: 需要键值对存储
# 使用字典
user_scores = {
    'alice': 95,
    'bob': 87,
    'charlie': 92
}
score = user_scores.get('alice', 0)  # O(1) 快速查找
 
# 场景4: 数据不会改变,需要节省内存
# 使用元组
coordinates = (10.5, 20.3, 30.1)
x, y, z = coordinates  # 元组解包

下面的决策树展示了如何根据需求选择合适的数据结构:

flowchart TD
    A[需要存储数据] --> B{数据需要修改?}
    B -->|否| C[使用元组 tuple]
    B -->|是| D{需要键值对?}

    D -->|是| E[使用字典 dict]
    D -->|否| F{需要保持顺序?}

    F -->|是| G[使用列表 list]
    F -->|否| H{需要唯一值?}

    H -->|是| I[使用集合 set]
    H -->|否| G

    C --> J{应用场景}
    E --> J
    G --> J
    I --> J

    J --> K[配置数据<br/>函数返回多值]
    J --> L[缓存<br/>计数器<br/>映射关系]
    J --> M[序列数据<br/>堆栈<br/>队列]
    J --> N[去重<br/>集合运算<br/>成员测试]

    style A fill:#e1f5e1
    style C fill:#e1f5ff
    style E fill:#e1f5ff
    style G fill:#e1f5ff
    style I fill:#e1f5ff

图表讲解:这个决策树提供了一个系统化的方法来选择合适的数据结构,根据具体需求逐层缩小选择范围。

首先问数据是否需要修改(第一个黄色决策):如果数据在创建后不需要修改,应该使用元组(绿色区域)。元组是不可变的,这带来了几个好处:更安全(不会意外修改)、更节省内存、可以作为字典的键。元组常用于存储配置数据、函数返回多个值、表示固定结构的数据(如坐标、RGB颜色)。

如果数据需要修改,继续问是否需要键值对存储(第二个黄色决策):如果需要通过键快速查找值,字典是最佳选择(绿色区域)。字典提供了平均O(1)的查找性能,是构建缓存、计数器、映射关系的理想选择。实际应用包括:词频统计、用户信息存储、配置管理等。

如果不需要键值对,问是否需要保持顺序(第三个黄色决策):如果需要保持元素插入顺序,使用列表(绿色区域)。列表是有序的、可变的,支持索引访问和快速追加。实际应用包括:序列数据处理、实现堆栈和队列、收集动态数据。

如果不需要保持顺序,问是否需要唯一值(第四个黄色决策):如果需要确保元素唯一,使用集合(绿色区域)。集合自动去重,提供高效的成员测试和集合运算。实际应用包括:去重、检查共同元素、权限管理等。

最右侧列出了各数据结构的典型应用场景(粉色区域):元组用于配置和函数返回值,字典用于缓存和映射,列表用于序列数据,集合用于去重和成员测试。

理解这个决策树,就能在实际编程中快速选择最合适的数据结构,提高代码效率和可读性。


七、实战案例:文本数据分析工具

7.1 需求分析

构建一个文本数据分析工具,能够:

  1. 统计文本的基本信息(字数、词数、段落数)
  2. 分析词频
  3. 查找特定模式
  4. 生成分析报告

7.2 完整实现

import re
from collections import Counter
 
class TextAnalyzer:
    """文本数据分析工具"""
 
    def __init__(self, text):
        """初始化分析器"""
        self.text = text
        self.words = self._extract_words()
        self.paragraphs = self._extract_paragraphs()
 
    def _extract_words(self):
        """提取单词"""
        # 移除标点,分割单词
        cleaned = re.sub(r'[^\w\s]', ' ', self.text.lower())
        words = cleaned.split()
        return words
 
    def _extract_paragraphs(self):
        """提取段落"""
        # 按空行分割段落
        paragraphs = [p.strip() for p in self.text.split('\n\n') if p.strip()]
        return paragraphs
 
    def get_statistics(self):
        """获取基本统计信息"""
        char_count = len(self.text)
        word_count = len(self.words)
        paragraph_count = len(self.paragraphs)
        unique_words = len(set(self.words))
 
        return {
            '字符数': char_count,
            '词数': word_count,
            '段落数': paragraph_count,
            '唯一词数': unique_words,
            '平均词长': sum(len(w) for w in self.words) / word_count if word_count > 0 else 0
        }
 
    def get_word_frequency(self, top_n=10):
        """获取词频统计"""
        counter = Counter(self.words)
        return counter.most_common(top_n)
 
    def find_patterns(self, pattern):
        """查找匹配的文本模式"""
        matches = re.findall(pattern, self.text, re.IGNORECASE)
        return matches
 
    def generate_report(self):
        """生成分析报告"""
        stats = self.get_statistics()
        top_words = self.get_word_frequency()
 
        report = f"""
========== 文本分析报告 ==========
字符数: {stats['字符数']}
词数: {stats['词数']}
段落数: {stats['段落数']}
唯一词数: {stats['唯一词数']}
平均词长: {stats['平均词长']:.2f}
 
========== 高频词汇 =========="""
        for word, count in top_words:
            report += f"\n{word}: {count}次"
 
        report += "\n" + "=" * 35 + "\n"
        return report
 
 
# 使用示例
sample_text = """
Python是一种高级编程语言,由Guido van Rossum于1991年首次发布。
Python的设计哲学强调代码的可读性和简洁的语法。
Python支持多种编程范式,包括面向对象、命令式、函数式和过程式编程。
 
Python在数据科学、机器学习、Web开发、自动化等领域得到广泛应用。
Python的简单易学使得它成为编程初学者的首选语言。
Python社区活跃,拥有丰富的第三方库和工具。
"""
 
# 创建分析器
analyzer = TextAnalyzer(sample_text)
 
# 生成报告
print(analyzer.generate_report())
 
# 查找特定模式
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
year_pattern = r'\b(19|20)\d{2}\b'
 
print("\n查找模式示例:")
print(f"邮箱: {analyzer.find_patterns(email_pattern)}")
print(f"年份: {analyzer.find_patterns(year_pattern)}")

7.3 扩展功能

class AdvancedTextAnalyzer(TextAnalyzer):
    """扩展的文本分析工具"""
 
    def get_sentences(self):
        """提取句子"""
        # 按句号、问号、感叹号分割
        sentences = re.split(r'[.!?]+', self.text)
        return [s.strip() for s in sentences if s.strip()]
 
    def find_longest_word(self):
        """查找最长的词"""
        if not self.words:
            return None
        return max(self.words, key=len)
 
    def calculate_readability(self):
        """计算可读性评分(简化版)"""
        sentences = self.get_sentences()
        if not sentences or not self.words:
            return 0
 
        avg_sentence_length = len(self.words) / len(sentences)
        # 简化的可读性公式
        readability = 100 - (1.0 * avg_sentence_length)
        return max(0, min(100, readability))
 
    def extract_numbers(self):
        """提取数字"""
        numbers = re.findall(r'\d+\.?\d*', self.text)
        return [float(n) if '.' in n else int(n) for n in numbers]
 
    def find_palindromes(self):
        """查找回文词"""
        palindromes = []
        for word in set(self.words):
            if len(word) > 2 and word == word[::-1]:
                palindromes.append(word)
        return palindromes
 
    def generate_advanced_report(self):
        """生成高级分析报告"""
        base_report = self.generate_report()
        sentences = self.get_sentences()
        longest_word = self.find_longest_word()
        readability = self.calculate_readability()
        numbers = self.extract_numbers()
        palindromes = self.find_palindromes()
 
        advanced_info = f"""
========== 高级分析 ==========
句子数: {len(sentences)}
最长单词: {longest_word} ({len(longest_word)}字符)
可读性评分: {readability:.1f}/100
数字: {numbers}
回文词: {palindromes}
"""
        return base_report + advanced_info
 
 
# 使用扩展分析器
advanced_analyzer = AdvancedTextAnalyzer(sample_text)
print(advanced_analyzer.generate_advanced_report())

下面的流程图展示了文本分析工具的完整工作流程:

flowchart TD
    A[开始: 输入文本] --> B[创建TextAnalyzer对象]
    B --> C[预处理文本]

    C --> D[提取单词列表]
    C --> E[提取段落列表]

    D --> F[统计基本指标]
    E --> F

    F --> G[计算字符数]
    F --> H[计算词数]
    F --> I[计算段落数]
    F --> J[计算唯一词数]
    F --> K[计算平均词长]

    G --> L[生成统计报告]
    H --> L
    I --> L
    J --> L
    K --> L

    L --> M{需要高级分析?}
    M -->|否| N[输出基础报告]
    M -->|是| O[创建AdvancedAnalyzer]

    O --> P[提取句子]
    O --> Q[查找最长词]
    O --> R[计算可读性]
    O --> S[提取数字]
    O --> T[查找回文词]

    P --> U[生成高级报告]
    Q --> U
    R --> U
    S --> U
    T --> U

    U --> V[输出完整报告]
    N --> W[结束]
    V --> W

    style A fill:#e1f5e1
    style W fill:#ffe1e1
    style M fill:#fff5e1
    style L fill:#e1f5ff
    style U fill:#e1f5ff

图表讲解:这个流程图展示了文本分析工具从输入到输出的完整处理流程,包括基础分析和高级分析两个层次。

处理从输入文本开始(绿色区域):首先创建TextAnalyzer对象,传入待分析的文本。然后进入预处理阶段(蓝色区域):提取单词列表和段落列表。提取单词时使用正则表达式去除标点符号,将文本转换为小写后分割;提取段落时按空行分割,并去除每段的首尾空白。

预处理完成后,进入基础统计阶段(蓝色区域):使用预处理得到的数据计算各种指标。字符数是原始文本的长度;词数是单词列表的长度;段落数是段落列表的长度;唯一词数是将单词列表转为集合后的长度;平均词长是所有单词长度的平均值。这些统计结果被汇总成基础报告。

此时有一个决策点(黄色区域):询问是否需要高级分析。如果不需要,直接输出基础报告后结束(红色区域)。

如果需要高级分析,创建AdvancedAnalyzer对象,执行更多分析任务(紫色区域):提取句子(按标点符号分割)、查找最长词(通过max函数)、计算可读性(基于句子长度的简化公式)、提取数字(使用正则表达式)、查找回文词(正读反读相同的词)。这些额外的分析结果与基础报告合并,生成完整的分析报告(绿色区域)。

这个工具展示了如何结合字符串处理、正则表达式、列表、字典等多种Python技术,构建实用的数据处理工具。通过模块化设计和类继承,代码结构清晰,易于扩展和维护。


八、核心概念总结

概念定义应用场景注意事项
列表有序可变的元素集合存储序列数据、动态数据修改会影响原列表
字典键值对的数据结构缓存、映射、快速查找键必须不可变
集合唯一值的无序集合去重、成员测试、集合运算无序,不能索引访问
元组不可变的有序序列固定数据、多返回值创建后不能修改
字符串不可变的文本序列文本处理、格式化输出不可变,操作返回新字符串
正则表达式文本模式匹配工具复杂文本查找和替换语法复杂,需要转义
列表推导式简洁创建列表的方式数据转换和过滤过于复杂会降低可读性

常见问题解答

Q1:列表和元组有什么区别,什么时候应该使用哪一个?

:列表和元组的主要区别在于可变性和性能特点。

列表是可变的,创建后可以添加、删除或修改元素。这种灵活性使得列表适合存储动态变化的数据,如收集用户输入、构建动态队列、需要频繁修改的数据集合。列表使用方括号[]创建,支持丰富的修改方法如appendinsertremove等。

元组是不可变的,创建后不能修改。这种不可变性带来了几个好处:更安全(不会意外修改)、可以作为字典的键、性能稍好(内存占用更小)。元组使用圆括号()创建,或用逗号分隔的值创建。

选择建议:如果数据需要修改,使用列表;如果数据不应修改,使用元组。元组特别适合表示固定结构的数据,如坐标(x, y)、RGB颜色(r, g, b)、函数返回多个值等。当数据作为字典的键或集合的元素时,必须使用不可变类型,这时元组是唯一选择(如果列表需要作为键,可以先转为元组)。


Q2:如何高效地从列表中删除元素?

:删除列表元素有多种方法,效率取决于具体情况。

对于已知索引位置,使用del语句或pop()方法效率最高。del list[index]直接删除指定位置的元素,list.pop(index)删除并返回被删除的元素。删除列表末尾元素是O(1)操作,但删除中间或开头的元素是O(n)操作,因为需要移动后续元素。

对于已知值,使用remove()方法删除第一次出现的匹配值。但如果值不存在会抛出ValueError,需要先检查或使用异常处理。remove()也是O(n)操作,因为需要查找。

对于批量删除,有几种高效方法:列表推导式创建新列表[x for x in old_list if condition]、使用filter()函数、就地删除时使用从后向前的循环(避免索引错位)。

# 方法1: 列表推导式(推荐)
filtered = [x for x in items if x not in to_remove]
 
# 方法2: filter函数
filtered = list(filter(lambda x: x not in to_remove, items))
 
# 方法3: 就地删除(从后向前)
for i in range(len(items)-1, -1, -1):
    if items[i] in to_remove:
        del items[i]

对于大型列表,如果只需要处理部分元素,考虑使用生成器表达式(x for x in items if condition)避免创建临时列表,可以节省内存。


Q3:字典的键有什么限制?可以使用列表作为键吗?

:字典的键必须是不可变类型,这是Python字典的基本要求。

可用的键类型包括:基本不可变类型如整数、浮点数、字符串、布尔值;元组(当且仅当元组本身包含的所有元素都不可变时);自定义对象(如果实现了__hash__方法且不可变)。

不能用作键的类型包括:列表、字典、集合等可变容器类型;其他可变对象。这是因为字典使用哈希表实现,键的哈希值必须在键的整个生命周期内保持不变。如果使用可变对象作为键,对象修改后哈希值会变化,导致无法找到原来的键值对。

如果需要将列表作为键,可以先将列表转换为元组:

# 错误:列表不能作为键
coordinates = {}
key = [1, 2]  # 列表
# coordinates[key] = "值"  # TypeError: unhashable type: 'list'
 
# 正确:转换为元组
key = tuple([1, 2])  # 元组
coordinates[key] = "值"  # 可以
 
# 实际应用:网格坐标
grid = {}
position = (3, 4)
grid[position] = "玩家位置"

另一个常见的解决方案是使用字符串作为键,将列表序列化为字符串表示,如使用str(list)json.dumps(list)。但要注意序列化和反序列化的开销。


Q4:正则表达式中的贪婪匹配和非贪婪匹配有什么区别?

:贪婪匹配和非贪婪匹配决定了量词(*+?{})的匹配行为。

贪婪匹配(默认行为)会尽可能多地匹配字符。例如,模式a.*b匹配字符串"aabcb"时,.*会匹配"abc"(尽可能多的字符),然后整个模式匹配"aabcb"。贪婪匹配在某些情况下会导致问题,如从HTML中提取标签内容时可能匹配过多。

非贪婪匹配(也称为懒惰匹配)会尽可能少地匹配字符。在量词后加?使其变为非贪婪,如*?+???{n,m}?。例如,模式a.*?b匹配字符串"aabcb"时,.*?会匹配空字符串(尽可能少的字符),然后尝试匹配b,失败后回溯,最终匹配"aab"

import re
 
text = "<div>内容1</div><div>内容2</div>"
 
# 贪婪匹配
greedy_pattern = r'<div>.*</div>'
greedy_match = re.search(greedy_pattern, text)
print(greedy_match.group())  # 匹配整个字符串
 
# 非贪婪匹配
lazy_pattern = r'<div>.*?</div>'
lazy_matches = re.findall(lazy_pattern, text)
print(lazy_matches)  # ['<div>内容1</div>', '<div>内容2</div>']

选择建议:大多数情况下,非贪婪匹配更符合预期,特别是在处理结构化文本(HTML、XML、JSON)时。但如果确定格式且想匹配最大的范围,贪婪匹配更合适。理解两者的区别有助于编写精确的正则表达式,避免过度匹配或匹配不足的问题。


Q5:如何处理大型文本文件而不消耗过多内存?

:处理大型文件时,关键是避免一次性加载整个文件到内存。

对于逐行处理,使用文件对象的迭代器特性,逐行读取和处理:

def process_large_file(filepath):
    """逐行处理大文件"""
    with open(filepath, 'r', encoding='utf-8') as f:
        for line in f:
            # 处理每一行
            processed_line = line.strip().lower()
            # 不存储整个文件,只处理当前行
            yield processed_line

对于需要分块读取的情况,使用read()方法指定大小:

def read_in_chunks(file, chunk_size=4096):
    """分块读取文件"""
    while True:
        chunk = file.read(chunk_size)
        if not chunk:
            break
        yield chunk

对于需要多次访问的数据,考虑使用数据库或磁盘缓存,而不是全部存储在内存中。Python的sqlite3模块提供了轻量级的嵌入式数据库,可以存储和查询大量数据。

使用生成器表达式代替列表推导式,避免创建临时列表:

# 列表推导式:创建完整列表
result = [process(x) for x in large_list]  # 占用大量内存
 
# 生成器表达式:惰性求值
result = (process(x) for x in large_list)  # 几乎不占内存
for item in result:
    use(item)

对于文本分析任务,可以边读边统计,而不是先存储再分析:

def count_words_in_large_file(filepath):
    """统计大文件中的词频"""
    word_count = Counter()
    with open(filepath, 'r', encoding='utf-8') as f:
        for line in f:
            words = line.strip().lower().split()
            word_count.update(words)
    return word_count

这种”流式处理”的方式,无论文件多大,内存使用量都保持在可控范围内,是处理大数据集的关键技术。


总结

本文全面介绍了Python的核心数据结构和文本处理技术。我们学习了列表、字典、集合等数据结构的特性和操作,掌握了字符串处理的各种方法,理解了正则表达式的强大功能,探讨了数据结构选择的最佳实践。

数据结构是编程的基础,选择合适的数据结构可以大大提高程序效率和代码可读性。字符串处理和正则表达式是处理文本数据的利器,在数据清洗、日志分析、网页抓取等任务中不可或缺。通过灵活运用这些工具,可以构建强大的数据处理应用程序。

下篇预告

下一篇我们将深入探讨文件操作与数据存储,带你了解Python的文件读写机制、路径处理、数据序列化等核心技术。你将学会如何自动化处理文件系统,实现数据的持久化存储。