引言:为什么实战练习是编程学习的核心

编程是一门实践性极强的技能,仅仅阅读理论书籍或观看视频教程是远远不够的。从零基础到能够独立完成项目实战,需要通过大量的代码练习来巩固基础知识、培养逻辑思维和解决问题的能力。本指南将为你精选一系列从基础到进阶的经典编程练习题,每个题目都配有详细的解析和完整的代码示例,帮助你逐步建立编程自信,最终达到项目实战的水平。

无论你是完全的编程新手,还是有一定基础想要提升实战能力的学习者,这些练习题都能为你提供系统化的训练路径。我们将按照难度递进的顺序,从最基本的语法练习到完整的项目实战,每个阶段都有明确的学习目标和实践任务。

第一阶段:基础语法与逻辑思维训练(1-2周)

1.1 数字运算与条件判断

题目1:判断素数(Prime Number Checker)

题目描述:编写一个程序,接收用户输入的一个正整数,判断该数字是否为素数(质数),并输出相应的提示信息。

学习目标

  • 掌握基本的输入输出操作
  • 理解循环结构和条件判断
  • 学习使用标志变量(flag)控制程序流程

详细解析: 素数是指大于1的自然数,除了1和它本身以外不再有其他因数的数。判断素数的核心思路是:尝试用从2到该数平方根的所有整数去除它,如果都不能整除,则该数为素数。

完整代码示例

import math

def is_prime(n):
    """
    判断一个数是否为素数
    :param n: 待判断的整数
    :return: True如果是素数,False如果不是
    """
    # 处理小于2的特殊情况
    if n < 2:
        return False
    
    # 处理2这个特殊情况(2是唯一的偶素数)
    if n == 2:
        return True
    
    # 处理偶数(除了2以外的偶数都不是素数)
    if n % 2 == 0:
        return False
    
    # 只需要检查到平方根即可,因为如果n有大于平方根的因数,
    # 那么必然有小于平方根的对应因数
    sqrt_n = int(math.sqrt(n)) + 1
    
    # 从3开始,每次加2(跳过偶数),检查到平方根为止
    for i in range(3, sqrt_n, 2):
        if n % i == 0:
            return False
    
    return True

# 主程序
def main():
    print("=== 素数判断器 ===")
    while True:
        try:
            num_str = input("请输入一个正整数(输入q退出):")
            if num_str.lower() == 'q':
                print("程序已退出,谢谢使用!")
                break
            
            num = int(num_str)
            
            if num < 1:
                print("请输入大于0的正整数!")
                continue
                
            if is_prime(num):
                print(f"✅ {num} 是素数!")
            else:
                print(f"❌ {num} 不是素数!")
                
        except ValueError:
            print("输入无效,请输入数字!")
        except Exception as e:
            print(f"发生错误:{e}")

if __name__ == "__main__":
    main()

代码逐行解析

  1. import math:导入数学模块,用于计算平方根
  2. def is_prime(n)::定义判断素数的函数
  3. if n < 2::处理边界情况,小于2的数不是素数
  4. if n == 2::2是唯一的偶素数,需要单独处理
  5. if n % 2 == 0::快速排除所有偶数
  6. sqrt_n = int(math.sqrt(n)) + 1:计算平方根,优化循环次数
  7. for i in range(3, sqrt_n, 2):从3开始,每次加2,跳过偶数
  8. if n % i == 0::如果能被整除,说明不是素数
  9. while Truetry-except:构建健壮的用户交互循环

运行示例

=== 素数判断器 ===
请输入一个正整数(输入q退出):17
✅ 17 是素数!
请输入一个正整数(输入q退出):100
❌ 100 不是素数!
请输入一个正整数(输入q退出):2
✅ 2 是素数!
请输入一个正整数(输入q退出):q
程序已退出,谢谢使用!

题目2:斐波那契数列生成器

题目描述:编写一个程序,生成斐波那契数列的前N项。斐波那契数列的定义是:前两项是0和1,从第三项开始,每一项都等于前两项之和。

学习目标

  • 掌握列表操作和循环结构
  • 理解递归与迭代的区别
  • 学习函数参数传递和返回值

完整代码示例

def fibonacci_iterative(n):
    """
    迭代方式生成斐波那契数列
    :param n: 要生成的项数
    :return: 包含n项斐波那契数列的列表
    """
    if n <= 0:
        return []
    elif n == 1:
        return [0]
    elif n == 2:
        return [0, 1]
    
    # 初始化前两项
    fib_list = [0, 1]
    
    # 从第3项开始计算
    for i in range(2, n):
        # 新项 = 前两项之和
        next_fib = fib_list[i-1] + fib_list[i-2]
        fib_list.append(next_fib)
    
    return fib_list

def fibonacci_recursive(n, a=0, b=1):
    """
    递归方式生成斐波那契数列(仅用于演示,效率较低)
    """
    if n <= 0:
        return []
    if n == 1:
        return [a]
    return [a] + fibonacci_recursive(n-1, b, a+b)

def fibonacci_generator(n):
    """
    使用生成器的方式(内存效率最高)
    """
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

def main():
    print("=== 斐波那契数列生成器 ===")
    
    while True:
        try:
            num_str = input("请输入要生成的项数(输入q退出):")
            if num_str.lower() == 'q':
                break
            
            n = int(num_str)
            if n < 1:
                print("请输入正整数!")
                continue
            
            print(f"\n迭代方式结果:")
            result1 = fibonacci_iterative(n)
            print(result1)
            
            print(f"\n生成器方式结果:")
            result2 = list(fibonacci_generator(n))
            print(result2)
            
            # 性能对比(当n很大时,递归方式会很慢)
            if n <= 20:
                print(f"\n递归方式结果:")
                result3 = fibonacci_recursive(n)
                print(result3)
            
            print("-" * 40)
            
        except ValueError:
            print("请输入有效的数字!")
        except Exception as e:
            print(f"错误:{e}")

