5_数据结构

Python数据结构:列表、元组、字典与集合

在前面的章节中,我们学习了Python的基本语法、数据类型和流程控制语句。本章将深入探讨Python的数据结构,包括列表(List)、元组(Tuple)、字典(Dictionary)、集合(Set)以及字符串的处理。这些数据结构是Python编程的基础,掌握它们对于编写高效的Python程序至关重要。

列表(List)

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

生活类比: 列表就像一个有序的购物清单或待办事项列表,你可以随时查看、添加、修改或删除其中的项目。

创建列表

可以使用方括号 [] 创建列表,各元素之间用逗号分隔:

# 创建空列表
empty_list = []

# 创建包含元素的列表
numbers = [1, 2, 3, 4, 5]
fruits = ["苹果", "香蕉", "橙子"]
mixed = [1, "hello", 3.14, True]  # 列表可以包含不同类型的元素

也可以使用 list() 函数将其他可迭代对象转换为列表:

# 从字符串创建列表
chars = list("Python")  # 结果: ['P', 'y', 't', 'h', 'o', 'n']

# 从range创建列表
numbers = list(range(1, 6))  # 结果: [1, 2, 3, 4, 5]

实用场景: 创建用户信息列表

# 存储系统中的用户信息
users = [
    ["张三", 28, "北京", "工程师"],
    ["李四", 35, "上海", "设计师"],
    ["王五", 22, "广州", "学生"]
]

# 存储接口返回的JSON数据
api_response = [
    {"id": 1, "name": "产品A", "price": 99.9},
    {"id": 2, "name": "产品B", "price": 199.5},
    {"id": 3, "name": "产品C", "price": 299.0}
]

访问列表元素

可以使用索引来访问列表中的元素。Python的索引从0开始:

fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]

# 使用正索引(从0开始)
print(fruits[0])  # 输出: 苹果
print(fruits[2])  # 输出: 橙子

# 使用负索引(从-1开始,表示从末尾开始计数)
print(fruits[-1])  # 输出: 西瓜
print(fruits[-3])  # 输出: 橙子

索引可视化:

索引:    0        1        2        3        4
正向: ["苹果",  "香蕉",  "橙子",  "葡萄",  "西瓜"]
反向:   -5       -4       -3       -2       -1

实用场景: 访问列表中的最新消息或最后添加的元素

# 获取最新消息
messages = ["早上好", "会议改期了", "项目已完成", "新任务已分配"]
latest_message = messages[-1]
print(f"最新消息: {latest_message}")  # 输出: 最新消息: 新任务已分配

# 获取最近三条消息
recent_messages = messages[-3:]
for msg in recent_messages:
    print(f"- {msg}")

切片操作

通过切片操作可以获取列表的一部分:

fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]

# 基本切片语法:list[start:end],包括start,不包括end
print(fruits[1:4])  # 输出: ['香蕉', '橙子', '葡萄']

# 省略start,默认从0开始
print(fruits[:3])  # 输出: ['苹果', '香蕉', '橙子']

# 省略end,默认到列表末尾
print(fruits[2:])  # 输出: ['橙子', '葡萄', '西瓜']

# 使用负索引进行切片
print(fruits[-3:-1])  # 输出: ['橙子', '葡萄']

# 设置步长(第三个参数)
print(fruits[::2])  # 输出: ['苹果', '橙子', '西瓜'],步长为2

# 反转列表
print(fruits[::-1])  # 输出: ['西瓜', '葡萄', '橙子', '香蕉', '苹果']

切片可视化:

fruits[1:4] 获取索引1到3的元素(不包括4):
["苹果", "香蕉", "橙子", "葡萄", "西瓜"]
         ↑      ↑      ↑
       包括    包括    包括

实用场景: 分页显示数据

# 模拟数据库查询返回的结果
all_products = ["手机", "电脑", "平板", "耳机", "相机", "手表", "键盘", "鼠标", "显示器", "打印机"]

# 每页显示3个商品
page_size = 3

# 显示第1页(索引0-2)
page1 = all_products[:page_size]
print("第1页:", page1)  # 输出: 第1页: ['手机', '电脑', '平板']

# 显示第2页(索引3-5)
page2 = all_products[page_size:page_size*2]
print("第2页:", page2)  # 输出: 第2页: ['耳机', '相机', '手表']

# 函数实现任意页的获取
def get_page(items, page_number, items_per_page):
    start = (page_number - 1) * items_per_page
    end = start + items_per_page
    return items[start:end]

# 获取第3页
page3 = get_page(all_products, 3, page_size)
print("第3页:", page3)  # 输出: 第3页: ['键盘', '鼠标', '显示器']

修改列表

列表是可变的,可以修改其元素:

fruits = ["苹果", "香蕉", "橙子"]

# 修改单个元素
fruits[1] = "梨"
print(fruits)  # 输出: ['苹果', '梨', '橙子']

# 使用切片修改多个元素
fruits[0:2] = ["草莓", "蓝莓"]
print(fruits)  # 输出: ['草莓', '蓝莓', '橙子']

实用场景: 更新购物车

# 购物车商品和数量
cart = [["笔记本电脑", 1], ["鼠标", 2], ["键盘", 1]]

# 客户将键盘数量改为2
cart[2][1] = 2
print("更新后的购物车:")
for item, quantity in cart:
    print(f"- {item}: {quantity}个")

# 客户将笔记本电脑和鼠标换成了其他型号
cart[0:2] = [["游戏本", 1], ["无线鼠标", 1]]
print("\n更换商品后的购物车:")
for item, quantity in cart:
    print(f"- {item}: {quantity}个")

列表操作

以下是一些常用的列表操作:

fruits = ["苹果", "香蕉", "橙子"]

# 添加元素到列表末尾
fruits.append("葡萄")
print(fruits)  # 输出: ['苹果', '香蕉', '橙子', '葡萄']

# 在指定位置插入元素
fruits.insert(1, "梨")
print(fruits)  # 输出: ['苹果', '梨', '香蕉', '橙子', '葡萄']

