Python基础数据结构与输入输出操作实战指南
1. Python基础数据结构与输入输出操作实战在日常编程中处理用户输入、格式化输出以及操作列表和元组是最基础也是最重要的技能之一。作为Python开发者我们需要熟练掌握这些基础操作因为它们构成了我们处理数据的基石。1.1 字符格式化输入输出字符格式化是Python中处理文本输出的强大工具。在Python 3.6之后引入的f-string格式化字符串字面量是最推荐的字符串格式化方式。# 基本格式化示例 name 张三 age 25 print(f姓名: {name}, 年龄: {age}) # 控制小数位数 price 19.99 print(f价格: {price:.2f}) # 输出: 价格: 19.99 # 对齐文本 print(f{商品:10} | {价格:10}) print(f{苹果:10} | {5.99:10.2f}) print(f{香蕉:10} | {3.49:10.2f})注意在Python 3.6之前可以使用%操作符或str.format()方法进行格式化但f-string更简洁直观是现在推荐的方式。个人信息输入输出时我们通常需要验证用户输入的有效性def get_personal_info(): while True: name input(请输入您的姓名: ).strip() if not name: print(姓名不能为空!) continue while True: age input(请输入您的年龄: ) if age.isdigit() and 0 int(age) 120: age int(age) break print(请输入有效的年龄(1-119)) return {name: name, age: age} user_info get_personal_info() print(f用户信息: {user_info})1.2 列表(List)操作全解析列表是Python中最常用的可变序列类型掌握其操作方法至关重要。1.2.1 列表的创建与基本操作# 创建列表的多种方式 empty_list [] numbers [1, 2, 3, 4, 5] mixed_list [1, two, 3.0, [4, 5]] from_range list(range(10)) from_string list(hello) # 列表常用方法 fruits [apple, banana, orange] fruits.append(grape) # 添加元素 fruits.insert(1, pear) # 在指定位置插入 removed fruits.pop() # 移除并返回最后一个元素 fruits.remove(banana) # 移除第一个匹配项 fruits.sort() # 原地排序 sorted_fruits sorted(fruits) # 返回新排序列表1.2.2 列表切片与复制列表切片是一个非常强大的功能可以轻松获取子列表numbers [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 基本切片 first_three numbers[:3] # [0, 1, 2] last_three numbers[-3:] # [7, 8, 9] middle numbers[3:7] # [3, 4, 5, 6] # 步长切片 even_numbers numbers[::2] # [0, 2, 4, 6, 8] reverse_numbers numbers[::-1] # 反转列表 # 切片赋值可以修改原列表 numbers[2:5] [20, 30, 40] # 替换子列表 numbers[1:1] [10, 15] # 插入而不删除任何元素重要提示列表赋值是引用赋值如果需要复制列表应该使用copy()方法或切片[:]来创建新列表。1.2.3 列表推导式列表推导式提供了一种简洁高效的方式来创建和转换列表# 基本列表推导式 squares [x**2 for x in range(10)] # 带条件的列表推导式 even_squares [x**2 for x in range(10) if x % 2 0] # 嵌套列表推导式 matrix [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flattened [num for row in matrix for num in row] # 字典和集合推导式类似 square_dict {x: x**2 for x in range(5)} unique_lengths {len(word) for word in [hello, world, python]}1.3 元组(Tuple)操作详解元组是不可变序列通常用于存储不应被修改的数据集合。1.3.1 元组的基本特性# 创建元组 empty_tuple () single_item (42,) # 注意逗号是必须的 multiple_items (1, 2, 3, 4, 5) from_list tuple([1, 2, 3]) # 元组解包 point (10, 20) x, y point # x10, y20 # 交换变量值 a, b 1, 2 a, b b, a # a2, b1 # 函数返回多个值 def get_stats(numbers): return min(numbers), max(numbers), len(numbers) min_val, max_val, count get_stats([5, 3, 8, 2])1.3.2 元组与列表的选择虽然元组和列表在很多方面相似但它们有各自的最佳使用场景使用元组的情况数据不应该被修改如配置常量作为字典的键因为元组是不可变的函数返回多个值时性能敏感的场景元组比列表更轻量使用列表的情况需要频繁修改数据集合需要用到列表特有的方法如sort()需要可变长度的序列# 性能比较 import timeit list_time timeit.timeit(x [1, 2, 3, 4, 5], number1000000) tuple_time timeit.timeit(x (1, 2, 3, 4, 5), number1000000) print(f列表创建时间: {list_time:.3f}秒) print(f元组创建时间: {tuple_time:.3f}秒)1.4 综合应用个人信息管理系统让我们将这些知识综合应用构建一个简单的个人信息管理系统def display_menu(): print(\n个人信息管理系统) print(1. 添加个人信息) print(2. 查看所有信息) print(3. 搜索信息) print(4. 删除信息) print(5. 退出) def add_person(people): name input(请输入姓名: ).strip() age int(input(请输入年龄: )) email input(请输入邮箱: ).strip() people.append((name, age, email)) # 使用元组存储个人信息 print(信息添加成功!) def display_all(people): if not people: print(当前没有存储任何信息) return print(\n{:10} {:5} {:20}.format(姓名, 年龄, 邮箱)) print(- * 40) for person in people: print({:10} {:5} {:20}.format(*person)) def search_person(people): name input(请输入要查找的姓名: ).strip().lower() found [p for p in people if p[0].lower() name] if not found: print(未找到匹配的信息) return print(\n找到以下匹配信息:) display_all(found) def delete_person(people): name input(请输入要删除的姓名: ).strip().lower() for i, person in enumerate(people[:]): if person[0].lower() name: del people[i] print(f已删除 {person[0]} 的信息) return print(未找到匹配的信息) def main(): people [] while True: display_menu() choice input(请选择操作(1-5): ) if choice 1: add_person(people) elif choice 2: display_all(people) elif choice 3: search_person(people) elif choice 4: delete_person(people) elif choice 5: print(感谢使用再见!) break else: print(无效的输入请重新选择) if __name__ __main__: main()2. 高级技巧与性能优化2.1 列表与元组的高级操作2.1.1 列表的排序与自定义排序# 基本排序 numbers [3, 1, 4, 1, 5, 9, 2, 6] numbers.sort() # 原地排序 sorted_numbers sorted(numbers) # 返回新列表 # 自定义排序 students [ (Alice, 22, A), (Bob, 19, B), (Charlie, 20, C) ] # 按年龄排序 students.sort(keylambda x: x[1]) # 按姓名长度和年龄排序 students.sort(keylambda x: (len(x[0]), x[1])) # 使用operator模块更高效 from operator import itemgetter, attrgetter students.sort(keyitemgetter(1)) # 按年龄排序2.1.2 元组的命名元组对于更清晰的代码可以使用collections.namedtuplefrom collections import namedtuple # 定义命名元组类型 Person namedtuple(Person, [name, age, email]) # 创建命名元组实例 p1 Person(张三, 30, zhangexample.com) p2 Person(name李四, age25, emailliexample.com) # 访问字段 print(p1.name) # 比普通元组的p1[0]更清晰 print(p2.email) # 命名元组仍然保持元组的特性 name, age, email p1 # 解包仍然有效2.2 性能优化技巧2.2.1 列表操作的效率# 不推荐的写法 - 频繁扩展列表 result [] for i in range(10000): result result [i] # 每次连接都创建新列表 # 推荐写法1 - 使用append result [] for i in range(10000): result.append(i) # 原地修改效率更高 # 推荐写法2 - 列表推导式 result [i for i in range(10000)] # 使用生成器表达式处理大数据 sum_of_squares sum(x*x for x in range(1000000))2.2.2 使用bisect模块维护有序列表import bisect # 维护一个有序列表 sorted_list [] for num in [5, 3, 8, 1, 9, 2]: bisect.insort(sorted_list, num) print(sorted_list) # [1, 2, 3, 5, 8, 9] # 快速查找 index bisect.bisect_left(sorted_list, 5) # 返回插入位置2.3 内存优化对于大型数据结构可以考虑使用array模块或numpy数组来节省内存import array # 创建一个紧凑的整数数组 int_array array.array(i, [1, 2, 3, 4, 5]) # 创建一个紧凑的双精度浮点数数组 float_array array.array(d, [1.0, 2.0, 3.14]) # 对于数值计算numpy数组更高效 import numpy as np large_array np.arange(1000000) # 比Python列表占用更少内存3. 常见问题与解决方案3.1 列表操作中的常见陷阱3.1.1 修改列表的同时迭代它# 错误的做法 - 在迭代时修改列表 numbers [1, 2, 3, 4, 5] for num in numbers: if num % 2 0: numbers.remove(num) # 可能导致意外行为 # 正确的做法1 - 创建副本 for num in numbers[:]: if num % 2 0: numbers.remove(num) # 正确的做法2 - 使用列表推导式 numbers [num for num in numbers if num % 2 ! 0]3.1.2 浅拷贝与深拷贝问题# 浅拷贝问题 original [[1, 2], [3, 4]] shallow_copy original.copy() shallow_copy[0][0] 99 print(original) # [[99, 2], [3, 4]] - 原列表也被修改了! # 深拷贝解决方案 import copy deep_copy copy.deepcopy(original) deep_copy[0][0] 100 print(original) # [[99, 2], [3, 4]] - 原列表不受影响3.2 元组使用中的注意事项3.2.1 可变的元组虽然元组本身不可变但如果它包含可变元素这些元素是可以被修改的mixed_tuple (1, 2, [3, 4]) try: mixed_tuple[0] 10 # 报错元组不可变 except TypeError as e: print(e) mixed_tuple[2].append(5) # 可以修改元组中的列表 print(mixed_tuple) # (1, 2, [3, 4, 5])3.2.2 单元素元组的创建创建只有一个元素的元组时必须加上逗号not_a_tuple (42) # 这是一个整数 is_a_tuple (42,) # 这是一个元组 also_tuple 42, # 这也是一个元组 print(type(not_a_tuple)) # class int print(type(is_a_tuple)) # class tuple3.3 输入验证的最佳实践处理用户输入时总是应该验证输入的有效性def get_positive_number(prompt): while True: try: value float(input(prompt)) if value 0: print(请输入一个正数) continue return value except ValueError: print(请输入有效的数字) def get_choice(options): while True: print(请选择:) for i, option in enumerate(options, 1): print(f{i}. {option}) try: choice int(input(您的选择: )) if 1 choice len(options): return choice - 1 print(f请输入1-{len(options)}之间的数字) except ValueError: print(请输入有效的数字)4. 实际项目中的应用案例4.1 数据清洗与转换在实际项目中我们经常需要处理各种数据格式的转换# 从CSV文件读取数据并转换为结构化格式 def process_csv_data(csv_lines): # 跳过标题行 header, *rows csv_lines.strip().split(\n) # 解析数据 data [] for row in rows: # 分割字段并去除空白 fields [field.strip() for field in row.split(,)] # 转换数据类型 try: name fields[0] age int(fields[1]) score float(fields[2]) data.append((name, age, score)) except (IndexError, ValueError) as e: print(f跳过无效行: {row} - 错误: {e}) return data # 示例数据 csv_data name, age, score 张三, 25, 89.5 李四, 30, 92.0 王五, 22, 76.5 无效数据 赵六, 28, 88.0 processed_data process_csv_data(csv_data) print(处理后的数据:, processed_data)4.2 多条件筛选与排序在数据分析中经常需要根据多个条件筛选和排序数据# 产品数据: (名称, 类别, 价格, 评分) products [ (iPhone 13, 手机, 799, 4.8), (Galaxy S21, 手机, 699, 4.7), (iPad Pro, 平板, 999, 4.9), (Surface Pro, 平板, 899, 4.6), (MacBook Pro, 笔记本, 1999, 4.9), (ThinkPad X1, 笔记本, 1499, 4.7) ] def filter_and_sort(products, categoryNone, max_priceNone, min_ratingNone): # 筛选产品 filtered [ p for p in products if (category is None or p[1] category) and (max_price is None or p[2] max_price) and (min_rating is None or p[3] min_rating) ] # 按价格升序评分降序排序 filtered.sort(keylambda x: (x[2], -x[3])) return filtered # 示例使用 print(筛选结果:) for product in filter_and_sort(products, category手机, max_price800): print(f{product[0]:12} {product[2]:6} {product[3]:4})4.3 高效统计与聚合使用列表和元组进行数据统计# 销售数据: (产品ID, 销售日期, 销售额) sales_data [ (101, 2023-01-05, 150.0), (102, 2023-01-05, 200.0), (101, 2023-01-06, 180.0), (103, 2023-01-06, 90.0), (102, 2023-01-07, 220.0), (101, 2023-01-07, 130.0) ] def analyze_sales(data): # 按产品ID分组统计 sales_by_product {} for product_id, _, amount in data: if product_id not in sales_by_product: sales_by_product[product_id] [] sales_by_product[product_id].append(amount) # 计算每个产品的总销售额、平均销售额和销售次数 result [] for product_id, amounts in sales_by_product.items(): total sum(amounts) average total / len(amounts) result.append((product_id, total, average, len(amounts))) # 按总销售额降序排序 result.sort(keylambda x: -x[1]) return result # 输出分析结果 print(产品ID | 总销售额 | 平均销售额 | 销售次数) print(- * 40) for item in analyze_sales(sales_data): print(f{item[0]:6} | {item[1]:8.2f} | {item[2]:8.2f} | {item[3]:6})5. 性能对比与最佳实践5.1 列表与元组的性能对比import timeit import sys # 创建速度比较 list_time timeit.timeit(x [1, 2, 3, 4, 5], number1000000) tuple_time timeit.timeit(x (1, 2, 3, 4, 5), number1000000) print(f列表创建时间: {list_time:.6f}秒) print(f元组创建时间: {tuple_time:.6f}秒) # 内存占用比较 list_obj [1, 2, 3, 4, 5] tuple_obj (1, 2, 3, 4, 5) print(f列表内存占用: {sys.getsizeof(list_obj)}字节) print(f元组内存占用: {sys.getsizeof(tuple_obj)}字节) # 访问速度比较 list_access timeit.timeit(x l[2], l [1, 2, 3, 4, 5], number1000000) tuple_access timeit.timeit(x t[2], t (1, 2, 3, 4, 5), number1000000) print(f列表访问时间: {list_access:.6f}秒) print(f元组访问时间: {tuple_access:.6f}秒)5.2 最佳实践总结选择合适的数据结构需要修改数据时使用列表数据不变时使用元组特别是作为字典键或函数返回值时考虑使用命名元组提高代码可读性高效操作列表使用列表推导式代替循环创建新列表使用append()和extend()而不是操作符扩展列表对大列表进行排序时考虑使用sorted()返回新列表输入处理原则总是验证和清理用户输入使用适当的格式化显示输出考虑使用try-except处理可能的输入错误内存与性能优化对于大型数值数据集考虑使用array或numpy避免在循环中重复创建列表使用生成器表达式处理大数据流代码可读性为复杂的数据结构使用有意义的变量名对长的列表或元组进行适当的分行使用注释解释复杂的数据操作