if __name__ == "__main__":
    main()

代码深度解析

  1. 迭代方式:最常用且效率最高的方法,使用列表存储结果,时间复杂度O(n)
  2. 递归方式:代码简洁但效率低,有重复计算问题,时间复杂度O(2^n),仅用于学习
  3. 生成器方式:使用yield关键字,内存占用最小,适合处理大数据量

复杂度分析

  • 时间复杂度:迭代和生成器都是O(n),递归是O(2^n)
  • 空间复杂度:迭代O(n),生成器O(1),递归O(n)(调用栈)

1.2 字符串处理与数据结构

题目3:回文字符串检测器

题目描述:编写一个程序,判断用户输入的字符串是否为回文(正读和反读都相同,忽略大小写、空格和标点符号)。

学习目标

  • 字符串清洗和预处理
  • 字符串切片操作
  • 正则表达式基础应用

完整代码示例

import re

def is_palindrome_simple(text):
    """
    简单方式:使用字符串切片
    """
    # 移除空格和标点,转为小写
    cleaned = ''.join(char.lower() for char in text if char.isalnum())
    return cleaned == cleaned[::-1]

def is_palindrome_two_pointers(text):
    """
    双指针方式:更高效的算法
    """
    cleaned = ''.join(char.lower() for char in text if char.isalnum())
    left, right = 0, len(cleaned) - 1
    
    while left < right:
        if cleaned[left] != cleaned[right]:
            return False
        left += 1
        right -= 1
    
    return True

def is_palindrome_regex(text):
    """
    使用正则表达式清洗字符串
    """
    # 移除所有非字母数字字符
    cleaned = re.sub(r'[^a-zA-Z0-9]', '', text).lower()
    return cleaned == cleaned[::-1]

def main():
    print("=== 回文检测器 ===")
    
    test_cases = [
        "A man, a plan, a canal: Panama",
        "race a car",
        "Was it a car or a cat I saw?",
        "No 'x' in Nixon",
        "hello world",
        "上海自来水来自海上"
    ]
    
    print("测试用例演示:")
    for test in test_cases:
        result1 = is_palindrome_simple(test)
        result2 = is_palindrome_two_pointers(test)
        result3 = is_palindrome_regex(test)
        
        print(f"\n输入:'{test}'")
        print(f"  简单方式:{result1}")
        print(f"  双指针方式:{result2}")
        print(f"  正则方式:{result3}")
    
    # 用户交互模式
    print("\n" + "="*40)
    print("用户输入模式:")
    while True:
        user_input = input("\n请输入要检测的字符串(输入q退出):")
        if user_input.lower() == 'q':
            break
        
        result = is_palindrome_simple(user_input)
        if result:
            print(f"✅ '{user_input}' 是回文!")
        else:
            print(f"❌ '{user_input}' 不是回文!")

if __name__ == "__main__":
    main()

代码要点解析

  1. char.isalnum():判断字符是否为字母或数字
  2. cleaned[::-1]:Python字符串切片,-1表示反转
  3. 双指针算法:left和right指针向中间移动,效率更高
  4. 正则表达式:re.sub(r'[^a-zA-Z0-9]', '', text)移除所有非字母数字字符

运行示例

=== 回文检测器 ===
测试用例演示:

输入:'A man, a plan, a canal: Panama'
  简单方式:True
  双指针方式:True
  正则方式:True

输入:'race a car'
  简单方式:False
  双指针方式:False
  正则方式:False

第二阶段:数据结构与算法基础(2-3周)

2.1 数组与列表操作

题目4:数组中第K个最大元素

题目描述:给定一个整数数组,找到第K个最大元素。你可以假设K总是有效的,且1 ≤ K ≤ 数组长度。

学习目标

  • 掌握排序算法
  • 理解优先队列/堆数据结构
  • 学习快速选择算法

完整代码示例

import heapq
import random

def find_kth_largest_sort(nums, k):
    """
    方法1:排序后取第K个
    时间复杂度:O(n log n)
    空间复杂度:O(1)
    """
    nums.sort()
    return nums[-k]

def find_kth_largest_heap(nums, k):
    """
    方法2:使用最小堆
    时间复杂度:O(n log k)
    空间复杂度:O(k)
    """
    # 创建大小为k的最小堆
    min_heap = []
    
    for num in nums:
        if len(min_heap) < k:
            heapq.heappush(min_heap, num)
        else:
            # 如果当前数比堆顶大,替换堆顶
            if num > min_heap[0]:
                heapq.heapreplace(min_heap, num)
    
    return min_heap[0]

def find_kth_largest_quickselect(nums, k):
    """
    方法3:快速选择算法(最优解)
    平均时间复杂度:O(n)
    最坏时间复杂度:O(n²)
    空间复杂度:O(1)
    """
    def quickselect(left, right, k_smallest):
        if left == right:
            return nums[left]
        
        # 随机选择pivot,避免最坏情况
        pivot_index = random.randint(left, right)
        pivot_index = partition(left, right, pivot_index)
        
        if k_smallest == pivot_index:
            return nums[k_smallest]
        elif k_smallest < pivot_index:
            return quickselect(left, pivot_index - 1, k_smallest)
        else:
            return quickselect(pivot_index + 1, right, k_smallest)
    
    def partition(left, right, pivot_index):
        pivot_value = nums[pivot_index]
        # 将pivot移到末尾
        nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
        
        store_index = left
        for i in range(left, right):
            if nums[i] < pivot_value:
                nums[store_index], nums[i] = nums[i], nums[store_index]
                store_index += 1
        
        # 将pivot放到正确位置
        nums[right], nums[store_index] = nums[store_index], nums[right]
        return store_index
    
    # 转换为寻找第(n-k+1)小的元素
    return quickselect(0, len(nums) - 1, len(nums) - k)