# 合并两个列表
more_fruits = ["西瓜", "芒果"]
fruits.extend(more_fruits)
print(fruits)  # 输出: ['苹果', '梨', '香蕉', '橙子', '葡萄', '西瓜', '芒果']
# 也可以使用 + 运算符
fruits = fruits + ["哈密瓜"]
print(fruits)  # 输出: ['苹果', '梨', '香蕉', '橙子', '葡萄', '西瓜', '芒果', '哈密瓜']

# 移除指定元素
fruits.remove("香蕉")
print(fruits)  # 输出: ['苹果', '梨', '橙子', '葡萄', '西瓜', '芒果', '哈密瓜']

# 移除并返回指定位置的元素
popped = fruits.pop(2)  # 移除位置2的元素(即"橙子")
print(popped)  # 输出: 橙子
print(fruits)  # 输出: ['苹果', '梨', '葡萄', '西瓜', '芒果', '哈密瓜']

# 如果不指定索引,pop()将移除并返回最后一个元素
popped = fruits.pop()
print(popped)  # 输出: 哈密瓜
print(fruits)  # 输出: ['苹果', '梨', '葡萄', '西瓜', '芒果']

# 查找元素的索引
index = fruits.index("葡萄")
print(index)  # 输出: 2

# 元素出现的次数
fruits.append("梨")
count = fruits.count("梨")
print(count)  # 输出: 2

# 排序列表
fruits.sort()
print(fruits)  # 输出: ['梨', '梨', '苹果', '葡萄', '西瓜', '芒果'](按中文拼音排序)
numbers = [5, 2, 8, 1, 9]
numbers.sort()
print(numbers)  # 输出: [1, 2, 5, 8, 9]

# 逆序排列
fruits.reverse()
print(fruits)  # 输出: ['芒果', '西瓜', '葡萄', '苹果', '梨', '梨']

# 清空列表
fruits.clear()
print(fruits)  # 输出: []

列表方法备忘录:

方法 功能 例子
append() 在列表末尾添加元素 fruits.append("葡萄")
insert() 在指定位置插入元素 fruits.insert(1, "梨")
extend() 合并另一个列表到末尾 fruits.extend(more_fruits)
remove() 移除指定元素 fruits.remove("香蕉")
pop() 移除并返回指定位置元素 fruit = fruits.pop(2)
index() 查找元素的索引 idx = fruits.index("葡萄")
count() 统计元素出现次数 n = fruits.count("梨")
sort() 排序列表 fruits.sort()
reverse() 反转列表 fruits.reverse()
clear() 清空列表 fruits.clear()

实用场景: 任务管理应用

# 任务管理系统
tasks = []

# 添加任务
tasks.append("完成Python作业")
tasks.append("准备周会演讲")
tasks.append("购买生日礼物")
print("当前任务:", tasks)

# 插入高优先级任务
tasks.insert(0, "回复紧急邮件")
print("添加紧急任务后:", tasks)

# 完成任务
completed_task = tasks.pop(0)  # 完成第一个任务
print(f"已完成: {completed_task}")
print("剩余任务:", tasks)

# 添加多个任务
new_tasks = ["整理文件", "预约医生"]
tasks.extend(new_tasks)
print("添加新任务后:", tasks)

# 取消任务
tasks.remove("购买生日礼物")
print("取消购物任务后:", tasks)

# 按字母顺序排序任务
tasks.sort()
print("任务排序后:", tasks)

列表推导式

列表推导式是一种简洁而强大的创建列表的方式:

# 基本语法: [expression for item in iterable]
squares = [x ** 2 for x in range(1, 6)]
print(squares)  # 输出: [1, 4, 9, 16, 25]

# 带条件的列表推导式: [expression for item in iterable if condition]
even_squares = [x ** 2 for x in range(1, 11) if x % 2 == 0]
print(even_squares)  # 输出: [4, 16, 36, 64, 100]

# 多层列表推导式
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [x for row in matrix for x in row]
print(flattened)  # 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]

列表推导式与传统循环对比:

传统方法:

# 求平方
squares = []
for x in range(1, 6):
    squares.append(x ** 2)

列表推导式:

# 求平方
squares = [x ** 2 for x in range(1, 6)]

实用场景: 数据转换和筛选

# 商品数据
products = [
    {"id": 1, "name": "笔记本电脑", "price": 5999, "stock": 10},
    {"id": 2, "name": "鼠标", "price": 109, "stock": 0},
    {"id": 3, "name": "键盘", "price": 329, "stock": 5},
    {"id": 4, "name": "显示器", "price": 1299, "stock": 3},
    {"id": 5, "name": "耳机", "price": 599, "stock": 0},
]

# 提取所有商品名称
product_names = [p["name"] for p in products]
print("所有商品:", product_names)

# 筛选有库存的商品
in_stock = [p["name"] for p in products if p["stock"] > 0]
print("有库存商品:", in_stock)

# 价格大于1000元的商品及其价格
expensive_products = [(p["name"], p["price"]) for p in products if p["price"] > 1000]
print("高价商品:")
for name, price in expensive_products:
    print(f"- {name}: ¥{price}")

# 所有商品的总价值 (价格 × 库存)
total_value = sum([p["price"] * p["stock"] for p in products])
print(f"库存总价值: ¥{total_value}")

元组(Tuple)

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

生活类比: 元组就像是密封的包装盒,一旦包装好就不能更改里面的内容。适合存储那些不应该被修改的数据,比如一周的天数或RGB颜色值。

创建元组

可以使用圆括号 () 创建元组,各元素之间用逗号分隔:

# 创建空元组
empty_tuple = ()

# 创建包含元素的元组
numbers = (1, 2, 3, 4, 5)
fruits = ("苹果", "香蕉", "橙子")
mixed = (1, "hello", 3.14, True)

# 注意:创建只有一个元素的元组时,需要在元素后加逗号
single_item = (42,)  # 这是一个元组
not_tuple = (42)  # 这不是元组,而是一个整数

也可以使用 tuple() 函数将其他可迭代对象转换为元组:

# 从列表创建元组
tuple_from_list = tuple([1, 2, 3])
print(tuple_from_list)  # 输出: (1, 2, 3)

# 从字符串创建元组
chars = tuple("Python")
print(chars)  # 输出: ('P', 'y', 't', 'h', 'o', 'n')

实用场景: 使用元组存储不变的数据

# RGB颜色值
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)