def main():
    print("=== 查找数组中第K个最大元素 ===")
    
    # 测试数据
    nums = [3, 2, 1, 5, 6, 4]
    k = 2
    
    print(f"数组:{nums}")
    print(f"K值:{k}")
    print(f"期望结果:5")
    print("\n三种方法对比:")
    
    result1 = find_kth_largest_sort(nums.copy(), k)
    print(f"排序方法:{result1}")
    
    result2 = find_kth_largest_heap(nums.copy(), k)
    print(f"堆方法:{result2}")
    
    result3 = find_kth_largest_quickselect(nums.copy(), k)
    print(f"快速选择:{result3}")
    
    # 性能测试(大数据量)
    print("\n性能测试(100万数据,找第1000大):")
    large_nums = [random.randint(1, 1000000) for _ in range(1000000)]
    
    import time
    
    # 只测试堆和快速选择,排序太慢
    start = time.time()
    result_heap = find_kth_largest_heap(large_nums.copy(), 1000)
    time_heap = time.time() - start
    
    start = time.time()
    result_quick = find_kth_largest_quickselect(large_nums.copy(), 1000)
    time_quick = time.time() - start
    
    print(f"堆方法耗时:{time_heap:.4f}秒,结果:{result_heap}")
    print(f"快速选择耗时:{time_quick:.4f}秒,结果:{result_quick}")

if __name__ == "__main__":
    main()

算法深度解析

  1. 排序方法:最简单直接,但效率最低,适合小数据量
  2. 堆方法:维护一个大小为k的最小堆,堆顶就是第k大元素
  3. 快速选择:基于快速排序的partition思想,平均O(n),是最优解

关键点

  • heapq模块提供了最小堆实现
  • 快速选择的核心是partition操作
  • 随机化pivot避免最坏情况O(n²)

2.2 哈希表与映射

题目5:两数之和(Two Sum)

题目描述:给定一个整数数组和一个目标值,找出数组中和为目标值的两个数的索引。假设每个输入只对应唯一的答案。

学习目标

  • 哈希表的使用
  • 字典的键值对操作
  • 空间换时间的权衡

完整代码示例

def two_sum_brute_force(nums, target):
    """
    暴力解法:双重循环
    时间复杂度:O(n²)
    空间复杂度:O(1)
    """
    n = len(nums)
    for i in range(n):
        for j in range(i + 1, n):
            if nums[i] + nums[j] == target:
                return [i, j]
    return []

def two_sum_hash_table(nums, target):
    """
    哈希表解法:一次遍历
    时间复杂度:O(n)
    空间复杂度:O(n)
    """
    hash_map = {}  # 存储值到索引的映射
    
    for i, num in enumerate(nums):
        complement = target - num
        
        # 检查补数是否在哈希表中
        if complement in hash_map:
            return [hash_map[complement], i]
        
        # 将当前数存入哈希表
        hash_map[num] = i
    
    return []

def two_sum_sorted(nums, target):
    """
    双指针解法(需要先排序)
    时间复杂度:O(n log n)
    空间复杂度:O(1)
    """
    # 创建索引-值对列表并排序
    indexed_nums = [(val, idx) for idx, val in enumerate(nums)]
    indexed_nums.sort()
    
    left, right = 0, len(indexed_nums) - 1
    
    while left < right:
        current_sum = indexed_nums[left][0] + indexed_nums[right][0]
        
        if current_sum == target:
            return [indexed_nums[left][1], indexed_nums[right][1]]
        elif current_sum < target:
            left += 1
        else:
            right -= 1
    
    return []

def main():
    print("=== 两数之和问题 ===")
    
    # 测试用例
    test_cases = [
        ([2, 7, 11, 15], 9),
        ([3, 2, 4], 6),
        ([3, 3], 6),
        ([1, 2, 3, 4, 5], 10)
    ]
    
    for nums, target in test_cases:
        print(f"\n数组:{nums},目标:{target}")
        
        result1 = two_sum_brute_force(nums, target)
        result2 = two_sum_hash_table(nums, target)
        result3 = two_sum_sorted(nums, target)
        
        print(f"暴力解法:{result1}")
        print(f"哈希表解法:{result2}")
        print(f"双指针解法:{result3}")
    
    # 性能对比
    print("\n" + "="*50)
    print("性能对比测试:")
    
    # 生成大规模测试数据
    import random, time
    large_nums = [random.randint(1, 10000) for _ in range(10000)]
    target = 15000
    
    # 暴力解法(只测试小规模)
    small_nums = large_nums[:100]
    start = time.time()
    two_sum_brute_force(small_nums, target)
    time_brute = time.time() - start
    
    # 哈希表解法
    start = time.time()
    two_sum_hash_table(large_nums, target)
    time_hash = time.time() - start
    
    # 双指针解法
    start = time.time()
    two_sum_sorted(large_nums, target)
    time_sorted = time.time() - start
    
    print(f"暴力解法(100个元素):{time_brute:.6f}秒")
    print(f"哈希表解法(10000个元素):{time_hash:.6f}秒")
    print(f"双指针解法(10000个元素):{time_sorted:.6f}秒")

if __name__ == "__main__":
    main()

代码要点解析

  1. enumerate():同时获取索引和值
  2. in操作符:检查键是否在字典中,O(1)时间复杂度
  3. 哈希表的核心思想:用空间换时间,将查找时间从O(n)降到O(1)

运行示例

=== 两数之和问题 ===