# 存储坐标
point_2d = (10.5, 20.8)
point_3d = (10.5, 20.8, 15.2)

# 固定选项
available_sizes = ("S", "M", "L", "XL")

# 存储记录
employee = ("张三", "IT部门", "工程师", 35000)
name, department, position, salary = employee  # 元组解包
print(f"{name}{department}{position},月薪{salary}元")

访问元组元素

与列表类似,可以使用索引访问元组元素:

fruits = ("苹果", "香蕉", "橙子", "葡萄", "西瓜")

# 使用索引
print(fruits[0])  # 输出: 苹果
print(fruits[-1])  # 输出: 西瓜

# 使用切片
print(fruits[1:4])  # 输出: ('香蕉', '橙子', '葡萄')

元组的不可变性

一旦创建,元组的元素不能被修改:

fruits = ("苹果", "香蕉", "橙子")

# 尝试修改元组元素会导致错误
# fruits[0] = "梨"  # 这会引发TypeError

# 但如果元组包含可变对象(如列表),那么可变对象的内容可以修改
t = (1, 2, [3, 4])
t[2][0] = 5
print(t)  # 输出: (1, 2, [5, 4])

提示: 元组的不可变性使其成为字典键和集合元素的理想选择,因为这些结构要求键/元素是不可变的。

实用场景: 使用元组作为函数返回多个值

# 函数返回多个值(作为元组)
def get_user_stats(user_id):
    # 假设这是从数据库获取的数据
    name = "张三"
    posts = 42
    followers = 1024
    following = 128
    return name, posts, followers, following  # 自动打包为元组

# 调用函数并解包返回值
user_name, post_count, follower_count, following_count = get_user_stats(12345)
print(f"用户: {user_name}")
print(f"帖子数: {post_count}")
print(f"粉丝数: {follower_count}")
print(f"关注数: {following_count}")

# 或者直接获取元组
stats = get_user_stats(12345)
print(f"用户 {stats[0]}{stats[2]} 个粉丝")

元组操作

# 合并元组
t1 = (1, 2, 3)
t2 = (4, 5, 6)
t3 = t1 + t2
print(t3)  # 输出: (1, 2, 3, 4, 5, 6)

# 重复元组
repeated = t1 * 3
print(repeated)  # 输出: (1, 2, 3, 1, 2, 3, 1, 2, 3)

# 检查元素是否在元组中
print("苹果" in fruits)  # 输出: True
print("梨" in fruits)    # 输出: False

# 获取元组长度
print(len(fruits))  # 输出: 5

# 获取元素出现的次数
numbers = (1, 2, 3, 2, 1, 4, 2)
print(numbers.count(2))  # 输出: 3

# 查找元素第一次出现的索引
print(numbers.index(3))  # 输出: 2

列表vs元组: 何时使用哪个?

列表 元组
可变(可修改内容) 不可变(内容固定)
适用于需要增删改的数据集合 适用于固定不变的数据集合
例如:购物车商品、任务列表 例如:RGB颜色值、坐标点、配置选项
稍微占用更多内存 内存使用更高效
不能作为字典键 可以作为字典键

实用场景: 使用元组作为字典键

# 存储不同城市不同月份的天气数据
# (城市, 月份) -> 温度
weather_data = {
    ("北京", "一月"): 0,
    ("北京", "七月"): 30,
    ("上海", "一月"): 7,
    ("上海", "七月"): 32,
    ("广州", "一月"): 14,
    ("广州", "七月"): 29
}

# 查询特定城市和月份的天气
city = "北京"
month = "七月"
temperature = weather_data[(city, month)]
print(f"{city}{month}的平均温度是{temperature}℃")

# 遍历所有数据
for (city, month), temp in weather_data.items():
    print(f"{city}{month}的平均温度是{temp}℃")

字典(Dictionary)

字典是一种可变的、无序的数据结构,它存储键值对。字典中的每个键必须是唯一的,且必须是不可变类型(如字符串、数字、元组)。

生活类比: 字典就像通讯录,你可以通过人名(键)快速找到对应的电话号码(值),而不必从头到尾翻阅整个通讯录。

创建字典

可以使用花括号 {} 创建字典,各键值对之间用逗号分隔,键和值之间用冒号 : 分隔:

# 创建空字典
empty_dict = {}

# 创建包含键值对的字典
person = {
    "name": "张三",
    "age": 30,
    "city": "北京"
}

# 使用dict()函数创建字典
person = dict(name="张三", age=30, city="北京")

实用场景: 存储用户信息

# 用户个人资料
user_profile = {
    "username": "zhang_san",
    "email": "[email protected]",
    "name": "张三",
    "age": 28,
    "is_active": True,
    "interests": ["编程", "阅读", "旅行"],
    "address": {
        "city": "北京",
        "district": "海淀",
        "street": "中关村大街"
    }
}

# 产品目录
products = {
    "A001": {"name": "笔记本电脑", "price": 5999, "stock": 10},
    "A002": {"name": "鼠标", "price": 109, "stock": 50},
    "A003": {"name": "键盘", "price": 329, "stock": 30}
}

访问字典元素

可以使用键来访问字典中的值:

person = {"name": "张三", "age": 30, "city": "北京"}

# 使用键访问值
print(person["name"])  # 输出: 张三

# 使用get()方法访问值(推荐方式,可以设置默认值)
print(person.get("age"))  # 输出: 30
print(person.get("email"))  # 输出: None(因为键不存在)
print(person.get("email", "未设置"))  # 输出: 未设置(使用默认值)

注意: 如果使用中括号语法访问不存在的键,会引发KeyError错误。而使用get()方法则会返回None或指定的默认值。

实用场景: 显示用户信息

# 从数据库获取的用户信息(可能缺少某些字段)
user = {
    "user_id": 12345,
    "username": "zhang_san",
    "name": "张三"
    # 可能没有email、phone等字段
}

# 安全地获取用户信息
print(f"用户ID: {user['user_id']}")
print(f"用户名: {user['username']}")
print(f"姓名: {user.get('name', '未提供')}")
print(f"邮箱: {user.get('email', '未提供')}")
print(f"电话: {user.get('phone', '未提供')}")