数组:[2, 7, 11, 15],目标:9
暴力解法:[0, 1]
哈希表解法:[0, 1]
双指针解法:[0, 1]

数组:[3, 2, 4],目标:6
暴力解法:[1, 2]
哈希表解法:[1, 2]
双指针解法:[1, 2]

第三阶段:面向对象编程与文件操作(2-3周)

3.1 类与对象基础

题目6:学生信息管理系统

题目描述:设计一个学生信息管理系统,包含学生类(Student)和管理类(StudentManager),支持添加、删除、查询、修改和统计功能。

学习目标

  • 类的定义和实例化
  • 封装、继承、多态
  • 异常处理
  • 文件持久化存储

完整代码示例

import json
import os
from datetime import datetime

class Student:
    """学生类,封装学生信息"""
    
    def __init__(self, student_id, name, age, score):
        self.student_id = student_id
        self.name = name
        self.age = age
        self.score = score
    
    def __str__(self):
        """字符串表示"""
        return f"学号:{self.student_id},姓名:{self.name},年龄:{self.age},成绩:{self.score}"
    
    def __repr__(self):
        """官方表示,用于调试"""
        return f"Student({self.student_id}, '{self.name}', {self.age}, {self.score})"
    
    def get_grade(self):
        """根据成绩获取等级"""
        if self.score >= 90:
            return "A"
        elif self.score >= 80:
            return "B"
        elif self.score >= 70:
            return "C"
        elif self.score >= 60:
            return "D"
        else:
            return "E"
    
    def is_pass(self):
        """判断是否及格"""
        return self.score >= 60
    
    def to_dict(self):
        """转换为字典,用于JSON序列化"""
        return {
            "student_id": self.student_id,
            "name": self.name,
            "age": self.age,
            "score": self.score
        }

class StudentManager:
    """学生管理类,提供增删改查功能"""
    
    def __init__(self, data_file="students.json"):
        self.data_file = data_file
        self.students = {}  # 使用字典存储,key为学号
        self.load_data()
    
    def add_student(self, student):
        """添加学生"""
        if student.student_id in self.students:
            raise ValueError(f"学号 {student.student_id} 已存在!")
        
        self.students[student.student_id] = student
        print(f"✅ 学生 {student.name} 添加成功!")
    
    def delete_student(self, student_id):
        """删除学生"""
        if student_id not in self.students:
            raise ValueError(f"学号 {student_id} 不存在!")
        
        student = self.students.pop(student_id)
        print(f"✅ 学生 {student.name} 已删除!")
    
    def find_student(self, student_id):
        """查找学生"""
        return self.students.get(student_id)
    
    def update_student(self, student_id, **kwargs):
        """更新学生信息"""
        if student_id not in self.students:
            raise ValueError(f"学号 {student_id} 不存在!")
        
        student = self.students[student_id]
        for key, value in kwargs.items():
            if hasattr(student, key):
                setattr(student, key, value)
        
        print(f"✅ 学生 {student.name} 信息已更新!")
    
    def get_all_students(self):
        """获取所有学生"""
        return list(self.students.values())
    
    def get_pass_rate(self):
        """计算及格率"""
        total = len(self.students)
        if total == 0:
            return 0
        
        passed = sum(1 for s in self.students.values() if s.is_pass())
        return passed / total
    
    def get_average_score(self):
        """计算平均分"""
        if not self.students:
            return 0
        
        total = sum(s.score for s in self.students.values())
        return total / len(self.students)
    
    def save_data(self):
        """保存数据到文件"""
        data = {
            "students": [s.to_dict() for s in self.students.values()],
            "last_updated": datetime.now().isoformat()
        }
        
        with open(self.data_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        print(f"💾 数据已保存到 {self.data_file}")
    
    def load_data(self):
        """从文件加载数据"""
        if not os.path.exists(self.data_file):
            return
        
        try:
            with open(self.data_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            for student_data in data.get("students", []):
                student = Student(**student_data)
                self.students[student.student_id] = student
            
            print(f"📂 已加载 {len(self.students)} 条学生记录")
        except Exception as e:
            print(f"加载数据失败:{e}")

def display_menu():
    """显示菜单"""
    print("\n" + "="*40)
    print("     学生信息管理系统")
    print("="*40)
    print("1. 添加学生")
    print("2. 删除学生")
    print("3. 查找学生")
    print("4. 修改学生信息")
    print("5. 显示所有学生")
    print("6. 统计信息")
    print("7. 保存数据")
    print("0. 退出系统")
    print("="*40)

def main():
    manager = StudentManager()
    
    while True:
        display_menu()
        choice = input("请选择操作(0-7):")
        
        try:
            if choice == "0":
                print("感谢使用,再见!")
                break
            
            elif choice == "1":
                student_id = input("学号:")
                name = input("姓名:")
                age = int(input("年龄:"))
                score = float(input("成绩:"))
                
                student = Student(student_id, name, age, score)
                manager.add_student(student)
            
            elif choice == "2":
                student_id = input("请输入要删除的学号:")
                manager.delete_student(student_id)
            
            elif choice == "3":
                student_id = input("请输入要查找的学号:")
                student = manager.find_student(student_id)
                if student:
                    print(student)
                else:
                    print("未找到该学生!")
            
            elif choice == "4":
                student_id = input("请输入要修改的学号:")
                if manager.find_student(student_id):
                    print("请输入要修改的信息(直接回车保持原值):")
                    name = input("新姓名:")
                    age = input("新年龄:")
                    score = input("新成绩:")
                    
                    updates = {}
                    if name: updates['name'] = name
                    if age: updates['age'] = int(age)
                    if score: updates['score'] = float(score)
                    
                    manager.update_student(student_id, **updates)
            
            elif choice == "5":
                students = manager.get_all_students()
                if not students:
                    print("暂无学生记录!")
                else:
                    print("\n所有学生信息:")
                    for s in students:
                        print(s)
            
            elif choice == "6":
                print("\n统计信息:")
                print(f"学生总数:{len(manager.students)}")
                print(f"平均分:{manager.get_average_score():.2f}")
                print(f"及格率:{manager.get_pass_rate()*100:.1f}%")
            
            elif choice == "7":
                manager.save_data()
            
            else:
                print("无效选择,请重新输入!")
        
        except Exception as e:
            print(f"操作出错:{e}")
        
        input("\n按回车键继续...")

if __name__ == "__main__":
    main()

代码深度解析

  1. 封装:Student类将数据和操作封装在一起
  2. 异常处理:使用raise ValueError处理业务逻辑错误
  3. 文件操作:使用JSON格式存储数据,便于读写
  4. 动态参数**kwargs实现灵活的更新操作
  5. 数据持久化:程序关闭后数据不丢失

运行示例

📂 已加载 0 条学生记录

========================================
     学生信息管理系统
========================================
1. 添加学生
2. 删除学生
3. 查找学生
4. 修改学生信息
5. 显示所有学生
6. 统计信息
7. 保存数据
0. 退出系统
========================================
请选择操作(0-7):1
学号:2021001
姓名:张三
年龄:20
成绩:85
✅ 学生 张三 添加成功!

第四阶段:项目实战(3-4周)

4.1 项目1:简易通讯录管理系统

项目描述:实现一个功能完整的命令行通讯录系统,支持联系人管理、分组、搜索、导入导出等功能。

项目架构设计

  • 数据层:JSON文件存储
  • 业务层:Contact类、AddressBook类
  • 表现层:命令行交互界面

完整代码实现

import json
import csv
import os
from datetime import datetime
from typing import List, Optional

class Contact:
    """联系人类"""
    
    def __init__(self, name: str, phone: str, email: str = "", group: str = "默认"):
        self.name = name
        self.phone = phone
        self.email = email
        self.group = group
        self.created_at = datetime.now().isoformat()
    
    def __str__(self):
        return f"【{self.group}】{self.name} - {self.phone} ({self.email})"
    
    def to_dict(self):
        return {
            "name": self.name,
            "phone": self.phone,
            "email": self.email,
            "group": self.group,
            "created_at": self.created_at
        }
    
    @classmethod
    def from_dict(cls, data):
        contact = cls(data["name"], data["phone"], data.get("email", ""), data.get("group", "默认"))
        contact.created_at = data.get("created_at", datetime.now().isoformat())
        return contact

class AddressBook:
    """通讯录管理类"""
    
    def __init__(self, data_file="contacts.json"):
        self.data_file = data_file
        self.contacts: List[Contact] = []
        self.load_data()
    
    def add_contact(self, contact: Contact):
        """添加联系人"""
        # 检查是否已存在
        for c in self.contacts:
            if c.phone == contact.phone:
                raise ValueError(f"电话号码 {contact.phone} 已存在!")
        
        self.contacts.append(contact)
        print(f"✅ 添加联系人:{contact.name}")
    
    def delete_contact(self, phone: str):
        """删除联系人"""
        for i, c in enumerate(self.contacts):
            if c.phone == phone:
                del self.contacts[i]
                print(f"✅ 删除联系人:{c.name}")
                return
        
        raise ValueError(f"未找到电话号码:{phone}")
    
    def search_contacts(self, keyword: str, search_by: str = "name") -> List[Contact]:
        """搜索联系人"""
        results = []
        keyword = keyword.lower()
        
        for contact in self.contacts:
            if search_by == "name" and keyword in contact.name.lower():
                results.append(contact)
            elif search_by == "phone" and keyword in contact.phone:
                results.append(contact)
            elif search_by == "group" and keyword in contact.group.lower():
                results.append(contact)
        
        return results
    
    def update_contact(self, phone: str, **kwargs):
        """更新联系人"""
        for contact in self.contacts:
            if contact.phone == phone:
                for key, value in kwargs.items():
                    if hasattr(contact, key):
                        setattr(contact, key, value)
                print(f"✅ 更新联系人:{contact.name}")
                return
        
        raise ValueError(f"未找到电话号码:{phone}")
    
    def get_all_groups(self):
        """获取所有分组"""
        groups = {}
        for contact in self.contacts:
            groups[contact.group] = groups.get(contact.group, 0) + 1
        return groups
    
    def export_to_csv(self, filename: str):
        """导出到CSV"""
        if not self.contacts:
            print("没有联系人可导出!")
            return
        
        with open(filename, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(["姓名", "电话", "邮箱", "分组", "创建时间"])
            for c in self.contacts:
                writer.writerow([c.name, c.phone, c.email, c.group, c.created_at])
        
        print(f"✅ 已导出到 {filename}")
    
    def import_from_csv(self, filename: str):
        """从CSV导入"""
        if not os.path.exists(filename):
            raise FileNotFoundError(f"文件 {filename} 不存在!")
        
        count = 0
        with open(filename, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                try:
                    contact = Contact(
                        name=row["姓名"],
                        phone=row["电话"],
                        email=row.get("邮箱", ""),
                        group=row.get("分组", "默认")
                    )
                    self.add_contact(contact)
                    count += 1
                except Exception as e:
                    print(f"跳过错误行:{e}")
        
        print(f"✅ 成功导入 {count} 个联系人")
    
    def save_data(self):
        """保存数据"""
        data = {
            "contacts": [c.to_dict() for c in self.contacts],
            "last_updated": datetime.now().isoformat()
        }
        
        with open(self.data_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        print(f"💾 数据已保存")
    
    def load_data(self):
        """加载数据"""
        if not os.path.exists(self.data_file):
            return
        
        try:
            with open(self.data_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            self.contacts = [Contact.from_dict(c) for c in data.get("contacts", [])]
            print(f"📂 已加载 {len(self.contacts)} 个联系人")
        except Exception as e:
            print(f"加载数据失败:{e}")

def main():
    book = AddressBook()
    
    while True:
        print("\n" + "="*50)
        print("通讯录管理系统".center(48))
        print("="*50)
        print("1. 添加联系人")
        print("2. 删除联系人")
        print("3. 搜索联系人")
        print("4. 修改联系人")
        print("5. 显示所有联系人")
        print("6. 分组统计")
        print("7. 导出CSV")
        print("8. 导入CSV")
        print("9. 保存数据")
        print("0. 退出系统")
        print("="*50)
        
        choice = input("请选择:").strip()
        
        try:
            if choice == "0":
                save = input("退出前是否保存数据?(y/n): ")
                if save.lower() == "y":
                    book.save_data()
                print("再见!")
                break
            
            elif choice == "1":
                name = input("姓名:")
                phone = input("电话:")
                email = input("邮箱(可选):")
                group = input("分组(默认):") or "默认"
                
                contact = Contact(name, phone, email, group)
                book.add_contact(contact)
            
            elif choice == "2":
                phone = input("请输入要删除的电话:")
                book.delete_contact(phone)
            
            elif choice == "3":
                keyword = input("搜索关键词:")
                by = input("搜索方式(name/phone/group,默认name):") or "name"
                results = book.search_contacts(keyword, by)
                
                if results:
                    print(f"\n找到 {len(results)} 个结果:")
                    for c in results:
                        print(c)
                else:
                    print("未找到匹配的联系人!")
            
            elif choice == "4":
                phone = input("请输入要修改的电话:")
                print("输入新信息(直接回车保持原值):")
                name = input("新姓名:")
                email = input("新邮箱:")
                group = input("新分组:")
                
                updates = {}
                if name: updates['name'] = name
                if email: updates['email'] = email
                if group: updates['group'] = group
                
                book.update_contact(phone, **updates)
            
            elif choice == "5":
                if not book.contacts:
                    print("暂无联系人!")
                else:
                    print(f"\n共 {len(book.contacts)} 个联系人:")
                    for c in book.contacts:
                        print(c)
            
            elif choice == "6":
                groups = book.get_all_groups()
                print("\n分组统计:")
                for group, count in groups.items():
                    print(f"  {group}: {count} 人")
            
            elif choice == "7":
                filename = input("导出文件名(默认contacts.csv):") or "contacts.csv"
                book.export_to_csv(filename)
            
            elif choice == "8":
                filename = input("导入文件名:")
                book.import_from_csv(filename)
            
            elif choice == "9":
                book.save_data()
            
            else:
                print("无效选择!")
        
        except Exception as e:
            print(f"错误:{e}")
        
        input("\n按回车键继续...")

if __name__ == "__main__":
    main()

项目特点

  1. 模块化设计:Contact和AddressBook类职责清晰
  2. 数据持久化:支持JSON和CSV两种格式
  3. 错误处理:完善的异常处理机制
  4. 用户友好:清晰的菜单和提示信息
  5. 扩展性:易于添加新功能(如分组管理、备份等)

4.2 项目2:简易计算器(GUI版本)

项目描述:使用Tkinter创建图形界面计算器,支持基本运算、历史记录和科学计算功能。

完整代码实现

import tkinter as tk
from tkinter import messagebox
import math

class Calculator:
    """图形界面计算器"""
    
    def __init__(self, root):
        self.root = root
        self.root.title("简易计算器")
        self.root.geometry("400x550")
        self.root.resizable(False, False)
        
        # 表达式和结果
        self.expression = ""
        self.result_var = tk.StringVar()
        self.result_var.set("0")
        
        # 历史记录
        self.history = []
        
        self.create_widgets()
    
    def create_widgets(self):
        """创建界面组件"""
        # 显示区域
        display_frame = tk.Frame(self.root, bg="#f0f0f0", padx=10, pady=10)
        display_frame.pack(fill="x")
        
        # 历史记录标签
        self.history_label = tk.Label(
            display_frame, 
            text="历史记录:", 
            font=("Arial", 10),
            bg="#f0f0f0",
            fg="#666",
            anchor="w"
        )
        self.history_label.pack(fill="x")
        
        # 结果显示
        result_label = tk.Label(
            display_frame,
            textvariable=self.result_var,
            font=("Arial", 24, "bold"),
            bg="#f0f0f0",
            fg="#000",
            anchor="e",
            padx=5
        )
        result_label.pack(fill="x", pady=5)
        
        # 按钮区域
        button_frame = tk.Frame(self.root, bg="#ddd")
        button_frame.pack(fill="both", expand=True, padx=5, pady=5)
        
        # 按钮布局
        buttons = [
            ["C", "⌫", "%", "/"],
            ["7", "8", "9", "*"],
            ["4", "5", "6", "-"],
            ["1", "2", "3", "+"],
            ["0", ".", "=", "√"]
        ]
        
        for i, row in enumerate(buttons):
            for j, text in enumerate(row):
                btn = tk.Button(
                    button_frame,
                    text=text,
                    font=("Arial", 14),
                    width=5,
                    height=2,
                    bg=self.get_button_color(text),
                    fg="#000" if text not in ["C", "⌫"] else "#d00",
                    command=lambda t=text: self.on_button_click(t)
                )
                btn.grid(row=i, column=j, padx=2, pady=2, sticky="nsew")
        
        # 配置网格权重
        for i in range(5):
            button_frame.grid_rowconfigure(i, weight=1)
        for j in range(4):
            button_frame.grid_columnconfigure(j, weight=1)
        
        # 历史记录按钮
        history_btn = tk.Button(
            self.root,
            text="查看历史记录",
            font=("Arial", 10),
            bg="#4CAF50",
            fg="white",
            command=self.show_history
        )
        history_btn.pack(fill="x", padx=10, pady=5)
    
    def get_button_color(self, text):
        """根据按钮文本返回颜色"""
        if text in ["C", "⌫"]:
            return "#ffcccc"
        elif text in ["+", "-", "*", "/", "=", "%", "√"]:
            return "#ffaa00"
        else:
            return "#e0e0e0"
    
    def on_button_click(self, text):
        """按钮点击事件处理"""
        if text == "C":
            self.expression = ""
            self.result_var.set("0")
        
        elif text == "⌫":
            self.expression = self.expression[:-1]
            if not self.expression:
                self.result_var.set("0")
            else:
                self.result_var.set(self.expression)
        
        elif text == "=":
            self.calculate()
        
        elif text == "√":
            self.square_root()
        
        elif text == "%":
            self.expression += "%"
            self.result_var.set(self.expression)
        
        else:
            # 数字和运算符
            self.expression += text
            self.result_var.set(self.expression)
    
    def calculate(self):
        """计算表达式"""
        if not self.expression:
            return
        
        try:
            # 处理百分号
            expr = self.expression.replace("%", "/100")
            
            # 安全计算(使用eval,实际项目中应避免)
            result = eval(expr)
            
            # 添加到历史记录
            self.history.append(f"{self.expression} = {result}")
            
            # 更新显示
            self.result_var.set(str(result))
            self.expression = str(result)
            
            # 更新历史记录标签
            self.update_history_label()
            
        except Exception as e:
            messagebox.showerror("错误", f"计算错误:{e}")
            self.expression = ""
            self.result_var.set("0")
    
    def square_root(self):
        """平方根计算"""
        if not self.expression:
            return
        
        try:
            value = float(self.expression)
            result = math.sqrt(value)
            
            self.history.append(f"√{self.expression} = {result}")
            self.result_var.set(str(result))
            self.expression = str(result)
            
            self.update_history_label()
            
        except Exception as e:
            messagebox.showerror("错误", f"计算错误:{e}")
    
    def update_history_label(self):
        """更新历史记录显示"""
        if self.history:
            last = self.history[-1]
            self.history_label.config(text=f"历史:{last}")
    
    def show_history(self):
        """显示完整历史记录"""
        if not self.history:
            messagebox.showinfo("历史记录", "暂无计算记录")
            return
        
        history_text = "\n".join(self.history[-10:])  # 显示最近10条
        messagebox.showinfo("历史记录", history_text)

def main():
    root = tk.Tk()
    app = Calculator(root)
    root.mainloop()

if __name__ == "__main__":
    main()

项目特点

  1. GUI编程:使用Tkinter创建图形界面
  2. 事件驱动:按钮点击事件处理
  3. 状态管理:维护表达式和结果状态
  4. 历史记录:记录计算历史
  5. 错误处理:友好的错误提示

第五阶段:进阶挑战(可选)

5.1 网络编程基础

题目:简易HTTP服务器

完整代码

from http.server import HTTPServer, BaseHTTPRequestHandler
import json
from datetime import datetime

class SimpleHTTPHandler(BaseHTTPRequestHandler):
    """自定义HTTP请求处理器"""
    
    def do_GET(self):
        """处理GET请求"""
        if self.path == "/":
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(b"""
                <html>
                <head><title>简易HTTP服务器</title></head>
                <body>
                    <h1>欢迎访问简易HTTP服务器</h1>
                    <p>可用端点:</p>
                    <ul>
                        <li>GET / - 本页面</li>
                        <li>GET /api/time - 获取当前时间</li>
                        <li>GET /api/info - 获取服务器信息</li>
                    </ul>
                </body>
                </html>
            """)
        
        elif self.path == "/api/time":
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            response = {
                "timestamp": datetime.now().isoformat(),
                "message": "当前时间"
            }
            self.wfile.write(json.dumps(response).encode())
        
        elif self.path == "/api/info":
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            response = {
                "server": "SimpleHTTPServer/1.0",
                "python_version": "3.x",
                "timestamp": datetime.now().isoformat()
            }
            self.wfile.write(json.dumps(response).encode())
        
        else:
            self.send_response(404)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write(b"404 Not Found")
    
    def log_message(self, format, *args):
        """自定义日志格式"""
        print(f"[{self.log_date_time_string()}] {format % args}")

def run_server(port=8000):
    """启动HTTP服务器"""
    server_address = ('', port)
    httpd = HTTPServer(server_address, SimpleHTTPHandler)
    
    print(f"🚀 服务器启动在 http://localhost:{port}")
    print("按 Ctrl+C 停止服务器")
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\n🛑 服务器已停止")
        httpd.shutdown()

if __name__ == "__main__":
    run_server()

使用说明

  1. 运行代码:python server.py
  2. 访问浏览器:http://localhost:8000
  3. 测试API:http://localhost:8000/api/time

5.2 数据分析基础

题目:销售数据分析器

完整代码

import csv
import json
from collections import defaultdict
from datetime import datetime

class SalesAnalyzer:
    """销售数据分析器"""
    
    def __init__(self, data_file="sales.csv"):
        self.data_file = data_file
        self.data = []
        self.load_data()
    
    def load_data(self):
        """加载销售数据"""
        if not os.path.exists(self.data_file):
            # 创建示例数据
            self.create_sample_data()
        
        with open(self.data_file, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                # 数据类型转换
                row['date'] = datetime.strptime(row['date'], '%Y-%m-%d')
                row['amount'] = float(row['amount'])
                row['quantity'] = int(row['quantity'])
                self.data.append(row)
    
    def create_sample_data(self):
        """创建示例数据"""
        sample_data = [
            {"date": "2024-01-01", "product": "笔记本", "amount": 5000, "quantity": 2, "category": "电子"},
            {"date": "2024-01-02", "product": "鼠标", "amount": 150, "quantity": 5, "category": "电子"},
            {"date": "2024-01-03", "product": "键盘", "amount": 300, "quantity": 3, "category": "电子"},
            {"date": "2024-01-04", "product": "显示器", "amount": 2000, "quantity": 1, "category": "电子"},
            {"date": "2024-01-05", "product": "笔记本", "amount": 5000, "quantity": 3, "category": "电子"},
            {"date": "2024-01-06", "product": "咖啡", "amount": 50, "quantity": 10, "category": "食品"},
            {"date": "2024-01-07", "product": "零食", "amount": 30, "quantity": 15, "category": "食品"},
        ]
        
        with open(self.data_file, 'w', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=["date", "product", "amount", "quantity", "category"])
            writer.writeheader()
            writer.writerows(sample_data)
        
        print(f"✅ 已创建示例数据:{self.data_file}")
    
    def total_sales(self):
        """总销售额"""
        return sum(item['amount'] for item in self.data)
    
    def total_quantity(self):
        """总销售量"""
        return sum(item['quantity'] for item in self.data)
    
    def sales_by_category(self):
        """按类别统计"""
        result = defaultdict(float)
        for item in self.data:
            result[item['category']] += item['amount']
        return dict(result)
    
    def sales_by_product(self):
        """按产品统计"""
        result = defaultdict(float)
        for item in self.data:
            result[item['product']] += item['amount']
        return dict(result)
    
    def monthly_sales(self):
        """按月统计"""
        result = defaultdict(float)
        for item in self.data]:
            month = item['date'].strftime('%Y-%m')
            result[month] += item['amount']
        return dict(result)
    
    def top_products(self, n=3):
        """Top N 产品"""
        products = self.sales_by_product()
        return sorted(products.items(), key=lambda x: x[1], reverse=True)[:n]
    
    def generate_report(self):
        """生成完整报告"""
        report = {
            "总销售额": self.total_sales(),
            "总销售量": self.total_quantity(),
            "按类别": self.sales_by_category(),
            "按产品": self.sales_by_product(),
            "按月": self.monthly_sales(),
            "Top产品": self.top_products()
        }
        
        print("\n" + "="*50)
        print("销售数据分析报告")
        print("="*50)
        print(f"📊 总销售额:{report['总销售额']:.2f}")
        print(f"📦 总销售量:{report['总销售量']}")
        
        print("\n按类别:")
        for cat, amount in report['按类别'].items():
            print(f"  {cat}: {amount:.2f}")
        
        print("\n按产品:")
        for prod, amount in report['按产品'].items():
            print(f"  {prod}: {amount:.2f}")
        
        print("\n按月:")
        for month, amount in report['按月'].items():
            print(f"  {month}: {amount:.2f}")
        
        print("\nTop 3 产品:")
        for prod, amount in report['Top产品']:
            print(f"  {prod}: {amount:.2f}")
        
        return report

def main():
    analyzer = SalesAnalyzer()
    analyzer.generate_report()

if __name__ == "__main__":
    main()

学习路径与建议

第一阶段(1-2周):基础语法

  • 目标:掌握变量、数据类型、运算符、条件语句、循环、函数
  • 练习:每天完成2-3个基础题目
  • 重点:理解程序执行流程,培养调试能力

第二阶段(2-3周):数据结构

  • 目标:掌握列表、字典、集合、元组,理解算法复杂度
  • 练习:每个数据结构至少完成3个练习题
  • 重点:空间换时间的思想,不同数据结构的适用场景

第三阶段(2-3周):面向对象

  • 目标:理解类与对象、封装、继承、多态
  • 练习:重构之前的代码为OOP版本
  • 重点:代码复用和模块化设计

第四阶段(3-4周):项目实战

  • 目标:综合运用所学知识,完成完整项目
  • 练习:至少完成2个完整项目
  • 重点:项目架构、错误处理、用户体验

进阶建议

  1. 代码规范:遵循PEP8,写好注释和文档
  2. 版本控制:学习使用Git管理代码
  3. 测试驱动:编写单元测试,保证代码质量
  4. 持续学习:关注新技术,参与开源项目

常见问题与解决方案

Q1: 代码运行出错怎么办?

A:

  1. 仔细阅读错误信息,找到出错的行号
  2. 使用print语句调试变量值
  3. 使用调试器(如pdb)逐步执行
  4. 搜索错误信息,参考他人解决方案

Q2: 如何提高编程效率?

A:

  1. 熟练掌握IDE的快捷键
  2. 使用代码片段(Snippets)
  3. 学习标准库和第三方库
  4. 养成良好的代码组织习惯

Q3: 遇到难题没有思路怎么办?

A:

  1. 将问题分解为小步骤
  2. 画流程图或写伪代码
  3. 参考类似问题的解决方案
  4. 寻求社区帮助(Stack Overflow、GitHub)

总结

本指南提供了从零基础到项目实战的完整学习路径,涵盖了基础语法、数据结构、面向对象编程和实际项目开发。关键在于坚持练习动手实践。每个题目都提供了详细的代码示例和解析,建议按照顺序逐步学习,不要急于求成。

记住,编程能力的提升是一个渐进的过程,需要大量的练习和不断的总结。祝你学习顺利,早日成为优秀的程序员!