# 使用字典作为简单数据库
product_id = "A002"
if product_id in products:
    product = products[product_id]
    print(f"商品: {product['name']}")
    print(f"价格: ¥{product['price']}")
    print(f"库存: {product['stock']}件")
else:
    print(f"商品 {product_id} 不存在")

修改字典

字典是可变的,可以添加、修改和删除键值对:

person = {"name": "张三", "age": 30, "city": "北京"}

# 修改值
person["age"] = 31
print(person)  # 输出: {'name': '张三', 'age': 31, 'city': '北京'}

# 添加新的键值对
person["email"] = "[email protected]"
print(person)  # 输出: {'name': '张三', 'age': 31, 'city': '北京', 'email': '[email protected]'}

# 删除键值对
del person["city"]
print(person)  # 输出: {'name': '张三', 'age': 31, 'email': '[email protected]'}

# 使用pop()方法删除并返回值
email = person.pop("email")
print(email)  # 输出: [email protected]
print(person)  # 输出: {'name': '张三', 'age': 31}

# 清空字典
person.clear()
print(person)  # 输出: {}

实用场景: 更新用户信息

# 当前用户信息
user = {
    "username": "zhang_san",
    "email": "[email protected]",
    "phone": "13800138000",
    "address": "北京市海淀区"
}

# 用户提交的更新信息(部分字段)
update_data = {
    "email": "[email protected]",
    "address": "上海市浦东新区"
}

# 更新用户信息
for key, value in update_data.items():
    user[key] = value

print("更新后的用户信息:")
for key, value in user.items():
    print(f"{key}: {value}")

# 更简洁的方式:使用update()方法
user = {
    "username": "zhang_san",
    "email": "[email protected]",
    "phone": "13800138000",
    "address": "北京市海淀区"
}
user.update(update_data)
print("使用update()更新后:")
for key, value in user.items():
    print(f"{key}: {value}")

字典方法

Python字典提供了许多有用的方法:

person = {
    "name": "张三",
    "age": 30,
    "city": "北京",
    "email": "[email protected]"
}

# 获取所有键
keys = person.keys()
print(keys)  # 输出: dict_keys(['name', 'age', 'city', 'email'])

# 获取所有值
values = person.values()
print(values)  # 输出: dict_values(['张三', 30, '北京', '[email protected]'])

# 获取所有键值对
items = person.items()
print(items)  # 输出: dict_items([('name', '张三'), ('age', 30), ('city', '北京'), ('email', '[email protected]')])

# 使用update()方法更新字典
person.update({"age": 31, "phone": "13800138000"})
print(person)  # 输出更新后的字典

# 检查键是否存在
print("name" in person)  # 输出: True
print("gender" in person)  # 输出: False

# 使用setdefault()方法获取值,如果键不存在则设置默认值
gender = person.setdefault("gender", "男")
print(gender)  # 输出: 男
print(person)  # 输出: 包含新添加的gender键

字典方法备忘录:

方法 功能 例子
keys() 返回所有键 person.keys()
values() 返回所有值 person.values()
items() 返回所有键值对 person.items()
get() 获取值,可设置默认值 person.get("age", 0)
update() 更新字典 person.update({"age": 31})
pop() 删除并返回值 age = person.pop("age")
clear() 清空字典 person.clear()
setdefault() 获取值,不存在时设置 person.setdefault("gender", "男")

实用场景: 单词频率统计

text = """
Python是一种广泛使用的解释型、高级编程语言。Python支持多种编程范式,
包括面向对象、命令式、函数式和过程式编程。它拥有动态类型系统和垃圾回收功能,
能够自动管理内存使用,并且其本身拥有一个广泛的标准库。
"""

# 将文本分割成单词
words = text.replace(",", "").replace("。", "").replace("\n", " ").split(" ")
words = [word for word in words if word]  # 移除空字符串

# 统计每个单词出现的次数
word_count = {}
for word in words:
    # 如果单词已存在,次数加1;否则设为1
    word_count[word] = word_count.get(word, 0) + 1

# 显示出现次数最多的5个单词
sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
print("出现频率最高的单词:")
for word, count in sorted_words[:5]:
    print(f"'{word}': {count}次")

字典推导式

类似于列表推导式,字典也有推导式语法:

# 从两个列表创建字典
keys = ["a", "b", "c", "d", "e"]
values = [1, 2, 3, 4, 5]
my_dict = {keys[i]: values[i] for i in range(len(keys))}
print(my_dict)  # 输出: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

# 带条件的字典推导式
original = {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
even_dict = {k: v for k, v in original.items() if v % 2 == 0}
print(even_dict)  # 输出: {'b': 2, 'd': 4}

# 转换值
squared = {k: v**2 for k, v in original.items()}
print(squared)  # 输出: {'a': 1, 'b': 4, 'c': 9, 'd': 16, 'e': 25}

实用场景: 数据转换和过滤

# 原始学生成绩数据
scores = {
    "张三": 85,
    "李四": 92,
    "王五": 78,
    "赵六": 65,
    "钱七": 90
}

# 转换成绩为等级
def score_to_grade(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

# 使用字典推导式转换成绩为等级
grades = {name: score_to_grade(score) for name, score in scores.items()}
print("学生成绩等级:")
for name, grade in grades.items():
    print(f"{name}: {grade}")

# 筛选成绩优秀(≥90)的学生
excellent = {name: score for name, score in scores.items() if score >= 90}
print("\n成绩优秀的学生:")
for name, score in excellent.items():
    print(f"{name}: {score}")

嵌套字典

字典的值可以是任何类型,包括其他字典:

# 嵌套字典示例
students = {
    "张三": {
        "age": 20,
        "major": "计算机科学",
        "grades": {"数学": 90, "英语": 85, "编程": 95}
    },
    "李四": {
        "age": 22,
        "major": "数据科学",
        "grades": {"数学": 95, "英语": 80, "编程": 90}
    }
}

# 访问嵌套字典中的值
print(students["张三"]["grades"]["编程"])  # 输出: 95

# 修改嵌套字典中的值
students["李四"]["grades"]["英语"] = 85

# 添加新的嵌套数据
students["王五"] = {
    "age": 21,
    "major": "人工智能",
    "grades": {"数学": 88, "英语": 90, "编程": 92}
}

实用场景: 构建产品目录

# 电子商务网站的产品目录
store = {
    "电子产品": {
        "手机": [
            {"id": "P001", "name": "智能手机A", "price": 3999, "stock": 50},
            {"id": "P002", "name": "智能手机B", "price": 4999, "stock": 30}
        ],
        "电脑": [
            {"id": "C001", "name": "笔记本电脑X", "price": 5999, "stock": 20},
            {"id": "C002", "name": "台式电脑Y", "price": 4599, "stock": 15}
        ]
    },
    "服装": {
        "男装": [
            {"id": "M001", "name": "男士T恤", "price": 99, "stock": 200},
            {"id": "M002", "name": "男士牛仔裤", "price": 199, "stock": 150}
        ],
        "女装": [
            {"id": "W001", "name": "女士连衣裙", "price": 299, "stock": 180},
            {"id": "W002", "name": "女士外套", "price": 399, "stock": 120}
        ]
    }
}

# 遍历并显示所有产品
for category, subcategories in store.items():
    print(f"【{category}】")
    for subcategory, products in subcategories.items():
        print(f"  {subcategory}:")
        for product in products:
            print(f"    - {product['name']}: ¥{product['price']}, 库存: {product['stock']}件")

# 查询特定产品
def find_product(product_id):
    for category, subcategories in store.items():
        for subcategory, products in subcategories.items():
            for product in products:
                if product['id'] == product_id:
                    return product
    return None

# 查找并显示产品信息
product = find_product("C001")
if product:
    print(f"\n找到产品: {product['name']}, 价格: ¥{product['price']}")
else:
    print("\n未找到该产品")

集合(Set)

集合是一个无序的、不重复的元素集合。它在处理去重、成员检测和数学集合运算方面非常高效。

创建集合

可以使用花括号 {} 创建集合(注意与字典的区别):

# 创建集合
fruits = {"苹果", "香蕉", "橙子"}
print(fruits)  # 输出顺序可能不同,因为集合是无序的

# 使用set()函数创建集合
numbers = set([1, 2, 3, 2, 1])  # 重复元素会被自动去除
print(numbers)  # 输出: {1, 2, 3}

# 创建空集合(注意:{}会创建空字典,而不是空集合)
empty_set = set()
print(type(empty_set))  # 输出: 
print(type({}))  # 输出: 

集合操作

fruits = {"苹果", "香蕉", "橙子"}

# 添加元素
fruits.add("葡萄")
print(fruits)  # 输出: {'香蕉', '苹果', '橙子', '葡萄'}(顺序可能不同)

# 移除元素
fruits.remove("香蕉")  # 如果元素不存在,会引发KeyError
print(fruits)  # 输出: {'苹果', '橙子', '葡萄'}

# 移除元素,如果不存在也不会报错
fruits.discard("香蕉")  # 不会引发错误,因为"香蕉"已经不在集合中了

# 随机移除并返回一个元素
popped = fruits.pop()
print(popped)  # 输出随机的一个元素
print(fruits)  # 原集合减少一个元素

# 清空集合
fruits.clear()
print(fruits)  # 输出: set()

实际应用:使用集合去重

集合最常见的用途之一是去除重复项:

# 去除列表中的重复项
duplicate_list = [1, 2, 3, 1, 2, 5, 6, 7, 8, 6, 4]
unique_list = list(set(duplicate_list))
print(unique_list)  # 输出: [1, 2, 3, 4, 5, 6, 7, 8](顺序可能不同)

# 去除字符串中的重复字符
text = "hello world"
unique_chars = ''.join(set(text))
print(unique_chars)  # 输出: 'helowrd '(顺序可能不同)

# 注意:这种方法会打乱原始顺序。如果需要保持顺序,应使用其他方法。

实际应用:成员检测

集合的成员检测效率非常高,比列表快得多:

# 准备测试数据
large_list = list(range(10000))
large_set = set(range(10000))

# 测试不在集合中的元素
import time

# 列表查找
start = time.time()
if 10001 not in large_list:
    pass
end = time.time()
print(f"列表查找耗时: {end - start:.6f}秒")  # 输出类似: 列表查找耗时: 0.000412秒

# 集合查找
start = time.time()
if 10001 not in large_set:
    pass
end = time.time()
print(f"集合查找耗时: {end - start:.6f}秒")  # 输出类似: 集合查找耗时: 0.000021秒

实际应用:数据过滤

# 假设有一个用户ID列表,我们需要过滤掉已注册的用户
all_users = [101, 102, 103, 104, 105, 106, 107]
registered_users = {101, 103, 105}

# 找出未注册的用户
unregistered_users = [user for user in all_users if user not in registered_users]
print(unregistered_users)  # 输出: [102, 104, 106, 107]

# 更高效的方法是直接使用集合差集
unregistered_users = list(set(all_users) - registered_users)
print(unregistered_users)  # 输出: [102, 104, 106, 107](顺序可能不同)

集合的数学运算

集合支持交集、并集、差集等数学运算,这些操作在数据处理中非常有用:

a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

# 并集(所有不重复的元素)
union = a | b  # 或者使用 a.union(b)
print(union)  # 输出: {1, 2, 3, 4, 5, 6, 7, 8}

# 交集(共有的元素)
intersection = a & b  # 或者使用 a.intersection(b)
print(intersection)  # 输出: {4, 5}

# 差集(在a中但不在b中的元素)
difference = a - b  # 或者使用 a.difference(b)
print(difference)  # 输出: {1, 2, 3}

# 对称差集(在a或b中,但不同时在a和b中的元素)
symmetric_difference = a ^ b  # 或者使用 a.symmetric_difference(b)
print(symmetric_difference)  # 输出: {1, 2, 3, 6, 7, 8}

# 检查是否为子集/超集
c = {1, 2}
print(c.issubset(a))  # 输出: True,c是a的子集
print(a.issuperset(c))  # 输出: True,a是c的超集

实际应用:标签系统

# 一个简单的文章标签系统
articles = [
    {"id": 1, "title": "Python入门", "tags": {"编程", "Python", "教程"}},
    {"id": 2, "title": "数据分析基础", "tags": {"数据", "Python", "分析"}},
    {"id": 3, "title": "Web开发入门", "tags": {"Web", "编程", "HTML"}}
]

# 查找所有与Python相关的文章
python_articles = [article for article in articles if "Python" in article["tags"]]
print("Python相关文章:")
for article in python_articles:
    print(f"- {article['title']}")

# 查找同时包含"编程"和"Python"标签的文章
programming_and_python = [
    article for article in articles 
    if {"编程", "Python"}.issubset(article["tags"])
]
print("\n既是编程又是Python的文章:")
for article in programming_and_python:
    print(f"- {article['title']}")

# 查找所有使用的标签
all_tags = set()
for article in articles:
    all_tags |= article["tags"]  # 使用并集操作合并标签
print(f"\n所有标签: {all_tags}")

集合推导式

类似于列表推导式,Python也支持集合推导式:

# 基本语法: {expression for item in iterable if condition}
squares = {x**2 for x in range(1, 6)}
print(squares)  # 输出: {1, 4, 9, 16, 25}

# 带条件的集合推导式
even_squares = {x**2 for x in range(1, 11) if x % 2 == 0}
print(even_squares)  # 输出: {4, 16, 36, 64, 100}

# 实际应用:从文本中提取所有唯一单词
text = "Python是一种编程语言,Python简单易学,编程让生活更美好"
words = {word for word in text.replace(",", "").replace("。", "")}
print(words)  # 输出所有唯一字符

字符串处理

虽然我们在基本数据类型章节已经介绍了字符串,但这里将更深入地讨论字符串的处理方法和实际应用场景。

字符串操作

# 字符串连接
first_name = "张"
last_name = "三"
full_name = first_name + last_name
print(full_name)  # 输出: 张三

# 字符串重复
stars = "*" * 10
print(stars)  # 输出: **********
print("-" * 30)  # 输出30个连字符,常用于分隔线

# 字符串长度
length = len("Python")
print(length)  # 输出: 6

# 字符串索引和切片
s = "Python编程"
print(s[0])  # 输出: P
print(s[-1])  # 输出: 程
print(s[0:6])  # 输出: Python
print(s[:6])   # 输出: Python(从开始到索引5)
print(s[6:])   # 输出: 编程(从索引6到结束)

# 字符串查找
text = "Python是一种流行的编程语言,Python易学易用。"
position = text.find("Python")  # 找到第一个匹配的索引
print(position)  # 输出: 0
position = text.find("Python", 1)  # 从索引1开始查找
print(position)  # 输出: 17
position = text.find("Java")  # 不存在的字符串
print(position)  # 输出: -1(表示未找到)

# 使用index()方法(与find类似,但找不到时会抛出异常)
try:
    position = text.index("Java")
except ValueError:
    print("未找到指定字符串")

# 检查字符串开头和结尾
print(text.startswith("Python"))  # 输出: True
print(text.endswith("。"))  # 输出: True

# 实用场景:检查文件类型
filename = "document.pdf"
if filename.endswith((".pdf", ".PDF")):  # 可以传入元组检查多个后缀
    print("这是PDF文件")

# 字符串替换
new_text = text.replace("Python", "JavaScript")
print(new_text)  # 输出: JavaScript是一种流行的编程语言,JavaScript易学易用。

# 限制替换次数
new_text = text.replace("Python", "JavaScript", 1)  # 只替换第一次出现
print(new_text)  # 输出: JavaScript是一种流行的编程语言,Python易学易用。

# 计数
count = text.count("Python")
print(count)  # 输出: 2

实际应用:文本处理

# 处理用户输入,去除多余空格
user_input = "  Python   编程  "
cleaned_input = user_input.strip()
print(cleaned_input)  # 输出: "Python   编程"

# 标准化文本(删除所有多余空格)
words = cleaned_input.split()  # 按空格分割成单词列表
normalized_text = " ".join(words)
print(normalized_text)  # 输出: "Python 编程"

# 文本转化:首字母大写
text = "python programming is fun"
print(text.title())  # 输出: "Python Programming Is Fun"

# 检查字符串内容
print("123".isdigit())  # 输出: True(是否全是数字)
print("Python3".isalnum())  # 输出: True(是否全是字母或数字)
print("Python".isalpha())  # 输出: True(是否全是字母)
print("PYTHON".isupper())  # 输出: True(是否全是大写)
print("python".islower())  # 输出: True(是否全是小写)

# 实际应用:验证用户输入
username = "user123"
if username.isalnum() and len(username) >= 5:
    print("用户名有效")
else:
    print("用户名无效:必须至少包含5个字母或数字")

字符串格式化

Python提供了多种字符串格式化的方法:

name = "张三"
age = 25
height = 1.75
scores = {"语文": 85, "数学": 92, "英语": 78}

# 使用f-string(Python 3.6+,推荐使用)
formatted = f"我叫{name},今年{age}岁,身高{height:.2f}米。"
print(formatted)  # 输出: 我叫张三,今年25岁,身高1.75米。

# f-string可以包含表达式
print(f"明年我将{age + 1}岁")  # 输出: 明年我将26岁
print(f"我的平均分是{sum(scores.values()) / len(scores):.1f}")  # 输出: 我的平均分是85.0

# 使用format()方法
formatted = "我叫{},今年{}岁,身高{:.2f}米。".format(name, age, height)
print(formatted)  # 输出: 我叫张三,今年25岁,身高1.75米。

# 使用format()方法带有命名参数
formatted = "我叫{name},今年{age}岁,身高{height:.2f}米。".format(name=name, age=age, height=height)
print(formatted)  # 输出: 我叫张三,今年25岁,身高1.75米。

# 使用format()方法带有位置索引
formatted = "我的成绩:语文{0}分,数学{1}分,英语{2}分".format(scores["语文"], scores["数学"], scores["英语"])
print(formatted)  # 输出: 我的成绩:语文85分,数学92分,英语78分

# 使用%运算符(旧式格式化,类似C语言)
formatted = "我叫%s,今年%d岁,身高%.2f米。" % (name, age, height)
print(formatted)  # 输出: 我叫张三,今年25岁,身高1.75米。

实际应用:格式化表格数据

# 格式化表格数据
students = [
    {"姓名": "张三", "年龄": 20, "成绩": 85},
    {"姓名": "李四", "年龄": 22, "成绩": 92},
    {"姓名": "王五", "年龄": 21, "成绩": 78}
]

# 打印表头
print(f"{'姓名':<10}{'年龄':<10}{'成绩':<10}")
print("-" * 30)

# 打印每行数据
for student in students:
    print(f"{student['姓名']:<10}{student['年龄']:<10}{student['成绩']:<10}")

# 输出:
# 姓名      年龄      成绩      
# ------------------------------
# 张三      20        85        
# 李四      22        92        
# 王五      21        78        

字符串分割和连接

# 字符串分割
text = "Python,Java,C++,JavaScript"
languages = text.split(",")
print(languages)  # 输出: ['Python', 'Java', 'C++', 'JavaScript']

# 限制分割次数
languages = text.split(",", 2)
print(languages)  # 输出: ['Python', 'Java', 'C++,JavaScript']

# 按行分割
text = """第一行
第二行
第三行"""
lines = text.splitlines()
print(lines)  # 输出: ['第一行', '第二行', '第三行']

# 字符串连接
languages = ["Python", "Java", "C++", "JavaScript"]
text = ", ".join(languages)
print(text)  # 输出: Python, Java, C++, JavaScript

# 实际应用:处理CSV数据
csv_line = "张三,25,北京,工程师"
name, age, city, occupation = csv_line.split(",")
print(f"姓名: {name}, 职业: {occupation}")  # 输出: 姓名: 张三, 职业: 工程师

# 实际应用:构建URL查询参数
params = {"user": "admin", "action": "login", "redirect": "/dashboard"}
query_string = "&".join([f"{key}={value}" for key, value in params.items()])
url = f"https://example.com/api?{query_string}"
print(url)  # 输出: https://example.com/api?user=admin&action=login&redirect=/dashboard

字符串修改

字符串本身是不可变的,但可以通过创建新字符串来实现修改:

# 移除空白
text = "   Python编程   "
print(text.strip())  # 输出: "Python编程"
print(text.lstrip())  # 输出: "Python编程   "(只移除左侧空白)
print(text.rstrip())  # 输出: "   Python编程"(只移除右侧空白)

# 移除特定字符
text = "###Python编程###"
print(text.strip("#"))  # 输出: "Python编程"

# 大小写转换
text = "Python Programming"
print(text.upper())  # 输出: "PYTHON PROGRAMMING"
print(text.lower())  # 输出: "python programming"
print(text.title())  # 输出: "Python Programming"(每个单词首字母大写)
print(text.capitalize())  # 输出: "Python programming"(首字母大写)
print(text.swapcase())  # 输出: "pYTHON pROGRAMMING"(交换大小写)

# 实际应用:标准化用户数据
emails = ["[email protected]", "[email protected]", "[email protected]"]
standardized_emails = [email.lower() for email in emails]
print(standardized_emails)  # 输出: ['[email protected]', '[email protected]', '[email protected]']

实际应用:文本分析

# 简单的文本分析
text = """
Python是一种广泛使用的解释型、高级编程语言,由吉多·范罗苏姆创建,第一版发布于1991年。
Python的设计具有很强的可读性,它使用大量的缩进和空白符号。
Python支持多种编程范式,包括面向对象、命令式、函数式和过程式编程。
"""

# 统计单词出现次数
words = text.replace("\n", "").replace(",", "").replace("。", "").replace("·", "").split()
word_count = {}
for word in words:
    if word in word_count:
        word_count[word] += 1
    else:
        word_count[word] = 1

# 显示出现次数最多的5个单词
sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
print("出现次数最多的单词:")
for word, count in sorted_words[:5]:
    print(f"{word}: {count}次")

# 计算平均单词长度
total_length = sum(len(word) for word in words)
average_length = total_length / len(words) if words else 0
print(f"平均单词长度: {average_length:.1f}个字符")

编程练习

为了巩固本章所学内容,下面提供了几个实用的编程练习及其解决方案。请先自行尝试解决,然后再查看参考答案。

练习1:列表操作与列表推导式

题目:创建一个包含你最喜欢的书籍名称的列表,然后使用列表推导式创建一个新列表,只包含字符数大于5的书籍名称。

参考解答

# 创建书籍列表
books = ["三体", "活着", "围城", "红楼梦", "百年孤独", "基督山伯爵", "平凡的世界"]

# 使用列表推导式筛选长度大于5的书名
long_titles = [book for book in books if len(book) > 5]
print(f"长标题书籍: {long_titles}")  # 输出: 长标题书籍: ['百年孤独', '基督山伯爵', '平凡的世界']

# 按书名长度排序
sorted_by_length = sorted(books, key=len)
print(f"按长度排序: {sorted_by_length}")  # 从短到长排序

# 按书名长度逆序排序
reverse_sorted = sorted(books, key=len, reverse=True)
print(f"按长度逆序: {reverse_sorted}")  # 从长到短排序

练习2:学生信息字典与成绩评定

题目:创建一个学生信息字典,包含姓名、年龄、成绩等信息,并编写代码根据成绩判断学生的等级(A、B、C、D、F)。

参考解答

# 创建学生信息字典
student = {
    "姓名": "张明",
    "年龄": 20,
    "成绩": {
        "语文": 85,
        "数学": 92,
        "英语": 78,
        "物理": 88,
        "化学": 76
    }
}

# 计算平均分
scores = student["成绩"]
average_score = sum(scores.values()) / len(scores)
print(f"{student['姓名']}的平均分为: {average_score:.1f}")

# 根据平均分确定等级
def get_grade(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

grade = get_grade(average_score)
print(f"{student['姓名']}的等级为: {grade}")

# 查找最高分和最低分科目
max_subject = max(scores.items(), key=lambda x: x[1])
min_subject = min(scores.items(), key=lambda x: x[1])
print(f"最高分科目: {max_subject[0]}, 分数: {max_subject[1]}")

# 打印所有不及格科目(如果有)
failed_subjects = [subject for subject, score in scores.items() if score < 60]
if failed_subjects:
    print(f"不及格科目: {', '.join(failed_subjects)}")
else:
    print("没有不及格科目,继续保持!")

练习3:集合运算

题目:创建两个集合:一个包含110的偶数,另一个包含110的奇数,然后计算它们的并集、交集和差集。

参考解答

# 创建集合
even_numbers = {2, 4, 6, 8, 10}
odd_numbers = {1, 3, 5, 7, 9}

# 验证集合内容
print(f"偶数集合: {even_numbers}")
print(f"奇数集合: {odd_numbers}")

# 使用集合推导式创建(另一种方法)
even_set = {x for x in range(1, 11) if x % 2 == 0}
odd_set = {x for x in range(1, 11) if x % 2 == 1}
print(f"使用集合推导式 - 偶数: {even_set}")
print(f"使用集合推导式 - 奇数: {odd_set}")

# 计算并集
union_set = even_numbers | odd_numbers
print(f"并集: {union_set}")  # 应该是1到10的所有数字

# 计算交集
intersection_set = even_numbers & odd_numbers
print(f"交集: {intersection_set}")  # 应该是空集合

# 计算差集
difference_even_odd = even_numbers - odd_numbers
print(f"差集(偶数-奇数): {difference_even_odd}")  # 应该等于偶数集合

difference_odd_even = odd_numbers - even_numbers
print(f"差集(奇数-偶数): {difference_odd_even}")  # 应该等于奇数集合

# 对称差集
symmetric_difference = even_numbers ^ odd_numbers
print(f"对称差集: {symmetric_difference}")  # 应该等于并集(因为没有共同元素)

# 验证性质
print(f"偶数集合是奇数集合的子集? {even_numbers.issubset(odd_numbers)}")  # False
print(f"全集是偶数集合的超集? {union_set.issuperset(even_numbers)}")  # True

练习4:字符统计

题目:编写一个程序,统计一个字符串中每个字符出现的次数,并以字典形式返回结果。

参考解答

def count_characters(text):
    """统计字符串中每个字符出现的次数"""
    char_count = {}
    for char in text:
        if char in char_count:
            char_count[char] += 1
        else:
            char_count[char] = 1
    return char_count

# 测试函数
sample_text = "Python编程很有趣,Python是一门优雅的语言!"
result = count_characters(sample_text)

# 打印结果
print("字符统计结果:")
for char, count in result.items():
    print(f"'{char}': {count}次")

# 找出出现次数最多的字符
most_common = max(result.items(), key=lambda x: x[1])
print(f"\n出现最多的字符是 '{most_common[0]}',共出现了 {most_common[1]} 次")

# 使用collections.Counter(更简洁的方法)
from collections import Counter
counter = Counter(sample_text)
print("\n使用Counter的结果:")
most_common_chars = counter.most_common(3)  # 获取出现最多的3个字符
for char, count in most_common_chars:
    print(f"'{char}': {count}次")

练习5:购物清单程序

题目:创建一个购物清单程序,使用列表或字典来存储商品和价格,并实现添加商品、删除商品、显示所有商品和计算总价等功能。

参考解答

class ShoppingList:
    def __init__(self):
        self.items = {}  # 使用字典存储商品名称和价格
    
    def add_item(self, name, price, quantity=1):
        """添加商品到购物清单"""
        if name in self.items:
            # 如果商品已存在,更新数量和总价
            self.items[name]['quantity'] += quantity
            self.items[name]['total'] = self.items[name]['price'] * self.items[name]['quantity']
        else:
            # 添加新商品
            self.items[name] = {
                'price': price,
                'quantity': quantity,
                'total': price * quantity
            }
        print(f"已添加: {name} x{quantity}, 单价: ¥{price}")
    
    def remove_item(self, name, quantity=None):
        """从购物清单中移除商品"""
        if name not in self.items:
            print(f"购物清单中没有 {name}")
            return
        
        if quantity is None or quantity >= self.items[name]['quantity']:
            # 移除所有该商品
            del self.items[name]
            print(f"已移除所有 {name}")
        else:
            # 减少商品数量
            self.items[name]['quantity'] -= quantity
            self.items[name]['total'] = self.items[name]['price'] * self.items[name]['quantity']
            print(f"已移除: {name} x{quantity}")
    
    def display_list(self):
        """显示购物清单"""
        if not self.items:
            print("购物清单为空")
            return
        
        print("\n当前购物清单:")
        print("-" * 45)
        print(f"{'商品名称':<15}{'单价':>8}{'数量':>8}{'合计':>12}")
        print("-" * 45)
        
        for name, details in self.items.items():
            print(f"{name:<15}¥{details['price']:>7.2f}{details['quantity']:>8}¥{details['total']:>11.2f}")
        
        print("-" * 45)
        total = sum(item['total'] for item in self.items.values())
        print(f"{'总计':>32}¥{total:>11.2f}")
    
    def calculate_total(self):
        """计算购物清单总价"""
        return sum(item['total'] for item in self.items.values())

# 测试购物清单程序
shopping = ShoppingList()

# 添加商品
shopping.add_item("苹果", 5.5, 3)
shopping.add_item("牛奶", 6.8, 2)
shopping.add_item("面包", 12.5)
shopping.display_list()

# 更新商品数量
shopping.add_item("苹果", 5.5, 2)  # 再添加2个苹果
shopping.display_list()

# 移除商品
shopping.remove_item("牛奶", 1)  # 移除1份牛奶
shopping.display_list()

# 计算总价
total = shopping.calculate_total()
print(f"购物总价: ¥{total:.2f}")

通过完成这些练习,你将能够更好地理解和应用Python的数据结构。尝试修改这些代码来适应不同的需求,或者创建自己的练习题目来加深理解。

小结

在本章中,我们学习了Python的主要数据结构:

  • 列表(List):有序、可变的数据集合,适用于存储相关项的序列
  • 元组(Tuple):有序、不可变的数据集合,适用于存储不应被修改的数据
  • 字典(Dictionary):无序的键值对集合,适用于通过键快速查找数据
  • 集合(Set):无序、不重复的数据集合,适用于存储唯一项并进行集合运算
  • 字符串处理:对文本数据进行各种操作的方法

这些数据结构是Python编程的基础,掌握它们将使你能够更有效地组织和处理数据。在下一章中,我们将学习Python的函数,进一步提高代码的模块化和重用性。

你可能感兴趣的:(Python学习笔记,数据结构,windows,网络,开发语言,笔记,python)