Python丨课程笔记Part5:更多进阶部分

Part 5 更多

1. 课件11:文件与交互

突破内存易失性的限制,通过文件系统实现数据的持久化存储

1.1. 文件读写基础与持久化概念

持久化:

数据在程序结束后依然存在,通常通过文件系统实现。

必要性:

内存中的数据在程序终止后会丢失,文件存储可以保存重要信息。

1.1.1. 核心函数

open(): 打开文件,返回文件对象。

常用参数:

  • 文件路径:字符串,指定文件位置。
  • 模式:字符串,指定文件操作类型,如 'r'(读取)、'w'(写入)、'a'(追加)、'b'(二进制模式)等。
    • 'r':只读模式,文件必须存在。
    • 'w':写入模式,文件不存在则创建,存在则覆盖。
    • 'a':追加模式,文件不存在则创建,存在则在末尾添加内容。
    • 'b':二进制模式,用于非文本文件,如图片、音频等。
    • '+':读写模式,可与其他模式结合使用,如 'r+'、'w+'。
      实现双向操作
      • 'r+':读写模式,文件必须存在。
      • 'w+':写入读模式,文件不存在则创建,存在则覆盖。
  • 编码:字符串,指定文件编码格式,如 'utf-8'。
1.1.2. 最佳实践

使用 with 语句管理文件上下文,确保文件正确关闭:

python 复制代码
with open('file.txt', 'r', encoding='utf-8') as file:
    content = file.read()
    # 文件在此块结束后自动关闭

它能确保代码块执行完毕后自动安全关闭文件,防止数据损坏或资源泄露

1.2. 结构化数据:CSV读写

CSV:Comma-Separated Values,逗号分隔值文件,用于存储表格数据的文本文件格式。

1.3. 示例代码

python 复制代码
"""
示例:文件读写基础与持久化

包含演示:
- open() 函数和各种文件模式
- with 语句管理文件上下文
- 读取文件的多种方式
- 写入和追加文件内容
- 文件指针位置管理

运行:
    python file_operations_demo.py

作者:自动生成示例(中文注释)
"""


def demo_basic_read_write():
    """演示基本的读写操作"""
    print('--- 基本读写操作 ---')
    
    # 写入文件
    with open('example.txt', 'w', encoding='utf-8') as f:
        f.write('Hello, World!\n')
        f.write('This is line 2.\n')
        f.write('This is line 3.\n')
    print('文件写入完成')
    
    # 读取整个文件
    with open('example.txt', 'r', encoding='utf-8') as f:
        content = f.read()
    print('文件内容:')
    print(content)


def demo_readlines():
    """演示逐行读取"""
    print('--- 逐行读取 ---')
    
    with open('example.txt', 'r', encoding='utf-8') as f:
        lines = f.readlines()  # 返回列表,每个元素包含换行符
    
    print(f'共有 {len(lines)} 行')
    for i, line in enumerate(lines, 1):
        print(f'第 {i} 行:{line.strip()}')
    print()


def demo_readline():
    """演示逐行读取(一次一行)"""
    print('--- 单行读取(for循环) ---')
    
    with open('example.txt', 'r', encoding='utf-8') as f:
        for i, line in enumerate(f, 1): # 类似生成器的行为
            print(f'第 {i} 行:{line.strip()}')
    print()


def demo_append():
    """演示追加模式"""
    print('--- 追加模式 ---')
    
    # 原始内容
    print('原始内容:')
    with open('example.txt', 'r', encoding='utf-8') as f:
        print(f.read())
    
    # 追加新内容
    with open('example.txt', 'a', encoding='utf-8') as f:
        f.write('This is appended line 4.\n')
        f.write('This is appended line 5.\n')
    
    print('追加后的内容:')
    with open('example.txt', 'r', encoding='utf-8') as f:
        print(f.read())


def demo_read_write_mode():
    """演示读写模式(r+ 和 w+)"""
    print('--- 读写模式 ---')
    
    # w+ 模式:写入并读取
    with open('rw_example.txt', 'w+', encoding='utf-8') as f:
        f.write('Line 1\n')
        f.write('Line 2\n')
        f.write('Line 3\n')
        
        # 回到文件开头读取
        f.seek(0)
        content = f.read()
        print('w+ 模式下的内容:')
        print(content)
    
    # r+ 模式:读取并修改
    with open('rw_example.txt', 'r+', encoding='utf-8') as f:
        f.seek(0)
        content = f.read()
        print('原始内容:')
        print(content)
        
        # 从开头写入(覆盖)
        f.seek(0)
        f.write('Modified content\n')
    
    print('修改后的内容:')
    with open('rw_example.txt', 'r', encoding='utf-8') as f:
        print(f.read())
    print()


def demo_file_pointer():
    """演示文件指针操作"""
    print('--- 文件指针操作 ---')
    
    with open('example.txt', 'r', encoding='utf-8') as f:
        # 读取前 10 个字符
        print('前 10 个字符:', f.read(10))
        print('当前指针位置:', f.tell())
        
        # 回到开头
        f.seek(0)
        print('回到开头后的指针位置:', f.tell())
        
        # 读取下一行
        first_line = f.readline()
        print('第一行:', first_line.strip())
        print('当前指针位置:', f.tell())
    print()


def demo_exception_handling():
    """演示异常处理"""
    print('--- 异常处理 ---')
    
    # 尝试读取不存在的文件
    try:
        with open('nonexistent.txt', 'r', encoding='utf-8') as f:
            content = f.read()
    except FileNotFoundError:
        print('捕获异常:文件不存在')
    
    # 权限错误示例(注释掉,避免权限问题)
    try:
        with open('example.txt', 'r', encoding='utf-8') as f:
            content = f.read()
        print('文件成功打开')
    except PermissionError:
        print('权限不足')
    except IOError as e:
        print(f'IO错误:{e}')
    print()


def cleanup():
    """清理示例文件"""
    import os
    files = ['example.txt', 'rw_example.txt']
    for file in files:
        if os.path.exists(file):
            os.remove(file)
    print('清理示例文件完成')


def main():
    print('=' * 50)
    print('文件读写基础演示')
    print('=' * 50)
    print()
    
    demo_basic_read_write()
    print()
    
    demo_readlines()
    
    demo_readline()
    
    demo_append()
    print()
    
    demo_read_write_mode()
    
    demo_file_pointer()
    
    demo_exception_handling()
    
    cleanup()


if __name__ == '__main__':
    main()
1.2.1. 读写CSV文件

模块:
csv:Python标准库中的模块,提供读写CSV文件的功能。

读取:
csv.reader():创建一个读取器对象,用于逐行读取CSV文件内容。

参数:文件对象

返回值:迭代器

每一行解析为一个字符串列表。

方法:

  • next(): 读取下一行数据。

写入:
csv.writer():创建一个写入器对象,用于将数据写入CSV文件。

方法:

  • writerow(): 写入一行数据。
  • writerows(): 写入多行数据。

写入时,建议使用 newline='' 参数打开文件,防止多余空行出现:

1.2.2. 示例代码
python 复制代码
"""
示例:CSV 文件读写

包含演示:
- csv.reader() 读取 CSV 文件
- csv.writer() 写入 CSV 文件
- 处理表格数据
- csv.DictReader 和 csv.DictWriter 高级用法

运行:
    python csv_handler_demo.py

作者:自动生成示例(中文注释)
"""

import csv
import os


def demo_write_csv():
    """演示写入 CSV 文件"""
    print('--- 写入 CSV 文件 ---')
    
    # 准备数据
    data = [
        ['姓名', '年龄', '城市'],
        ['Alice', 25, 'Beijing'],
        ['Bob', 30, 'Shanghai'],
        ['Charlie', 28, 'Guangzhou'],
        ['Diana', 26, 'Shenzhen']
    ]
    
    # 写入 CSV 文件
    # newline='' 可以防止多余的空行出现
    with open('students.csv', 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        writer.writerows(data)
    
    print('CSV 文件写入完成')


def demo_read_csv():
    """演示读取 CSV 文件"""
    print('--- 读取 CSV 文件 ---')
    
    with open('students.csv', 'r', encoding='utf-8') as f:
        reader = csv.reader(f)
        for i, row in enumerate(reader):
            if i == 0:
                print(f'表头:{row}')
            else:
                print(f'  {row}')
    print()


def demo_dict_writer():
    """演示字典形式的 CSV 写入"""
    print('--- 字典形式的 CSV 写入 ---')
    
    # 字典形式的数据
    data = [
        {'姓名': 'Emma', '年龄': 27, '城市': 'Hangzhou'},
        {'姓名': 'Frank', '年龄': 29, '城市': 'Chengdu'},
        {'姓名': 'Grace', '年龄': 24, '城市': 'Wuhan'}
    ]
    
    fieldnames = ['姓名', '年龄', '城市']
    
    with open('more_students.csv', 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()  # 写入表头
        writer.writerows(data)
    
    print('字典形式的 CSV 文件写入完成')


def demo_dict_reader():
    """演示字典形式的 CSV 读取"""
    print('--- 字典形式的 CSV 读取 ---')
    
    with open('more_students.csv', 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for i, row in enumerate(reader, 1):
            print(f'第 {i} 行:{dict(row)}')
    print()


def demo_data_analysis():
    """演示 CSV 数据分析"""
    print('--- CSV 数据分析 ---')
    
    # 读取数据并分析
    ages = []
    with open('students.csv', 'r', encoding='utf-8') as f:
        reader = csv.reader(f)
        next(reader)  # 跳过表头
        for row in reader:
            ages.append(int(row[1]))
    
    print(f'平均年龄:{sum(ages) / len(ages):.1f}')
    print(f'最小年龄:{min(ages)}')
    print(f'最大年龄:{max(ages)}')
    print()


def demo_csv_with_different_delimiter():
    """演示使用不同分隔符的 CSV"""
    print('--- 不同分隔符的 CSV ---')
    
    data = [
        ['Name', 'Score', 'Grade'],
        ['Alice', 85, 'B'],
        ['Bob', 92, 'A'],
        ['Charlie', 78, 'C']
    ]
    
    # 使用分号作为分隔符
    with open('scores.csv', 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f, delimiter=';')
        writer.writerows(data)
    
    print('使用分号分隔符的 CSV 文件写入完成')
    
    # 读取时指定分隔符
    with open('scores.csv', 'r', encoding='utf-8') as f:
        reader = csv.reader(f, delimiter=';')
        for row in reader:
            print(row)
    print()


def cleanup():
    """清理示例文件"""
    files = ['students.csv', 'more_students.csv', 'scores.csv']
    for file in files:
        if os.path.exists(file):
            os.remove(file)
    print('清理示例文件完成')


def main():
    print('=' * 50)
    print('CSV 文件读写演示')
    print('=' * 50)
    print()
    
    demo_write_csv()
    print()
    
    demo_read_csv()
    
    demo_dict_writer()
    print()
    
    demo_dict_reader()
    
    demo_data_analysis()
    
    demo_csv_with_different_delimiter()
    
    cleanup()


if __name__ == '__main__':
    main()

1.3. 复杂数据:JSON读写

JSON(JavaScript Object Notation):一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。

网络API的事实标准格式

语法与字典和列表几乎相同

1.3.1. 读写JSON文件

模块:
json:Python标准库中的模块,提供读写JSON文件的功能。

文件操作:

  • json.dump(obj, file): 将Python对象 obj 序列化为JSON格式,并写入文件对象 file
  • json.load(file): 从文件对象 file 中读取JSON数据,并反序列化为Python对象。

内存字符串操作:

  • json.dumps(obj): 将Python对象 obj 序列化为JSON格式的字符串。
  • json.loads(s): 将JSON格式的字符串 s 反序列化为Python对象。

优势:

能完美表示嵌套和层级结构,支持布尔值(对应 JSON

的 true/false)和空值(对应 JSON 的 null)的自动转换

1.3.2. 代码示例
python 复制代码
"""
示例:JSON 文件读写与数据序列化

包含演示:
- json.dump() 写入 JSON 文件
- json.load() 读取 JSON 文件
- json.dumps() 序列化为字符串
- json.loads() 反序列化字符串
- Python 对象到 JSON 的类型转换
- 复杂嵌套数据结构

运行:
    python json_handler_demo.py

作者:自动生成示例(中文注释)
"""

import json
import os


def demo_dump_load():
    """演示 dump 和 load(文件操作)"""
    print('--- json.dump() 和 json.load() ---')
    
    # Python 对象
    data = {
        'name': 'Alice',
        'age': 25,
        'city': 'Beijing',
        'hobbies': ['reading', 'coding', 'hiking'],
        'is_student': True,
        'gpa': None
    }
    
    # 写入 JSON 文件
    with open('person.json', 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    
    print('JSON 文件写入完成')
    print('文件内容:')
    with open('person.json', 'r', encoding='utf-8') as f:
        print(f.read())
    
    # 读取 JSON 文件
    with open('person.json', 'r', encoding='utf-8') as f:
        loaded_data = json.load(f)
    
    print('读取的数据:', loaded_data)
    print()


def demo_dumps_loads():
    """演示 dumps 和 loads(字符串操作)"""
    print('--- json.dumps() 和 json.loads() ---')
    
    # Python 对象
    person = {
        'name': 'Bob',
        'age': 30,
        'skills': ['Python', 'JavaScript', 'SQL']
    }
    
    # 序列化为 JSON 字符串
    json_string = json.dumps(person, ensure_ascii=False, indent=2)
    print('JSON 字符串:')
    print(json_string)
    
    # 反序列化
    parsed_data = json.loads(json_string)
    print('解析后的数据:', parsed_data)
    print()


def demo_type_conversion():
    """演示 Python 类型到 JSON 类型的转换"""
    print('--- Python 类型与 JSON 类型转换 ---')
    
    data = {
        'string': 'Hello',
        'integer': 42,
        'float': 3.14,
        'boolean_true': True,
        'boolean_false': False,
        'null_value': None,
        'list': [1, 2, 3],
        'dict': {'nested': 'value'},
        'tuple': (4, 5, 6)  # 元组会转换为列表
    }
    
    json_string = json.dumps(data, indent=2)
    print('转换结果:')
    print(json_string)
    print()


def demo_nested_structure():
    """演示复杂嵌套数据结构"""
    print('--- 复杂嵌套结构 ---')
    
    company_data = {
        'company_name': 'Tech Corp',
        'departments': [
            {
                'name': 'Engineering',
                'employees': [
                    {'id': 1, 'name': 'Alice', 'salary': 80000},
                    {'id': 2, 'name': 'Bob', 'salary': 75000}
                ]
            },
            {
                'name': 'Sales',
                'employees': [
                    {'id': 3, 'name': 'Charlie', 'salary': 70000}
                ]
            }
        ]
    }
    
    # 写入文件
    with open('company.json', 'w', encoding='utf-8') as f:
        json.dump(company_data, f, ensure_ascii=False, indent=2)
    
    print('复杂数据结构写入完成')
    
    # 读取并展示
    with open('company.json', 'r', encoding='utf-8') as f:
        loaded = json.load(f)
    
    print(f'公司:{loaded["company_name"]}')
    for dept in loaded['departments']:
        print(f'  部门:{dept["name"]}')
        for emp in dept['employees']:
            print(f'    员工:{emp["name"]},薪资:{emp["salary"]}')
    print()


def demo_pretty_print():
    """演示格式化输出"""
    print('--- 格式化输出 ---')
    
    data = {'name': 'Diana', 'items': [1, 2, 3, 4, 5]}
    
    # 紧凑格式
    compact = json.dumps(data, separators=(',', ':'))
    print('紧凑格式:', compact)
    
    # 美化格式(默认)
    pretty = json.dumps(data, indent=2)
    print('美化格式:')
    print(pretty)
    print()


def demo_error_handling():
    """演示错误处理"""
    print('--- 错误处理 ---')
    
    # 有效的 JSON
    try:
        valid_json = '{"name": "Eve", "age": 28}'
        data = json.loads(valid_json)
        print('有效的 JSON 解析成功:', data)
    except json.JSONDecodeError as e:
        print('JSON 解码错误:', e)
    
    # 无效的 JSON
    try:
        invalid_json = "{'name': 'Frank'}"  # 使用单引号不符合 JSON 标准
        data = json.loads(invalid_json)
    except json.JSONDecodeError as e:
        print('捕获到 JSON 解码错误(预期)')
    
    # 文件不存在
    try:
        with open('nonexistent.json', 'r') as f:
            data = json.load(f)
    except FileNotFoundError:
        print('捕获到文件不存在错误(预期)')
    print()


def cleanup():
    """清理示例文件"""
    files = ['person.json', 'company.json']
    for file in files:
        if os.path.exists(file):
            os.remove(file)
    print('清理示例文件完成')


def main():
    print('=' * 50)
    print('JSON 文件读写演示')
    print('=' * 50)
    print()
    
    demo_dump_load()
    
    demo_dumps_loads()
    
    demo_type_conversion()
    
    demo_nested_structure()
    
    demo_pretty_print()
    
    demo_error_handling()
    
    cleanup()


if __name__ == '__main__':
    main()

1.4. 实践中的文件处理

防御性编程

文件系统中可能存在的异常很多

注意异常处理

常见异常:

  • FileNotFoundError: 文件未找到。
  • PermissionError: 权限不足。
  • JSONDecodeError: JSON解码错误。
  • IOError: 输入输出错误。

实例:配置加载器

读取、校验、写入默认值分别封装为纯函数

配置文件损坏或丢失,程序应能自动创建并使用预设的默认值,确保系统稳定启动

1.5. 示例代码

python 复制代码
"""
示例:配置加载器与管理

包含演示:
- 从 JSON 文件加载配置
- 验证配置的必要字段
- 与默认值合并
- 创建默认配置文件
- 错误恢复机制

运行:
    python config_loader_demo.py

作者:自动生成示例(中文注释)
"""

import json
import os


# 默认配置常量
DEFAULT_CONFIG = {
    'app_name': 'MyApp',
    'version': '1.0.0',
    'debug': False,
    'log_level': 'INFO',
    'server': {
        'host': 'localhost',
        'port': 8000,
        'timeout': 30
    },
    'database': {
        'engine': 'sqlite',
        'path': './data.db',
        'max_connections': 10
    },
    'features': {
        'enable_cache': True,
        'cache_ttl': 3600,
        'enable_auth': True
    }
}


def load_config(config_file='config.json'):
    """
    加载配置文件
    
    参数:
        config_file (str): 配置文件路径
    
    返回:
        dict: 配置字典
    """
    if not os.path.exists(config_file):
        print(f'配置文件 {config_file} 不存在,使用默认配置')
        return DEFAULT_CONFIG.copy()
    
    try:
        with open(config_file, 'r', encoding='utf-8') as f:
            config = json.load(f)
        print(f'成功加载配置文件:{config_file}')
        return config
    except json.JSONDecodeError as e:
        print(f'配置文件 JSON 格式错误:{e},使用默认配置')
        return DEFAULT_CONFIG.copy()
    except Exception as e:
        print(f'加载配置失败:{e},使用默认配置')
        return DEFAULT_CONFIG.copy()


def validate_config(config):
    """
    验证配置的必要字段
    
    参数:
        config (dict): 要验证的配置
    
    返回:
        tuple: (是否有效, 错误信息列表)
    """
    errors = []
    
    # 验证必要字段
    if 'app_name' not in config:
        errors.append('缺少必要字段:app_name')
    
    if 'server' not in config or not isinstance(config['server'], dict):
        errors.append('缺少必要字段:server(应为字典)')
    else:
        if 'host' not in config['server']:
            errors.append('缺少必要字段:server.host')
        if 'port' not in config['server']:
            errors.append('缺少必要字段:server.port')
        elif not isinstance(config['server']['port'], int):
            errors.append('server.port 应为整数')
    
    if 'database' not in config or not isinstance(config['database'], dict):
        errors.append('缺少必要字段:database(应为字典)')
    else:
        if 'engine' not in config['database']:
            errors.append('缺少必要字段:database.engine')
    
    return len(errors) == 0, errors


def merge_with_defaults(config, defaults):
    """
    将配置与默认值合并(递归)
    
    参数:
        config (dict): 用户配置
        defaults (dict): 默认配置
    
    返回:
        dict: 合并后的配置
    """
    result = defaults.copy()
    
    for key, value in config.items():
        if key in defaults and isinstance(defaults[key], dict) and isinstance(value, dict):
            # 递归合并嵌套字典
            result[key] = merge_with_defaults(value, defaults[key])
        else:
            result[key] = value
    
    return result


def save_config(config, config_file='config.json'):
    """
    保存配置文件
    
    参数:
        config (dict): 要保存的配置
        config_file (str): 配置文件路径
    """
    try:
        with open(config_file, 'w', encoding='utf-8') as f:
            json.dump(config, f, ensure_ascii=False, indent=2)
        print(f'配置已保存到:{config_file}')
    except Exception as e:
        print(f'保存配置失败:{e}')


def demo_basic_loading():
    """演示:基本加载"""
    print('--- 演示 1:基本加载 ---')
    
    # 模拟 1:配置文件不存在
    config = load_config('missing_config.json')
    print(f'加载的配置:app_name={config["app_name"]}, version={config["version"]}')
    print()


def demo_create_default():
    """演示:创建默认配置文件"""
    print('--- 演示 2:创建默认配置 ---')
    
    config_file = 'default_config.json'
    save_config(DEFAULT_CONFIG, config_file)
    
    # 验证创建的文件
    with open(config_file, 'r', encoding='utf-8') as f:
        content = f.read()
    print('创建的配置文件内容(前 300 字符):')
    print(content[:300] + '...')
    print()


def demo_load_and_validate():
    """演示:加载并验证配置"""
    print('--- 演示 3:加载并验证 ---')
    
    # 创建一个测试配置文件
    test_config = {
        'app_name': 'TestApp',
        'server': {
            'host': '0.0.0.0',
            'port': 5000
        },
        'database': {
            'engine': 'postgres'
        }
    }
    
    with open('test_config.json', 'w', encoding='utf-8') as f:
        json.dump(test_config, f)
    
    # 加载并验证
    config = load_config('test_config.json')
    is_valid, errors = validate_config(config)
    
    if is_valid:
        print('✓ 配置验证成功')
    else:
        print('✗ 配置验证失败:')
        for error in errors:
            print(f'  - {error}')
    print()


def demo_merge_with_defaults():
    """演示:与默认值合并"""
    print('--- 演示 4:与默认值合并 ---')
    
    # 部分配置
    partial_config = {
        'app_name': 'CustomApp',
        'server': {
            'port': 9000  # 只覆盖 port,host 使用默认
        }
    }
    
    # 合并
    merged = merge_with_defaults(partial_config, DEFAULT_CONFIG)
    
    print('部分配置:')
    print(f'  app_name={partial_config["app_name"]}')
    print(f'  server.port={partial_config["server"]["port"]}')
    print()
    
    print('合并后的配置:')
    print(f'  app_name={merged["app_name"]}')
    print(f'  server.host={merged["server"]["host"]} (来自默认值)')
    print(f'  server.port={merged["server"]["port"]} (来自用户配置)')
    print(f'  database.engine={merged["database"]["engine"]} (来自默认值)')
    print()


def demo_invalid_config():
    """演示:处理无效的配置"""
    print('--- 演示 5:处理无效配置 ---')
    
    # 创建无效的 JSON 文件
    with open('invalid_config.json', 'w') as f:
        f.write('{ invalid json content }')
    
    config = load_config('invalid_config.json')
    is_valid, errors = validate_config(config)
    print(f'配置验证结果:有效={is_valid}')
    print()


def demo_nested_config():
    """演示:处理嵌套配置"""
    print('--- 演示 6:嵌套配置管理 ---')
    
    # 复杂配置
    complex_config = {
        'app_name': 'ComplexApp',
        'environments': {
            'development': {
                'server': {'host': 'localhost', 'port': 3000},
                'debug': True
            },
            'production': {
                'server': {'host': '0.0.0.0', 'port': 8000},
                'debug': False
            }
        }
    }
    
    # 保存
    save_config(complex_config, 'complex_config.json')
    
    # 加载并显示
    loaded = load_config('complex_config.json')
    print('已加载复杂配置')
    print(f'  开发环境 host: {loaded["environments"]["development"]["server"]["host"]}')
    print(f'  生产环境 debug: {loaded["environments"]["production"]["debug"]}')
    print()


def cleanup():
    """清理示例文件"""
    files = [
        'default_config.json',
        'test_config.json',
        'invalid_config.json',
        'complex_config.json'
    ]
    
    for file in files:
        if os.path.exists(file):
            os.remove(file)
    
    print('清理示例文件完成')


def main():
    print('=' * 50)
    print('配置加载器与管理演示')
    print('=' * 50)
    print()
    
    demo_basic_loading()
    
    demo_create_default()
    
    demo_load_and_validate()
    
    demo_merge_with_defaults()
    
    demo_invalid_config()
    
    demo_nested_config()
    
    cleanup()


if __name__ == '__main__':
    main()

2. 课件12:第三方模块应用

如何安装第三方库

利用 Pandas 进行数据预处理

通过 Matplotlib 和 Seaborn 实现数据可视化

2.1. 第三方模块的安装与管理

核心工具:
pip:Python的包管理工具,用于安装和管理第三方库。

常用命令:

  • 安装:
    pip install package_name:安装指定的包。

  • 卸载:
    pip uninstall package_name:卸载指定的包。

  • 查看已安装包:
    pip list:列出所有已安装的包及其版本。
    pip list:列出所有已安装的包及其版本。

  • 显示包信息:
    pip show package_name:显示指定包的详细信息。

python 复制代码
"""
示例:第三方模块安装与管理

包含演示:
- pip 命令格式和使用
- 包的安装、卸载、查看
- 虚拟环境管理
- requirements.txt 文件的使用

注意:此示例主要展示命令格式,不会实际执行安装操作
需要网络连接和管理员权限才能实际执行

运行:
    python pip_management_demo.py

作者:自动生成示例(中文注释)
"""

import subprocess
import sys
import os


def demo_pip_commands():
    """演示 pip 命令格式(不实际执行)"""
    print('--- pip 命令格式演示 ---')

    commands = [
        {
            'name': '安装包',
            'command': 'pip install package_name',
            'description': '安装指定的包',
            'example': 'pip install pandas'
        },
        {
            'name': '安装特定版本',
            'command': 'pip install package_name==version',
            'description': '安装指定版本的包',
            'example': 'pip install pandas==2.0.0'
        },
        {
            'name': '从 requirements.txt 安装',
            'command': 'pip install -r requirements.txt',
            'description': '从文件安装多个包',
            'example': 'pip install -r requirements.txt'
        },
        {
            'name': '卸载包',
            'command': 'pip uninstall package_name',
            'description': '卸载指定的包',
            'example': 'pip uninstall pandas'
        },
        {
            'name': '查看已安装包',
            'command': 'pip list',
            'description': '列出所有已安装的包及其版本',
            'example': 'pip list'
        },
        {
            'name': '显示包信息',
            'command': 'pip show package_name',
            'description': '显示指定包的详细信息',
            'example': 'pip show pandas'
        },
        {
            'name': '升级包',
            'command': 'pip install --upgrade package_name',
            'description': '升级指定的包到最新版本',
            'example': 'pip install --upgrade pandas'
        },
        {
            'name': '导出依赖',
            'command': 'pip freeze > requirements.txt',
            'description': '将当前环境的所有包导出到文件',
            'example': 'pip freeze > requirements.txt'
        }
    ]

    for cmd in commands:
        print(f"\n{cmd['name']}:")
        print(f"  命令: {cmd['command']}")
        print(f"  说明: {cmd['description']}")
        print(f"  示例: {cmd['example']}")

    print()


def demo_requirements_file():
    """演示 requirements.txt 文件的使用"""
    print('--- requirements.txt 文件示例 ---')

    requirements_content = """# 数据分析相关包
pandas>=2.0.0
numpy>=1.24.0
matplotlib>=3.7.0
seaborn>=0.12.0

# 机器学习包
scikit-learn>=1.3.0
tensorflow>=2.13.0

# 开发工具
jupyter>=1.0.0
ipykernel>=6.0.0

# 其他工具
requests>=2.31.0
beautifulsoup4>=4.12.0
"""

    # 创建示例 requirements.txt 文件
    with open('example_requirements.txt', 'w', encoding='utf-8') as f:
        f.write(requirements_content)

    print('已创建 example_requirements.txt 文件')
    print('文件内容:')
    print(requirements_content)

    # 演示如何从文件安装(命令格式)
    print('安装命令:')
    print('  pip install -r example_requirements.txt')
    print()


def demo_virtual_environment():
    """演示虚拟环境管理"""
    print('--- 虚拟环境管理 ---')

    venv_commands = [
        {
            'name': '创建虚拟环境',
            'command': 'python -m venv myenv',
            'description': '创建名为 myenv 的虚拟环境'
        },
        {
            'name': '激活虚拟环境 (Windows)',
            'command': 'myenv\\Scripts\\activate',
            'description': '激活虚拟环境'
        },
        {
            'name': '激活虚拟环境 (Linux/Mac)',
            'command': 'source myenv/bin/activate',
            'description': '激活虚拟环境'
        },
        {
            'name': '退出虚拟环境',
            'command': 'deactivate',
            'description': '退出当前虚拟环境'
        },
        {
            'name': '删除虚拟环境',
            'command': 'rmdir /s myenv  (Windows) 或 rm -rf myenv (Linux/Mac)',
            'description': '删除虚拟环境目录'
        }
    ]

    for cmd in venv_commands:
        print(f"\n{cmd['name']}:")
        print(f"  命令: {cmd['command']}")
        print(f"  说明: {cmd['description']}")

    print()


def demo_package_info():
    """演示获取已安装包的信息"""
    print('--- 查看已安装包信息 ---')

    try:
        # 尝试获取 pip 版本
        result = subprocess.run([sys.executable, '-m', 'pip', '--version'],
                              capture_output=True, text=True, check=True)
        print(f"pip 版本: {result.stdout.strip()}")

        # 查看已安装的包(限制输出)
        result = subprocess.run([sys.executable, '-m', 'pip', 'list', '--format=freeze'],
                              capture_output=True, text=True, check=True)

        installed_packages = result.stdout.strip().split('\n')
        print(f"\n已安装包数量: {len(installed_packages)}")

        # 显示前10个包
        print("前10个已安装包:")
        for i, pkg in enumerate(installed_packages[:10]):
            print(f"  {i+1}. {pkg}")

        if len(installed_packages) > 10:
            print(f"  ... 还有 {len(installed_packages) - 10} 个包")

    except subprocess.CalledProcessError as e:
        print(f"执行 pip 命令失败: {e}")
    except FileNotFoundError:
        print("未找到 pip 命令,请确保 Python 已正确安装")

    print()


def demo_import_test():
    """测试一些常用包的导入"""
    print('--- 常用包导入测试 ---')

    test_packages = [
        ('pandas', 'pd'),
        ('numpy', 'np'),
        ('matplotlib', 'plt'),
        ('seaborn', 'sns'),
        ('scikit-learn', 'sklearn'),
        ('requests', None),
        ('json', None),  # 标准库
        ('csv', None),   # 标准库
    ]

    for package, alias in test_packages:
        try:
            if alias:
                exec(f"import {package} as {alias}")
                print(f"✓ {package} ({alias}) - 已安装")
            else:
                exec(f"import {package}")
                print(f"✓ {package} - 已安装")
        except ImportError:
            print(f"✗ {package} - 未安装")
        except Exception as e:
            print(f"? {package} - 导入错误: {e}")

    print()


def cleanup():
    """清理示例文件"""
    files = ['example_requirements.txt']
    for file in files:
        if os.path.exists(file):
            os.remove(file)
    print('清理示例文件完成')


def main():
    print('=' * 50)
    print('第三方模块安装与管理演示')
    print('=' * 50)
    print()

    demo_pip_commands()

    demo_requirements_file()

    demo_virtual_environment()

    demo_package_info()

    demo_import_test()

    cleanup()


if __name__ == '__main__':
    main()
2.1.1. 包

包(Package)的概念:

包是包含多个模块的目录,目录中必须包含一个 __init__.py 文件

用于标识该目录为一个包。

2.2. 数据预处理

Data Preprocessing

垃圾进,垃圾出

数据清洗在数据分析中占据重要地位,占时一般也是最多的

2.2.1. 数据清理
  • 缺失值处理:
    • isnull(): 检测缺失值。
    • dropna(): 删除缺失值。
    • fillna(): 填充缺失值。
    • ffill(): 向前填充缺失值。
    • bfill(): 向后填充缺失值。
  • 重复值处理:
    • duplicated(): 检测重复值。
    • drop_duplicates(): 删除重复值。
  • 异常值处理:
    • 四分位距(IQR)法:计算Q1和Q3,定义异常值范围。
      小于下界 Q1−1.5×IQRQ1 - 1.5 \times IQRQ1−1.5×IQR 的值
      或者大于上界 Q3+1.5×IQRQ3 + 1.5 \times IQRQ3+1.5×IQR 的值
      可通过 箱体图 或 小提琴图 进行可视化检测。
  • 数据集成:
    • 合并:
      pd.merge(): 基于键合并数据集。
    • 堆叠:
      pd.concat(): 沿指定轴连接数据集。
      按行或列方向堆叠多个数据集
      有点类似于关系运算中的连接
  • 数据转换:
    • 标准化:
      • Z-score 标准化:均值为0,标准差为1。
      • Min-Max 标准化:缩放到指定范围(通常为0到1)。
    • 离散化:
      • pd.cut(): 将连续变量分箱。
        例如将年龄分为青年、中年、老年等类别。
      • pd.qcut(): 基于分位数分箱。
  • 数据规约(Data Reduction):
    主成分分析(PCA):通过线性变换将高维数据映射至低维空间,保留主要信息。
    常用于多维数据可视化

2.3. 数据可视化

两种主要绘图库及应用场景:

2.3.1. Matplotlib 基础
  • 图表结构
    • Figure:图表容器。
    • Axes:坐标系。
    • Axis:坐标轴。
    • Subplot:子图。
  • 绘图流程
    1. 准备数据
    2. 绘制图形
    3. 保存或显示图形
  • 基础图形
    • scatter: 散点图。
    • bar:柱状图。
    • hist:直方图。
    • plot:折线图。
    • pie:饼图。
python 复制代码
"""
示例:Matplotlib 基础绘图演示

包含演示:
- 图表结构(Figure, Axes, Axis, Subplot)
- 绘图流程(准备数据、绘制、保存/显示)
- 基础图形:scatter, bar, hist, plot, pie
- 子图和多图布局

运行:
    python matplotlib_basics_demo.py

依赖:
    pip install matplotlib numpy pandas

注意:如果在无GUI环境中运行,可能需要设置后端:
    import matplotlib
    matplotlib.use('Agg')  # 使用非交互式后端

作者:自动生成示例(中文注释)
"""

try:
    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    MATPLOTLIB_AVAILABLE = True
except ImportError:
    print("警告:matplotlib 未安装,跳过绘图演示")
    MATPLOTLIB_AVAILABLE = False
    # 不设置 plt = None, np = None, pd = None,避免 Pylance 类型推断问题
import os

import os

# 设置中文字体支持(如果有中文字体的话)
if MATPLOTLIB_AVAILABLE:
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
    plt.rcParams['axes.unicode_minus'] = False

# 如果在无GUI环境中,设置后端
try:
    if MATPLOTLIB_AVAILABLE:
        import matplotlib
        matplotlib.use('Agg')
    SAVE_ONLY = True
except:
    SAVE_ONLY = False


def parse_arguments():
    """解析命令行参数"""
    import argparse
    parser = argparse.ArgumentParser(description='Matplotlib 基础绘图演示')
    parser.add_argument('--save-images', action='store_true',
                       help='保存生成的图片文件(默认不保存)')
    return parser.parse_args()


def ensure_dir(dir_name):
    """确保目录存在"""
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)


def demo_chart_structure():
    """演示图表结构"""
    print('--- 图表结构演示 ---')

    # 创建Figure和Axes
    fig, ax = plt.subplots(figsize=(8, 6))

    # 设置标题和标签
    ax.set_title('图表结构示例', fontsize=16, fontweight='bold')
    ax.set_xlabel('X轴标签')
    ax.set_ylabel('Y轴标签')

    # 添加网格
    ax.grid(True, alpha=0.3)

    # 添加文本注释
    ax.text(0.5, 0.5, '这是Axes区域', ha='center', va='center',
            transform=ax.transAxes, fontsize=12, bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))

    # 保存图表
    ensure_dir('matplotlib_basics')
    plt.savefig('matplotlib_basics/chart_structure.png', dpi=150, bbox_inches='tight')
    print('图表已保存为 matplotlib_basics/chart_structure.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()

    print('Figure: 整个图表容器')
    print('Axes: 坐标系区域')
    print('Axis: X轴和Y轴')
    print('Title: 图表标题')
    print('Labels: 轴标签')
    print()


def demo_scatter_plot():
    """演示散点图"""
    print('--- 散点图 (scatter) ---')

    # 生成随机数据
    np.random.seed(42)
    x = np.random.randn(100)
    y = 2 * x + np.random.randn(100) * 0.5
    sizes = np.random.randint(20, 200, 100)
    colors = np.random.rand(100)

    # 创建散点图
    fig, ax = plt.subplots(figsize=(10, 6))

    scatter = ax.scatter(x, y, s=sizes, c=colors, alpha=0.6, cmap='viridis')

    # 设置标题和标签
    ax.set_title('散点图示例', fontsize=16, fontweight='bold')
    ax.set_xlabel('X变量')
    ax.set_ylabel('Y变量')

    # 添加颜色条
    plt.colorbar(scatter, ax=ax, label='颜色值')

    # 添加网格
    ax.grid(True, alpha=0.3)

    plt.savefig('matplotlib_basics/scatter_plot.png', dpi=150, bbox_inches='tight')
    print('散点图已保存为 matplotlib_basics/scatter_plot.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()
    print()


def demo_bar_chart():
    """演示柱状图"""
    print('--- 柱状图 (bar) ---')

    # 准备数据
    categories = ['产品A', '产品B', '产品C', '产品D', '产品E']
    sales = [120, 150, 98, 175, 132]
    colors = ['skyblue', 'lightgreen', 'lightcoral', 'gold', 'violet']

    # 创建柱状图
    fig, ax = plt.subplots(figsize=(10, 6))

    bars = ax.bar(categories, sales, color=colors, alpha=0.8, width=0.6)

    # 添加数值标签
    for bar, value in zip(bars, sales):
        ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,
                f'{value}', ha='center', va='bottom', fontweight='bold')

    # 设置标题和标签
    ax.set_title('产品销售柱状图', fontsize=16, fontweight='bold')
    ax.set_xlabel('产品类别')
    ax.set_ylabel('销售额')

    # 设置Y轴范围
    ax.set_ylim(0, max(sales) * 1.1)

    # 添加网格
    ax.grid(True, axis='y', alpha=0.3)

    plt.savefig('matplotlib_basics/bar_chart.png', dpi=150, bbox_inches='tight')
    print('柱状图已保存为 matplotlib_basics/bar_chart.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()
    print()


def demo_histogram():
    """演示直方图"""
    print('--- 直方图 (hist) ---')

    # 生成正态分布数据
    np.random.seed(42)
    data1 = np.random.normal(50, 10, 1000)  # 均值50,标准差10
    data2 = np.random.normal(70, 15, 1000)  # 均值70,标准差15

    # 创建直方图
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))

    # 子图1:单个直方图
    ax1.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
    ax1.set_title('单个数据集直方图', fontsize=14, fontweight='bold')
    ax1.set_xlabel('值')
    ax1.set_ylabel('频次')
    ax1.grid(True, alpha=0.3)

    # 子图2:多个数据集比较
    ax2.hist([data1, data2], bins=30, alpha=0.7, label=['数据集1', '数据集2'],
             color=['skyblue', 'lightgreen'], edgecolor='black')
    ax2.set_title('多个数据集直方图比较', fontsize=14, fontweight='bold')
    ax2.set_xlabel('值')
    ax2.set_ylabel('频次')
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.savefig('matplotlib_basics/histogram.png', dpi=150, bbox_inches='tight')
    print('直方图已保存为 matplotlib_basics/histogram.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()
    print()


def demo_line_plot():
    """演示折线图"""
    print('--- 折线图 (plot) ---')

    # 准备时间序列数据
    months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
    sales_2023 = [120, 135, 148, 162, 175, 168, 185, 192, 178, 195, 210, 225]
    sales_2024 = [125, 142, 155, 168, 182, 175, 195, 205, 188, 202, 218, 235]

    # 创建折线图
    fig, ax = plt.subplots(figsize=(12, 6))

    ax.plot(months, sales_2023, marker='o', linestyle='-', linewidth=2,
            color='blue', label='2023年', markersize=6)
    ax.plot(months, sales_2024, marker='s', linestyle='--', linewidth=2,
            color='red', label='2024年', markersize=6)

    # 设置标题和标签
    ax.set_title('年度销售趋势对比', fontsize=16, fontweight='bold')
    ax.set_xlabel('月份')
    ax.set_ylabel('销售额(万元)')

    # 添加图例
    ax.legend()

    # 添加网格
    ax.grid(True, alpha=0.3)

    # 旋转X轴标签
    plt.xticks(rotation=45)

    plt.tight_layout()
    plt.savefig('matplotlib_basics/line_plot.png', dpi=150, bbox_inches='tight')
    print('折线图已保存为 matplotlib_basics/line_plot.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()
    print()


def demo_pie_chart():
    """演示饼图"""
    print('--- 饼图 (pie) ---')

    # 准备数据
    departments = ['技术部', '销售部', '市场部', '财务部', '人事部']
    budgets = [35, 25, 20, 12, 8]
    colors = ['lightblue', 'lightgreen', 'lightcoral', 'gold', 'violet']
    explode = [0.1, 0, 0, 0, 0]  # 突出显示第一个扇形

    # 创建饼图
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))

    # 子图1:基础饼图
    wedges1, texts1, autotexts1 = ax1.pie(budgets, labels=departments, colors=colors,
                                         autopct='%1.1f%%', startangle=90, explode=explode)

    ax1.set_title('各部门预算分配(突出技术部)', fontsize=14, fontweight='bold')

    # 子图2:环形饼图
    wedges2, texts2, autotexts2 = ax2.pie(budgets, labels=departments, colors=colors,
                                         autopct='%1.1f%%', startangle=90, wedgeprops=dict(width=0.3))

    ax2.set_title('各部门预算分配(环形图)', fontsize=14, fontweight='bold')

    # 调整字体大小
    for autotext in autotexts1 + autotexts2:
        autotext.set_fontsize(10)
        autotext.set_fontweight('bold')

    plt.tight_layout()
    plt.savefig('matplotlib_basics/pie_chart.png', dpi=150, bbox_inches='tight')
    print('饼图已保存为 matplotlib_basics/pie_chart.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()
    print()


def demo_subplots():
    """演示子图布局"""
    print('--- 子图和多图布局 ---')

    # 创建2x2的子图布局
    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
    fig.suptitle('多图布局示例', fontsize=16, fontweight='bold')

    # 子图1:散点图
    np.random.seed(42)
    x = np.random.randn(50)
    y = 2 * x + np.random.randn(50)
    ax1.scatter(x, y, alpha=0.6, color='blue')
    ax1.set_title('散点图')
    ax1.grid(True, alpha=0.3)

    # 子图2:柱状图
    categories = ['A', 'B', 'C', 'D']
    values = [10, 15, 7, 12]
    ax2.bar(categories, values, color='lightgreen', alpha=0.7)
    ax2.set_title('柱状图')
    ax2.grid(True, axis='y', alpha=0.3)

    # 子图3:直方图
    data = np.random.normal(0, 1, 200)
    ax3.hist(data, bins=20, alpha=0.7, color='lightcoral', edgecolor='black')
    ax3.set_title('直方图')
    ax3.grid(True, alpha=0.3)

    # 子图4:饼图
    sizes = [30, 25, 20, 15, 10]
    labels = ['A', 'B', 'C', 'D', 'E']
    ax4.pie(sizes, labels=labels, autopct='%1.0f%%', startangle=90)
    ax4.set_title('饼图')

    plt.tight_layout()
    plt.savefig('matplotlib_basics/subplots_demo.png', dpi=150, bbox_inches='tight')
    print('子图布局已保存为 matplotlib_basics/subplots_demo.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()
    print()


def cleanup():
    """清理生成的图片文件"""
    image_dir = 'matplotlib_basics'
    if os.path.exists(image_dir):
        import shutil
        shutil.rmtree(image_dir)
        print('清理图片目录完成')


def main():
    print('=' * 50)
    print('Matplotlib 基础绘图演示')
    print('=' * 50)
    print()

    # 解析命令行参数
    args = parse_arguments()

    if not MATPLOTLIB_AVAILABLE:
        print('matplotlib 未安装,无法运行绘图演示')
        print('请运行:pip install matplotlib numpy pandas')
        return

    try:
        demo_chart_structure()
        demo_scatter_plot()
        demo_bar_chart()
        demo_histogram()
        demo_line_plot()
        demo_pie_chart()
        demo_subplots()

        print('所有图表已生成并保存为PNG文件')
        if SAVE_ONLY:
            print('注意:当前环境为无GUI模式,所有图表已保存为文件')

    except ImportError as e:
        print(f'导入错误:{e}')
        print('请安装必要的包:pip install matplotlib numpy pandas')
    except Exception as e:
        print(f'运行错误:{e}')
    finally:
        # 根据命令行参数决定是否清理图片
        if not args.save_images:
            cleanup()
        else:
            print('图片文件已保存,可在当前目录查看')
            print('下次运行时如需清理,请不使用 --save-images 参数')


if __name__ == '__main__':
    main()
2.3.2. Seaborn 高阶绘图
  • 优势:
    语法简介,内置美观调色板(palette),适合统计图表绘制。
  • 高阶图形:
    • 小提琴图(violinplot):显示数据分布及其概率密度。
    • 箱线图(boxplot):显示数据的分位数及异常值。
    • 回归模型图(lmplot):显示变量间的线性关系及回归线。
    • 成对图(pairplot):显示多变量间的两两关系。
python 复制代码
"""
示例:Seaborn 高级绘图演示

包含演示:
- 小提琴图(violinplot):显示数据分布及其概率密度
- 箱线图(boxplot):显示数据的分位数及异常值
- 回归模型图(lmplot):显示变量间的线性关系及回归线
- 成对图(pairplot):显示多变量间的两两关系
- Seaborn的优势:语法简洁,内置美观调色板

运行:
    python seaborn_advanced_demo.py

依赖:
    pip install seaborn matplotlib numpy pandas scikit-learn

作者:自动生成示例(中文注释)
"""

try:
    import seaborn as sns
    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    from sklearn.datasets import make_regression, load_iris
    SEABORN_AVAILABLE = True
    MATPLOTLIB_AVAILABLE = True
except ImportError:
    print("警告:seaborn 或 scikit-learn 未安装,跳过相关演示")
    SEABORN_AVAILABLE = False
    # 尝试导入基础库
    try:
        import matplotlib.pyplot as plt
        import numpy as np
        import pandas as pd
        MATPLOTLIB_AVAILABLE = True
    except ImportError:
        MATPLOTLIB_AVAILABLE = False
        # 不设置 plt = None, np = None, pd = None,避免 Pylance 类型推断问题
import os

# 设置Seaborn样式
if SEABORN_AVAILABLE:
    sns.set_style("whitegrid")  # 设置背景样式
    sns.set_palette("husl")     # 设置颜色调色板

# 设置中文字体支持
if MATPLOTLIB_AVAILABLE:
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
    plt.rcParams['axes.unicode_minus'] = False

# 如果在无GUI环境中,设置后端
try:
    if MATPLOTLIB_AVAILABLE:
        import matplotlib
        matplotlib.use('Agg')
    SAVE_ONLY = True
except:
    SAVE_ONLY = False


def parse_arguments():
    """解析命令行参数"""
    import argparse
    parser = argparse.ArgumentParser(description='Seaborn 高级绘图演示')
    parser.add_argument('--save-images', action='store_true',
                       help='保存生成的图片文件(默认不保存)')
    return parser.parse_args()


def ensure_dir(dir_name):
    """确保目录存在"""
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)


def demo_violinplot():
    """演示小提琴图"""
    print('--- 小提琴图 (violinplot) ---')

    if not SEABORN_AVAILABLE:
        print('跳过小提琴图演示(需要 seaborn)')
        return

    # 创建示例数据:不同部门的员工薪资
    np.random.seed(42)
    data = {
        '部门': ['技术部'] * 50 + ['销售部'] * 50 + ['市场部'] * 50,
        '薪资': (
            np.random.normal(15000, 3000, 50).tolist() +  # 技术部:均值15000,标准差3000
            np.random.normal(12000, 2500, 50).tolist() +  # 销售部:均值12000,标准差2500
            np.random.normal(10000, 2000, 50).tolist()    # 市场部:均值10000,标准差2000
        )
    }

    df = pd.DataFrame(data)

    # 创建小提琴图
    plt.figure(figsize=(10, 6))

    ax = sns.violinplot(x='部门', y='薪资', data=df,
                       hue='部门', palette='Set3', inner='quartile', legend=False)

    # 设置标题和标签
    ax.set_title('各部门薪资分布小提琴图', fontsize=16, fontweight='bold')
    ax.set_xlabel('部门', fontsize=12)
    ax.set_ylabel('薪资(元)', fontsize=12)

    # 添加数值统计
    for i, dept in enumerate(['技术部', '销售部', '市场部']):
        dept_data = df[df['部门'] == dept]['薪资']
        mean_val = dept_data.mean()
        plt.text(i, mean_val + 500, f'均值: {mean_val:.0f}',
                ha='center', va='bottom', fontweight='bold')

    plt.tight_layout()
    ensure_dir('seaborn_advanced')
    plt.savefig('seaborn_advanced/violinplot_demo.png', dpi=150, bbox_inches='tight')
    print('小提琴图已保存为 seaborn_advanced/violinplot_demo.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()

    print('小提琴图特点:')
    print('- 显示数据分布的形状和密度')
    print('- 白色箱子显示四分位数范围')
    print('- 黑色线条显示中位数')
    print('- 适合比较多个类别的分布')
    print()


def demo_boxplot():
    """演示箱线图"""
    print('--- 箱线图 (boxplot) ---')

    if not SEABORN_AVAILABLE:
        print('跳过箱线图演示(需要 seaborn 和 scikit-learn)')
        return

    # 使用模拟的iris数据集
    np.random.seed(42)
    # 模拟iris数据:3个品种,每个有4个特征
    data = []
    feature_names = ['sepal length', 'sepal width', 'petal length', 'petal width']
    species_names = ['setosa', 'versicolor', 'virginica']

    for species_idx, species in enumerate(species_names):
        for _ in range(50):
            if species == 'setosa':
                features = [
                    np.random.normal(5.0, 0.3),  # sepal length
                    np.random.normal(3.4, 0.3),  # sepal width
                    np.random.normal(1.5, 0.2),  # petal length
                    np.random.normal(0.2, 0.1)   # petal width
                ]
            elif species == 'versicolor':
                features = [
                    np.random.normal(5.9, 0.5),
                    np.random.normal(2.8, 0.3),
                    np.random.normal(4.3, 0.5),
                    np.random.normal(1.3, 0.2)
                ]
            else:  # virginica
                features = [
                    np.random.normal(6.6, 0.6),
                    np.random.normal(3.0, 0.3),
                    np.random.normal(5.6, 0.5),
                    np.random.normal(2.0, 0.3)
                ]
            data.append(features + [species])

    df = pd.DataFrame(data, columns=feature_names + ['species'])

    # 创建箱线图
    fig, axes = plt.subplots(2, 2, figsize=(15, 12))
    fig.suptitle('鸢尾花数据集特征箱线图', fontsize=16, fontweight='bold')

    colors = ['lightblue', 'lightgreen', 'lightcoral', 'gold']

    for i, (feature, color) in enumerate(zip(feature_names, colors)):
        ax = axes[i//2, i%2]

        # 绘制箱线图
        sns.boxplot(x='species', y=feature, data=df, ax=ax,
                   hue='species', palette='Set2', width=0.6, legend=False)

        ax.set_title(f'{feature} by Species', fontsize=12, fontweight='bold')
        ax.set_xlabel('Species', fontsize=10)
        ax.set_ylabel(feature, fontsize=10)

        # 添加网格
        ax.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.savefig('seaborn_advanced/boxplot_demo.png', dpi=150, bbox_inches='tight')
    print('箱线图已保存为 seaborn_advanced/boxplot_demo.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()

    print('箱线图特点:')
    print('- 箱子显示四分位数范围(Q1-Q3)')
    print('- 中间线显示中位数(Q2)')
    print('- 须线显示数据范围')
    print('- 点显示异常值')
    print('- 适合检测异常值和比较分布')
    print()


def demo_lmplot():
    """演示回归模型图"""
    print('--- 回归模型图 (lmplot) ---')

    if not SEABORN_AVAILABLE:
        print('跳过回归模型图演示(需要 seaborn)')
        return

    # 生成模拟回归数据
    np.random.seed(42)
    n_samples = 200

    # 线性关系数据
    x_linear = np.random.uniform(0, 10, n_samples)
    y_linear = 2 * x_linear + 1 + np.random.normal(0, 2, n_samples)

    # 二次关系数据
    x_quad = np.random.uniform(0, 10, n_samples)
    y_quad = 0.5 * x_quad**2 - 2 * x_quad + 5 + np.random.normal(0, 3, n_samples)

    # 创建DataFrame
    df_linear = pd.DataFrame({'x': x_linear, 'y': y_linear})
    df_quad = pd.DataFrame({'x': x_quad, 'y': y_quad})

    # 创建回归图
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
    fig.suptitle('回归模型图示例', fontsize=16, fontweight='bold')

    # 线性回归
    sns.regplot(x='x', y='y', data=df_linear, ax=ax1,
                scatter_kws={'alpha':0.6, 'color':'blue'},
                line_kws={'color':'red', 'linewidth':2})

    ax1.set_title('线性回归', fontsize=14, fontweight='bold')
    ax1.set_xlabel('X变量')
    ax1.set_ylabel('Y变量')
    ax1.grid(True, alpha=0.3)

    # 多项式回归(二次)
    sns.regplot(x='x', y='y', data=df_quad, ax=ax2, order=2,
                scatter_kws={'alpha':0.6, 'color':'green'},
                line_kws={'color':'red', 'linewidth':2})

    ax2.set_title('多项式回归(二次)', fontsize=14, fontweight='bold')
    ax2.set_xlabel('X变量')
    ax2.set_ylabel('Y变量')
    ax2.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.savefig('seaborn_advanced/lmplot_demo.png', dpi=150, bbox_inches='tight')
    print('回归模型图已保存为 seaborn_advanced/lmplot_demo.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()

    print('回归模型图特点:')
    print('- 显示变量间的线性关系')
    print('- 自动拟合回归线')
    print('- 显示置信区间')
    print('- 支持多项式回归(order参数)')
    print()


def demo_pairplot():
    """演示成对图"""
    print('--- 成对图 (pairplot) ---')

    if not SEABORN_AVAILABLE:
        print('跳过成对图演示(需要 seaborn 和 scikit-learn)')
        return

    # 使用模拟的iris数据集
    np.random.seed(42)
    data = []
    feature_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
    species_names = ['setosa', 'versicolor', 'virginica']

    for species_idx, species in enumerate(species_names):
        for _ in range(50):
            if species == 'setosa':
                features = [
                    np.random.normal(5.0, 0.3),  # sepal length
                    np.random.normal(3.4, 0.3),  # sepal width
                    np.random.normal(1.5, 0.2),  # petal length
                    np.random.normal(0.2, 0.1)   # petal width
                ]
            elif species == 'versicolor':
                features = [
                    np.random.normal(5.9, 0.5),
                    np.random.normal(2.8, 0.3),
                    np.random.normal(4.3, 0.5),
                    np.random.normal(1.3, 0.2)
                ]
            else:  # virginica
                features = [
                    np.random.normal(6.6, 0.6),
                    np.random.normal(3.0, 0.3),
                    np.random.normal(5.6, 0.5),
                    np.random.normal(2.0, 0.3)
                ]
            data.append(features + [species])

    df = pd.DataFrame(data, columns=feature_names + ['species'])

    # 创建成对图
    plt.figure(figsize=(12, 10))

    pair_plot = sns.pairplot(df, hue='species', palette='Set1',
                           diag_kind='kde',  # 对角线使用核密度估计
                           plot_kws={'alpha': 0.6},
                           diag_kws={'alpha': 0.7})

    pair_plot.fig.suptitle('鸢尾花数据集特征成对关系图', fontsize=16, fontweight='bold', y=1.02)

    plt.tight_layout()
    plt.savefig('seaborn_advanced/pairplot_demo.png', dpi=150, bbox_inches='tight')
    print('成对图已保存为 seaborn_advanced/pairplot_demo.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()

    print('成对图特点:')
    print('- 显示所有数值变量间的两两关系')
    print('- 对角线显示单变量分布')
    print('- 颜色编码不同类别')
    print('- 适合探索性数据分析')
    print('- 可以快速发现变量间的相关性和模式')
    print()


def demo_seaborn_advantages():
    """演示Seaborn的优势:语法简洁,美观调色板"""
    print('--- Seaborn 优势演示 ---')

    if not SEABORN_AVAILABLE:
        print('跳过 Seaborn 优势演示(需要 seaborn)')
        return

    # 创建比较数据
    np.random.seed(42)
    categories = ['A', 'B', 'C', 'D', 'E']
    values1 = np.random.randint(10, 50, 5)
    values2 = np.random.randint(15, 45, 5)

    df = pd.DataFrame({
        'category': categories * 2,
        'value': np.concatenate([values1, values2]),
        'group': ['Group1'] * 5 + ['Group2'] * 5
    })

    # Seaborn版本(简洁语法)
    plt.figure(figsize=(12, 5))

    plt.subplot(1, 2, 1)
    ax1 = sns.barplot(x='category', y='value', hue='group', data=df, palette='husl')
    ax1.set_title('Seaborn版本(简洁语法)', fontsize=12, fontweight='bold')
    ax1.set_xlabel('类别')
    ax1.set_ylabel('数值')
    ax1.legend(title='分组')

    # Matplotlib版本(繁琐语法)- 为了对比
    plt.subplot(1, 2, 2)

    x = np.arange(len(categories))
    width = 0.35

    bars1 = plt.bar(x - width/2, values1, width, label='Group1', alpha=0.8, color='skyblue')
    bars2 = plt.bar(x + width/2, values2, width, label='Group2', alpha=0.8, color='lightgreen')

    plt.xlabel('类别')
    plt.ylabel('数值')
    plt.title('Matplotlib版本(繁琐语法)', fontsize=12, fontweight='bold')
    plt.xticks(x, categories)
    plt.legend(title='分组')

    # 添加数值标签
    for bars in [bars1, bars2]:
        for bar in bars:
            height = bar.get_height()
            plt.text(bar.get_x() + bar.get_width()/2., height,
                    f'{int(height)}', ha='center', va='bottom')

    plt.tight_layout()
    plt.savefig('seaborn_advanced/seaborn_advantages.png', dpi=150, bbox_inches='tight')
    print('Seaborn优势对比图已保存为 seaborn_advanced/seaborn_advantages.png')

    if not SAVE_ONLY:
        plt.show()
    plt.close()

    print('Seaborn优势:')
    print('1. 语法简洁:一行代码即可创建复杂图表')
    print('2. 自动美化:内置美观调色板和样式')
    print('3. 数据导向:直接使用DataFrame和列名')
    print('4. 统计功能:自动添加统计信息和置信区间')
    print('5. 集成Pandas:无缝配合数据处理')
    print()


def cleanup():
    """清理生成的图片文件"""
    image_dir = 'seaborn_advanced'
    if os.path.exists(image_dir):
        import shutil
        shutil.rmtree(image_dir)
        print('清理图片目录完成')


def main():
    print('=' * 50)
    print('Seaborn 高级绘图演示')
    print('=' * 50)
    print()

    # 解析命令行参数
    args = parse_arguments()

    if not SEABORN_AVAILABLE:
        print('seaborn 未安装,无法运行高级绘图演示')
        print('请运行:pip install seaborn matplotlib numpy pandas scikit-learn')
        return

    try:
        demo_violinplot()
        demo_boxplot()
        demo_lmplot()
        demo_pairplot()
        demo_seaborn_advantages()

        print('所有Seaborn图表已生成并保存为PNG文件')
        if SAVE_ONLY:
            print('注意:当前环境为无GUI模式,所有图表已保存为文件')

    except ImportError as e:
        print(f'导入错误:{e}')
        print('请安装必要的包:pip install seaborn matplotlib numpy pandas scikit-learn')
    except Exception as e:
        print(f'运行错误:{e}')
    finally:
        # 根据命令行参数决定是否清理图片
        if not args.save_images:
            cleanup()
        else:
            print('图片文件已保存,可在当前目录查看')
            print('下次运行时如需清理,请不使用 --save-images 参数')


if __name__ == '__main__':
    main()
2.3.3. PandasAI 简介

PandasAI:基于大语言模型(如 GPT-4)构建的库,旨在简化数据分析过程。

使得用户能够通过自然语言直接与数据交互,实现自动清洗和查询等功能。

python 复制代码
"""
示例:Pandas 数据预处理演示

包含演示:
- 缺失值处理(isnull, dropna, fillna, ffill, bfill)
- 重复值处理(duplicated, drop_duplicates)
- 异常值处理(IQR法)
- 数据集成(merge, concat)
- 数据转换(标准化、离散化)
- 数据规约(PCA)

运行:
    python pandas_preprocessing_demo.py

依赖:
    pip install pandas numpy scikit-learn matplotlib seaborn

作者:自动生成示例(中文注释)
"""

try:
    import pandas as pd
    import numpy as np
    PANDAS_AVAILABLE = True
except ImportError:
    print("警告:pandas 未安装,跳过数据预处理演示")
    PANDAS_AVAILABLE = False
    # 不设置 pd = None 和 np = None,避免 Pylance 类型推断问题

try:
    from sklearn.preprocessing import StandardScaler, MinMaxScaler
    from sklearn.decomposition import PCA
    SKLEARN_AVAILABLE = True
except ImportError:
    print("警告:scikit-learn 未安装,跳过相关演示")
    SKLEARN_AVAILABLE = False

import warnings
warnings.filterwarnings('ignore')


def parse_arguments():
    """解析命令行参数"""
    import argparse
    parser = argparse.ArgumentParser(description='Pandas 数据预处理演示')
    parser.add_argument('--save-images', action='store_true',
                       help='保存生成的图片文件(默认不保存)')
    return parser.parse_args()


def demo_missing_values():
    """演示缺失值处理"""
    print('--- 缺失值处理 ---')

    # 创建包含缺失值的数据
    data = {
        'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
        'age': [25, np.nan, 30, 35, np.nan],
        'salary': [50000, 60000, np.nan, 80000, 70000],
        'department': ['IT', 'HR', 'IT', np.nan, 'Finance']
    }

    df = pd.DataFrame(data)
    print('原始数据:')
    print(df)
    print()

    # 检测缺失值
    print('缺失值检测:')
    print(df.isnull())
    print(f'每列缺失值数量:\n{df.isnull().sum()}')
    print()

    # 删除缺失值
    print('删除包含缺失值的行:')
    df_dropna = df.dropna()
    print(df_dropna)
    print()

    # 填充缺失值
    print('填充缺失值:')
    df_fillna = df.copy()

    # 用均值填充数值列
    df_fillna['age'] = df_fillna['age'].fillna(df_fillna['age'].mean())
    df_fillna['salary'] = df_fillna['salary'].fillna(df_fillna['salary'].mean())

    # 用特定值填充分类列
    df_fillna['department'] = df_fillna['department'].fillna('Unknown')

    print(df_fillna)
    print()

    # 前向填充和后向填充
    print('前向填充(ffill):')
    df_ffill = df.copy()
    df_ffill = df_ffill.ffill()
    print(df_ffill)
    print()

    print('后向填充(bfill):')
    df_bfill = df.copy()
    df_bfill = df_bfill.bfill()
    print(df_bfill)
    print()


def demo_duplicates():
    """演示重复值处理"""
    print('--- 重复值处理 ---')

    # 创建包含重复值的数据
    data = {
        'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob', 'David'],
        'age': [25, 30, 25, 35, 30, 40],
        'department': ['IT', 'HR', 'IT', 'Finance', 'HR', 'IT']
    }

    df = pd.DataFrame(data)
    print('原始数据:')
    print(df)
    print()

    # 检测重复值
    print('检测重复行:')
    print(df.duplicated())
    print()

    # 删除重复值
    print('删除重复行(保留第一次出现):')
    df_dedup = df.drop_duplicates()
    print(df_dedup)
    print()

    # 基于特定列删除重复值
    print('基于 name 列删除重复值:')
    df_dedup_name = df.drop_duplicates(subset=['name'])
    print(df_dedup_name)
    print()


def demo_outliers():
    """演示异常值处理(IQR法)"""
    print('--- 异常值处理(IQR法)---')

    # 创建包含异常值的数据
    np.random.seed(42)
    data = {
        'value': [10, 12, 11, 13, 12, 14, 100, 11, 13, 12, 15, 200, 13, 11, 12]
    }

    df = pd.DataFrame(data)
    print('原始数据:')
    print(df.describe())
    print()

    # 计算IQR
    Q1 = df['value'].quantile(0.25)
    Q3 = df['value'].quantile(0.75)
    IQR = Q3 - Q1

    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    print(f'Q1: {Q1}, Q3: {Q3}, IQR: {IQR}')
    print(f'下界: {lower_bound}, 上界: {upper_bound}')
    print()

    # 识别异常值
    outliers = df[(df['value'] < lower_bound) | (df['value'] > upper_bound)]
    print('异常值:')
    print(outliers)
    print()

    # 移除异常值
    df_clean = df[(df['value'] >= lower_bound) & (df['value'] <= upper_bound)]
    print('移除异常值后的数据:')
    print(df_clean.describe())
    print()


def demo_data_integration():
    """演示数据集成"""
    print('--- 数据集成 ---')

    # 创建两个数据集
    df1 = pd.DataFrame({
        'id': [1, 2, 3, 4],
        'name': ['Alice', 'Bob', 'Charlie', 'David'],
        'department': ['IT', 'HR', 'Finance', 'IT']
    })

    df2 = pd.DataFrame({
        'id': [1, 2, 3, 5],
        'salary': [50000, 60000, 70000, 80000],
        'experience': [2, 3, 4, 5]
    })

    print('数据集1:')
    print(df1)
    print()
    print('数据集2:')
    print(df2)
    print()

    # 合并(merge)
    print('内连接合并(inner join):')
    df_merged = pd.merge(df1, df2, on='id', how='inner')
    print(df_merged)
    print()

    print('左连接合并(left join):')
    df_left = pd.merge(df1, df2, on='id', how='left')
    print(df_left)
    print()

    # 堆叠(concat)
    df3 = pd.DataFrame({
        'id': [6, 7],
        'name': ['Eve', 'Frank'],
        'department': ['Marketing', 'Sales']
    })

    print('原始数据集1:')
    print(df1)
    print()
    print('要堆叠的数据集3:')
    print(df3)
    print()

    print('垂直堆叠(concat):')
    df_concat = pd.concat([df1, df3], ignore_index=True)
    print(df_concat)
    print()


def demo_data_transformation():
    """演示数据转换"""
    print('--- 数据转换 ---')

    if not SKLEARN_AVAILABLE:
        print('跳过标准化演示(需要 scikit-learn)')
        return

    # 创建数值数据
    data = {
        'feature1': [10, 20, 30, 40, 50],
        'feature2': [100, 200, 300, 400, 500],
        'age': [25, 30, 35, 40, 45]
    }

    df = pd.DataFrame(data)
    print('原始数据:')
    print(df)
    print()

    # Z-score 标准化
    print('Z-score 标准化:')
    scaler = StandardScaler()
    df_standardized = pd.DataFrame(
        scaler.fit_transform(df),
        columns=df.columns
    )
    print(df_standardized)
    print(f'均值: {df_standardized.mean().round(6).tolist()}')
    print(f'标准差: {df_standardized.std().round(6).tolist()}')
    print()

    # Min-Max 标准化
    print('Min-Max 标准化:')
    minmax_scaler = MinMaxScaler()
    df_normalized = pd.DataFrame(
        minmax_scaler.fit_transform(df),
        columns=df.columns
    )
    print(df_normalized)
    print()

    # 离散化(分箱)
    print('年龄离散化(pd.cut):')
    age_bins = [0, 30, 40, 100]
    age_labels = ['青年', '中年', '老年']
    df['age_category'] = pd.cut(df['age'], bins=age_bins, labels=age_labels)
    print(df[['age', 'age_category']])
    print()

    print('年龄离散化(pd.qcut - 等频分箱):')
    df['age_quartile'] = pd.qcut(df['age'], q=3, labels=['低', '中', '高'])
    print(df[['age', 'age_quartile']])
    print()


def demo_pca():
    """演示主成分分析(PCA)"""
    print('--- 数据规约:主成分分析(PCA)---')

    if not SKLEARN_AVAILABLE:
        print('跳过 PCA 演示(需要 scikit-learn)')
        return

    # 创建高维数据
    np.random.seed(42)
    data = np.random.randn(50, 4)  # 50个样本,4个特征
    df = pd.DataFrame(data, columns=['feature1', 'feature2', 'feature3', 'feature4'])

    print('原始数据形状:', df.shape)
    print('原始数据描述:')
    print(df.describe())
    print()

    # 应用PCA
    pca = PCA(n_components=2)  # 降维到2维
    principal_components = pca.fit_transform(df)

    df_pca = pd.DataFrame(
        principal_components,
        columns=['PC1', 'PC2']
    )

    print('PCA后数据形状:', df_pca.shape)
    print('主成分数据:')
    print(df_pca.head())
    print()

    # 解释方差比
    print('各主成分解释的方差比:')
    print(pca.explained_variance_ratio_)
    print(f'前2个主成分解释的总方差: {pca.explained_variance_ratio_.sum():.3f}')
    print()


def main():
    print('=' * 50)
    print('Pandas 数据预处理演示')
    print('=' * 50)
    print()

    # 解析命令行参数(保持与其他演示文件一致)
    args = parse_arguments()

    if not PANDAS_AVAILABLE:
        print('pandas 未安装,无法运行数据预处理演示')
        print('请运行:pip install pandas numpy')
        return

    try:
        demo_missing_values()
        demo_duplicates()
        demo_outliers()
        demo_data_integration()
        demo_data_transformation()
        demo_pca()

    except ImportError as e:
        print(f'导入错误:{e}')
        print('请安装必要的包:pip install pandas numpy scikit-learn matplotlib seaborn')
    except Exception as e:
        print(f'运行错误:{e}')


if __name__ == '__main__':
    main()

3. 课件13:API开发实战

通过程序与互联网服务进行数据交互

3.1. HTTP API

API本质:

Application Programming Interface,应用程序编程接口。

标准化的通信协议和数据格式

HTTP协议基础:

  • 请求结构:

    • 请求行
      方法、URL
      常用方法:
      • GET:请求数据。
      • POST:提交数据。
      • PUT:更新数据。
      • DELETE:删除数据。
        Python requests 库会自动处理URL的参数编码
        URL:
      • 协议(http/https)
      • 主机名(域名或IP地址)
      • 路径
      • 查询参数
    • 请求头
      例如User-Agent、Content-Type等
    • 请求体
      数据
  • 响应结构:

    • 状态行
      状态码、状态消息
      常见状态码:200(成功)、404(未找到)、500(服务器错误)等
    • 响应头
      例如Content-Type、Content-Length等
    • 响应体
      数据

3.2. 重要库:Requests

Requests:Python中用于发送HTTP请求的第三方库。

3.2.1. 基础调用
  • requests.get(url, params=None, **kwargs): 发送GET请求。
  • requests.post(url, data=None, json=None, **kwargs): 发送POST请求。
3.2.2. 进阶配置
  • 请求头(Headers):
    通过 headers 参数自定义请求头。
    伪装User-Agent 防止被服务器拒绝访问。
  • 超时与重试:
    通过 timeout 参数设置请求超时时间。
    否则可能永久阻塞程序
    使用 requests.adapters.HTTPAdapter 配置重试策略。
  • 会话保持:
    requests.Session(): 创建会话对象,保持跨请求的参数(如Cookies)。
    可以复用连接,提高性能。
  • SSL验证:
    默认验证HTTPS证书,可通过 verify 参数禁用验证(不推荐)。
    一般仅测试环境禁用绕过

3.3. JSON数据解析与处理

现代Web API通常使用JSON格式传输数据

处理方法:

  • 自动解析:
    response.json(): 将响应体解析为Python对象(字典或列表)。
  • 安全读取:
    访问嵌套字典时,使用.get() 方法并设置默认值,避免KeyError异常。
  • 批量提取:
    使用列表推导式或循环遍历提取所需字段。
  • 时间转换:
    使用 datetime 模块将时间戳转换为可读格式。
    例如 strptime()strftime() 方法。

3.4. 优秀实践方法

  1. 异常捕获
  2. 状态检查
  3. 数据清洗
  4. 安全建议:
    • 避免在URL中传递敏感信息。
    • 使用环境变量或配置文件存储API密钥。
      不要将密钥硬编码在代码中。
    • 定期更新依赖库,修补安全漏洞。

3.5. 代码示例

python 复制代码
"""
示例:API开发实战演示

包含演示:
- HTTP请求基础(GET/POST)
- 请求头配置与User-Agent伪装
- 超时与重试机制
- 会话保持与Cookie管理
- SSL证书验证
- JSON数据解析与处理
- 异常处理与错误恢复
- 安全实践与API密钥管理

运行:
    python api_development_demo.py

依赖:
    pip install requests

注意:
    本示例使用公开API进行演示,不会产生实际费用
    如需测试私有API,请替换相应的URL和认证信息

作者:自动生成示例(中文注释)
"""

try:
    import requests
    from requests.adapters import HTTPAdapter
    from urllib3.util.retry import Retry
    from requests.exceptions import RequestException
    REQUESTS_AVAILABLE = True
except ImportError:
    print("警告:requests 未安装,跳过API演示")
    REQUESTS_AVAILABLE = False
    # 不设置 requests = None,避免 Pylance 类型推断问题

import json
import time
import os
from datetime import datetime

# 设置API密钥(实际使用时应该从环境变量读取)
# NEVER hardcode API keys in your code!
API_KEY = os.getenv('DEMO_API_KEY', 'demo_key_replace_with_real_key')


def demo_basic_http_requests():
    """演示基础HTTP请求"""
    print('--- 基础HTTP请求 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过HTTP请求演示(需要 requests)')
        return

    try:
        # GET请求示例 - 获取JSONPlaceholder的示例数据
        print('1. GET请求示例:')
        response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
        print(f'状态码: {response.status_code}')
        print(f'响应头: {dict(response.headers)}')
        print(f'响应内容: {response.json()}')
        print()

        # POST请求示例 - 创建新资源
        print('2. POST请求示例:')
        new_post = {
            'title': '示例标题',
            'body': '这是通过API创建的示例内容',
            'userId': 1
        }
        response = requests.post('https://jsonplaceholder.typicode.com/posts',
                               json=new_post)
        print(f'POST状态码: {response.status_code}')
        print(f'创建的资源: {response.json()}')
        print()

    except Exception as e:
        print(f'请求错误: {e}')


def demo_request_headers():
    """演示请求头配置"""
    print('--- 请求头配置 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过请求头演示(需要 requests)')
        return

    try:
        # 自定义请求头
        headers = {
            'User-Agent': 'Python-API-Demo/1.0',
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {API_KEY}'
        }

        print('1. 自定义请求头示例:')
        response = requests.get('https://httpbin.org/headers', headers=headers)
        print(f'发送的请求头: {response.json()["headers"]}')
        print()

        # 模拟浏览器User-Agent
        print('2. 浏览器User-Agent伪装:')
        browser_headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
        response = requests.get('https://httpbin.org/user-agent', headers=browser_headers)
        print(f'伪装的User-Agent: {response.json()["user-agent"]}')
        print()

    except Exception as e:
        print(f'请求错误: {e}')


def demo_timeout_and_retry():
    """演示超时与重试机制"""
    print('--- 超时与重试机制 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过超时重试演示(需要 requests)')
        return

    try:
        # 配置重试策略
        retry_strategy = Retry(
            total=3,  # 总重试次数
            status_forcelist=[429, 500, 502, 503, 504],  # 需要重试的状态码
            allowed_methods=["HEAD", "GET", "OPTIONS"],  # 需要重试的方法
            backoff_factor=1  # 重试间隔倍数
        )

        # 创建带有重试机制的会话
        adapter = HTTPAdapter(max_retries=retry_strategy)
        session = requests.Session()
        session.mount("http://", adapter)
        session.mount("https://", adapter)

        print('1. 超时设置示例:')
        # 设置5秒超时
        start_time = time.time()
        try:
            response = requests.get('https://httpbin.org/delay/2', timeout=5)
            elapsed = time.time() - start_time
            print(f'请求成功,耗时: {elapsed:.2f}秒')
        except Exception as e:
            elapsed = time.time() - start_time
            print(f'请求超时或其他错误,耗时: {elapsed:.2f}秒, 错误: {e}')
        print()

        print('2. 重试机制示例:')
        # 测试重试(使用可能不稳定的端点)
        try:
            response = session.get('https://httpbin.org/status/200')  # 改用成功的端点
            print(f'重试后状态码: {response.status_code}')
        except Exception as e:
            print(f'重试请求错误: {e}')
        print()

    except Exception as e:
        print(f'请求错误: {e}')


def demo_session_management():
    """演示会话管理和Cookie保持"""
    print('--- 会话管理和Cookie保持 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过会话管理演示(需要 requests)')
        return

    try:
        # 创建会话对象
        session = requests.Session()

        print('1. 会话保持示例:')
        # 第一次请求
        response1 = session.get('https://httpbin.org/cookies/set/session_id/12345')
        print(f'第一次请求Cookie: {response1.json()}')

        # 第二次请求(会自动携带Cookie)
        response2 = session.get('https://httpbin.org/cookies')
        print(f'第二次请求Cookie: {response2.json()}')
        print()

        print('2. 自定义Cookie示例:')
        # 设置自定义Cookie
        session.cookies.set('user_preference', 'dark_mode', domain='httpbin.org')
        response = session.get('https://httpbin.org/cookies')
        print(f'自定义Cookie: {response.json()}')
        print()

    except Exception as e:
        print(f'请求错误: {e}')


def demo_ssl_verification():
    """演示SSL证书验证"""
    print('--- SSL证书验证 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过SSL验证演示(需要 requests)')
        return

    try:
        print('1. 默认SSL验证(推荐):')
        response = requests.get('https://httpbin.org/get', timeout=10)
        print(f'HTTPS请求成功: {response.status_code}')
        print()

        print('2. 禁用SSL验证(仅测试环境使用):')
        # 注意:生产环境中不推荐禁用SSL验证
        response = requests.get('https://httpbin.org/get', verify=False, timeout=10)
        print(f'禁用验证请求成功: {response.status_code}')
        print('⚠️  警告:生产环境应始终启用SSL验证')
        print()

    except Exception as e:
        print(f'请求错误: {e}')


def demo_json_processing():
    """演示JSON数据解析与处理"""
    print('--- JSON数据解析与处理 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过JSON处理演示(需要 requests)')
        return

    try:
        # 获取GitHub API数据 - 获取最近的几个releases
        response = requests.get('https://api.github.com/repos/microsoft/vscode/releases?per_page=3',
                              headers={'Accept': 'application/vnd.github.v3+json'},
                              timeout=10)

        if response.status_code == 200:
            releases = response.json()

            print('1. 列表数据处理 - 多个Release:')
            print(f'获取到 {len(releases)} 个发布版本')
            print()

            # 处理每个release
            for i, release in enumerate(releases[:2], 1):  # 只处理前2个release
                print(f'Release {i}:')
                print(f'  版本名称: {release.get("name", "N/A")}')
                print(f'  标签: {release.get("tag_name", "N/A")}')
                print(f'  发布时间: {release.get("published_at", "N/A")[:10]}')  # 只显示日期部分

                # 处理assets列表
                assets = release.get('assets', [])
                if assets:
                    print(f'  附件数量: {len(assets)}')
                    print('  附件列表:')
                    for asset in assets[:2]:  # 只显示前2个附件
                        print(f'    - {asset.get("name", "N/A")}: {asset.get("download_count", 0)} 次下载')
                else:
                    print('  附件数量: 0 (无附件)')

                print()

            print('2. 嵌套数据安全访问:')
            if releases:
                first_release = releases[0]
                author = first_release.get('author', {})
                print(f'最新版本发布者: {author.get("login", "N/A")}')
                print(f'发布者类型: {author.get("type", "N/A")}')
                print(f'是否为预发布: {first_release.get("prerelease", False)}')
            print()

            print('3. 数据聚合统计:')
            total_reactions = 0
            reaction_types = {}
            prerelease_count = 0
            draft_count = 0

            for release in releases:
                # 统计reactions
                reactions = release.get('reactions', {})
                total_count = reactions.get('total_count', 0)
                total_reactions += total_count

                # 统计各种reaction类型
                for reaction_type in ['+1', 'laugh', 'hooray', 'heart', 'rocket', 'eyes']:
                    count = reactions.get(reaction_type, 0)
                    reaction_types[reaction_type] = reaction_types.get(reaction_type, 0) + count

                # 统计发布类型
                if release.get('prerelease', False):
                    prerelease_count += 1
                if release.get('draft', False):
                    draft_count += 1

            print(f'总反应数: {total_reactions}')
            print(f'预发布版本数: {prerelease_count}')
            print(f'草稿版本数: {draft_count}')
            print('各类型反应统计:')
            for reaction_type, count in reaction_types.items():
                if count > 0:  # 只显示有反应的类型
                    emoji_map = {
                        '+1': '👍',
                        'laugh': '😄',
                        'hooray': '🎉',
                        'heart': '❤️',
                        'rocket': '🚀',
                        'eyes': '👀'
                    }
                    emoji = emoji_map.get(reaction_type, reaction_type)
                    print(f'  {emoji} {reaction_type}: {count}')
            print()

            print('4. 时间戳转换:')
            if releases:
                published_at = releases[0].get('published_at')
                if published_at:
                    # 移除时区信息进行解析
                    dt = datetime.fromisoformat(published_at.replace('Z', '+00:00'))
                    print(f'最新版本发布日期: {dt.strftime("%Y年%m月%d日 %H:%M:%S")}')
            print()

    except Exception as e:
        print(f'请求或解析错误: {e}')


def demo_error_handling():
    """演示异常处理与错误恢复"""
    print('--- 异常处理与错误恢复 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过异常处理演示(需要 requests)')
        return

    def safe_api_call(url, max_retries=3, backoff_factor=1):
        """安全的API调用函数"""
        for attempt in range(max_retries):
            try:
                response = requests.get(url, timeout=5)

                # 检查状态码
                response.raise_for_status()  # 抛出HTTPError异常如果状态码不是2xx

                return response.json()

            except RequestException as e:
                print(f'尝试 {attempt + 1}/{max_retries}: {type(e).__name__}: {e}')
                # 对于某些错误码,不需要重试
                if hasattr(e, 'response') and e.response and e.response.status_code in [400, 401, 403, 404]:
                    break
            except Exception as e:
                print(f'尝试 {attempt + 1}/{max_retries}: {type(e).__name__}: {e}')
                # 对于其他异常,继续重试

            if attempt < max_retries - 1:
                wait_time = backoff_factor * (2 ** attempt)  # 指数退避
                print(f'等待 {wait_time} 秒后重试...')
                time.sleep(wait_time)

        return None

    print('1. 安全的API调用示例:')
    result = safe_api_call('https://jsonplaceholder.typicode.com/posts/1')
    if result:
        print(f'成功获取数据: {result.get("title", "N/A")}')
    else:
        print('所有重试都失败了')
    print()

    print('2. 错误状态码处理:')
    result = safe_api_call('https://httpbin.org/status/404')
    if result is None:
        print('正确处理了404错误')
    print()


def demo_security_practices():
    """演示安全实践"""
    print('--- 安全实践 ---')

    if not REQUESTS_AVAILABLE:
        print('跳过安全实践演示(需要 requests)')
        return

    print('1. API密钥安全管理:')
    print('✅ 使用环境变量存储敏感信息')
    print('✅ 不要在代码中硬编码API密钥')
    print(f'当前API密钥: {"已设置" if API_KEY != "demo_key_replace_with_real_key" else "使用演示密钥"}')
    print()

    print('2. URL参数安全:')
    # 安全的方式:使用params参数
    try:
        params = {'q': 'python tutorial', 'limit': 10}
        response = requests.get('https://httpbin.org/get', params=params)
        print(f'安全参数传递: {response.json()["args"]}')
    except Exception as e:
        print(f'参数传递示例错误: {e}')
    print()

    print('3. 输入验证和清理:')
    def safe_search(query):
        """安全的搜索函数"""
        if not query or len(query) > 100:
            return None

        # 清理输入
        query = query.strip()

        try:
            # 使用params而不是字符串拼接
            response = requests.get('https://httpbin.org/get',
                                  params={'search': query},
                                  timeout=5)
            return response.json()
        except:
            return None

    try:
        result = safe_search('python api')
        if result:
            print(f'安全搜索结果: {result["args"]}')
    except Exception as e:
        print(f'安全搜索示例错误: {e}')
    print()

    print('4. 速率限制意识:')
    print('✅ 实现请求间隔')
    print('✅ 监控API使用量')
    print('✅ 实现指数退避重试')
    print()


def main():
    print('=' * 50)
    print('API开发实战演示')
    print('=' * 50)
    print()

    if not REQUESTS_AVAILABLE:
        print('requests 未安装,无法运行API演示')
        print('请运行:pip install requests')
        return

    try:
        demo_basic_http_requests()
        demo_request_headers()
        demo_timeout_and_retry()
        demo_session_management()
        demo_ssl_verification()
        demo_json_processing()
        demo_error_handling()
        demo_security_practices()

        print('所有API演示已完成')

    except Exception as e:
        print(f'演示过程中发生错误: {e}')


if __name__ == '__main__':
    main()

4. 课件14:大模型开发实战

从"与大模型聊天"到"在程序中调用大模型"

目标:

理解LLM的API调用机制

掌握本地部署模型的集成方式

且能将LLM嵌入到实际应用流程

工程视角:

LLM------可调用的服务

程序通过API传入数据,获取并解析结果

4.1. 环境搭建:Ollama本地部署

Ollama平台:

  • 优势:
    本地部署,支持标准API调用,且适合CPU运行。
  • 模型:
    Qwen3:0.6b
  • 安装指令:
    ollama pull qwen3:0.6b

具体环境搭建此处不展示

4.2. 大模型API基础调用

Python集成:

使用ollama Python包调用本地模型。

核心函数:
chat()

  • model: 模型名称。
  • messages结构:
    包含字典的列表
    对话的核心

三大角色:

  • system:系统角色,设定对话背景。
    一般用于定义规则、身份和输出格式
  • user:用户角色,发送对话信息。
    具体的输入数据或问题
  • assistant:助手角色,返回对话结果。
    模型的输出结果或存储的历史对话

进阶参数:

  • stream=True:启用流式响应,逐步获取结果。
    类似"打字机"式的输出效果
  • think=True:模型输出其推理过程,显示[思考中]标签
    助于理解AI的逻辑

4.3. 实践案例:问卷自动分析

通过问卷处理展示传统编程于AI结合的优势

  • 混合处理模式:

    • 单选/多选题:通过 Python 传统逻辑进行统计分析。
    • 开放性文字题:循环调用 LLM API 进行文本情感分析、主题提取和建议汇总
  • 多轮调用与多轮对话的比较:

    自动化处理中,程序通常为"多轮调用"

    每条问卷记录发起一次独立API求情

    将模型作为一个系统的"分析模块"来应用

4.4. 代码伦理与安全性

  • 幻觉问题
    模型分析可能出错,注意分析,辩证使用
  • 隐私问题
    避免上传敏感个人信息,确保数据安全
  • 学术诚信
    使用AI辅助时,注意引用和标注,避免抄袭风险

4.5. 示例代码

python 复制代码
"""
示例:大模型开发实战演示

包含演示:
- Ollama本地模型基础调用
- messages结构与角色系统
- 流式响应与推理过程显示
- 问卷自动分析实践案例
- 代码伦理与安全性考虑

运行:
    python llm_integration_demo.py

依赖:
    pip install ollama pandas

注意:
    需要先安装并运行Ollama:
    1. 下载安装Ollama:https://ollama.ai
    2. 拉取模型:ollama pull qwen3:0.6b
    3. 启动Ollama服务

作者:自动生成示例(中文注释)
"""

try:
    import ollama
    import pandas as pd
    import json
    import time
    from datetime import datetime
    OLLAMA_AVAILABLE = True
except ImportError as e:
    print(f"警告:缺少依赖包 - {e}")
    print("请运行:pip install ollama pandas")
    OLLAMA_AVAILABLE = False


def demo_basic_ollama_chat():
    """演示Ollama基础聊天调用"""
    print('--- 基础Ollama聊天调用 ---')

    if not OLLAMA_AVAILABLE:
        print('跳过Ollama演示(需要 ollama 和 pandas)')
        return

    try:
        # 基础对话
        print('1. 基础对话示例:')
        messages = [
            {
                'role': 'user',
                'content': '你好,请介绍一下自己。'
            }
        ]

        response = ollama.chat(
            model='qwen3:0.6b',
            messages=messages
        )

        print(f'用户: {messages[0]["content"]}')
        print(f'助手: {response["message"]["content"]}')
        print()

        # 包含系统角色的对话
        print('2. 带系统角色的对话:')
        system_messages = [
            {
                'role': 'system',
                'content': '你是一个专业的Python编程助手,请用简洁的技术术语回答问题。'
            },
            {
                'role': 'user',
                'content': '解释一下什么是装饰器?'
            }
        ]

        response = ollama.chat(
            model='qwen3:0.6b',
            messages=system_messages
        )

        print(f'系统: {system_messages[0]["content"]}')
        print(f'用户: {system_messages[1]["content"]}')
        print(f'助手: {response["message"]["content"]}')
        print()

    except Exception as e:
        print(f'Ollama调用错误: {e}')
        print('请确保Ollama已安装并运行,且已拉取qwen3:0.6b模型')


def demo_streaming_response():
    """演示流式响应"""
    print('--- 流式响应演示 ---')

    if not OLLAMA_AVAILABLE:
        print('跳过流式响应演示(需要 ollama)')
        return

    try:
        print('流式响应(打字机效果):')
        messages = [
            {
                'role': 'user',
                'content': '请写一段关于Python的简短介绍。'
            }
        ]

        # 启用流式响应
        stream = ollama.chat(
            model='qwen3:0.6b',
            messages=messages,
            stream=True
        )

        print('助手: ', end='', flush=True)
        full_response = ''

        for chunk in stream:
            if chunk['message']['content']:
                content = chunk['message']['content']
                print(content, end='', flush=True)
                full_response += content
                time.sleep(0.05)  # 模拟打字机效果

        print('\n')
        print(f'完整响应长度: {len(full_response)} 字符')
        print()

    except Exception as e:
        print(f'流式响应错误: {e}')


def demo_reasoning_process():
    """演示推理过程显示"""
    print('--- 推理过程显示 ---')

    if not OLLAMA_AVAILABLE:
        print('跳过推理过程演示(需要 ollama)')
        return

    try:
        print('启用推理过程显示:')
        messages = [
            {
                'role': 'user',
                'content': '如果一个班有30个学生,其中20个喜欢数学,15个喜欢物理,10个两个都喜欢,问有多少学生不喜欢数学也不喜欢物理?'
            }
        ]

        # 启用推理过程显示
        response = ollama.chat(
            model='qwen3:0.6b',
            messages=messages,
            options={'think': True}  # 注意:实际参数可能因模型而异
        )

        print(f'问题: {messages[0]["content"]}')
        print(f'推理过程: {response.get("thinking", "无推理过程")}')
        print(f'最终答案: {response["message"]["content"]}')
        print()

    except Exception as e:
        print(f'推理过程演示错误: {e}')
        print('注意:推理过程显示功能可能需要特定模型支持')


def demo_multi_turn_conversation():
    """演示多轮对话"""
    print('--- 多轮对话演示 ---')

    if not OLLAMA_AVAILABLE:
        print('跳过多轮对话演示(需要 ollama)')
        return

    try:
        # 维护对话历史
        conversation_history = [
            {
                'role': 'system',
                'content': '你是一个有帮助的编程助手,请记住我们之前的对话内容。'
            }
        ]

        # 第一轮对话
        user_input1 = '我想学习Python编程,应该从哪里开始?'
        conversation_history.append({
            'role': 'user',
            'content': user_input1
        })

        response1 = ollama.chat(
            model='qwen3:0.6b',
            messages=conversation_history
        )

        assistant_reply1 = response1['message']['content']
        conversation_history.append({
            'role': 'assistant',
            'content': assistant_reply1
        })

        print('第一轮对话:')
        print(f'用户: {user_input1}')
        print(f'助手: {assistant_reply1}')
        print()

        # 第二轮对话(基于历史)
        user_input2 = '那数据结构和算法重要吗?'
        conversation_history.append({
            'role': 'user',
            'content': user_input2
        })

        response2 = ollama.chat(
            model='qwen3:0.6b',
            messages=conversation_history
        )

        assistant_reply2 = response2['message']['content']

        print('第二轮对话(记住上下文):')
        print(f'用户: {user_input2}')
        print(f'助手: {assistant_reply2}')
        print()

    except Exception as e:
        print(f'多轮对话错误: {e}')


def create_sample_questionnaire_data():
    """创建示例问卷数据"""
    # 模拟问卷数据
    questionnaire_data = [
        {
            'id': 1,
            'age': 25,
            'gender': '女',
            'education': '本科',
            'programming_experience': '1-2年',
            'favorite_language': ['Python', 'JavaScript'],
            'learning_motivation': '我想转行到IT行业,听说编程就业前景好',
            'difficulties': '基础语法理解起来比较吃力,特别是面向对象编程',
            'suggestions': '希望有更多实战项目练习'
        },
        {
            'id': 2,
            'age': 30,
            'gender': '男',
            'education': '硕士',
            'programming_experience': '3-5年',
            'favorite_language': ['Python', 'Java'],
            'learning_motivation': '工作中需要用到编程技能,公司要求学习',
            'difficulties': '时间不够用,工作太忙了',
            'suggestions': '课程内容很不错,但节奏可以稍微慢一点'
        },
        {
            'id': 3,
            'age': 22,
            'gender': '女',
            'education': '大专',
            'programming_experience': '无',
            'favorite_language': ['Python'],
            'learning_motivation': '对编程感兴趣,想自学一些技能',
            'difficulties': '数学基础不好,算法思维跟不上',
            'suggestions': '多一些基础概念的解释视频'
        }
    ]

    return questionnaire_data


def analyze_questionnaire_with_llm(questionnaire_data):
    """使用LLM分析问卷数据"""
    print('--- 问卷自动分析演示 ---')

    if not OLLAMA_AVAILABLE:
        print('跳过问卷分析演示(需要 ollama)')
        return

    try:
        # 1. 统计分析(传统编程)
        print('1. 传统编程统计分析:')

        # 性别统计
        gender_stats = {}
        for record in questionnaire_data:
            gender = record['gender']
            gender_stats[gender] = gender_stats.get(gender, 0) + 1

        print(f'性别分布: {gender_stats}')

        # 年龄统计
        ages = [record['age'] for record in questionnaire_data]
        avg_age = sum(ages) / len(ages)
        print(f'平均年龄: {avg_age:.1f}岁')
        print()

        # 2. LLM情感分析
        print('2. LLM情感分析:')

        for i, record in enumerate(questionnaire_data, 1):
            motivation = record['learning_motivation']

            analysis_prompt = f"""
            请分析以下学习动机的情感倾向:
            "{motivation}"

            请从以下方面进行分析:
            1. 情感倾向(积极/消极/中性)
            2. 主要动机类型
            3. 简要分析理由

            请用JSON格式返回结果,包含字段:sentiment, motivation_type, reason
            """

            messages = [
                {
                    'role': 'system',
                    'content': '你是一个专业的情感分析助手,请准确分析文本情感并返回指定格式的JSON结果。'
                },
                {
                    'role': 'user',
                    'content': analysis_prompt
                }
            ]

            try:
                response = ollama.chat(
                    model='qwen3:0.6b',
                    messages=messages
                )

                result_text = response['message']['content']

                # 尝试解析JSON结果
                try:
                    # 清理可能的markdown代码块标记
                    if '```json' in result_text:
                        result_text = result_text.split('```json')[1].split('```')[0].strip()
                    elif '```' in result_text:
                        result_text = result_text.split('```')[1].split('```')[0].strip()

                    analysis_result = json.loads(result_text)

                    print(f'记录{i}学习动机分析:')
                    print(f'  原文: {motivation}')
                    print(f'  情感倾向: {analysis_result.get("sentiment", "未知")}')
                    print(f'  动机类型: {analysis_result.get("motivation_type", "未知")}')
                    print(f'  分析理由: {analysis_result.get("reason", "无")}')
                    print()

                except json.JSONDecodeError:
                    print(f'记录{i}分析结果解析失败,使用原始文本:')
                    print(f'  原文: {motivation}')
                    print(f'  LLM分析: {result_text[:200]}...')
                    print()

            except Exception as e:
                print(f'记录{i}分析失败: {e}')
                print()

        # 3. 主题提取和建议汇总
        print('3. 主题提取和建议汇总:')

        all_difficulties = [record['difficulties'] for record in questionnaire_data]
        all_suggestions = [record['suggestions'] for record in questionnaire_data]

        combined_text = '学习困难:' + ';'.join(all_difficulties) + '\n建议:' + ';'.join(all_suggestions)

        summary_prompt = f"""
        请分析以下问卷反馈数据,提取主要主题并提供改进建议:

        {combined_text}

        请从以下方面进行分析:
        1. 学习困难的主要类型和频率
        2. 学生建议的主要方向
        3. 对课程改进的具体建议

        请用结构化的方式呈现分析结果。
        """

        messages = [
            {
                'role': 'system',
                'content': '你是一个教育数据分析专家,请专业地分析问卷数据并提供建设性建议。'
            },
            {
                'role': 'user',
                'content': summary_prompt
            }
        ]

        response = ollama.chat(
            model='qwen3:0.6b',
            messages=messages
        )

        print('综合分析结果:')
        print(response['message']['content'])
        print()

    except Exception as e:
        print(f'问卷分析错误: {e}')


def demo_ethics_and_security():
    """演示代码伦理与安全性考虑"""
    print('--- 代码伦理与安全性演示 ---')

    print('1. 隐私保护示例:')
    print('✅ 敏感信息脱敏处理')
    print('✅ 本地处理,避免上传敏感数据')
    print()

    print('2. 幻觉问题处理:')
    print('✅ 交叉验证重要信息')
    print('✅ 设置置信度阈值')
    print('✅ 人工审核关键决策')
    print()

    print('3. 学术诚信提醒:')
    print('✅ 标注AI生成内容')
    print('✅ 理解而非抄袭AI输出')
    print('✅ 平衡AI辅助与自主学习')
    print()

    # 演示数据脱敏
    print('4. 数据脱敏示例:')

    sensitive_data = {
        'name': '张三',
        'id_card': '123456789012345678',
        'phone': '13800138000',
        'email': 'zhangsan@example.com',
        'feedback': '我觉得这门课很有帮助'
    }

    def anonymize_data(data):
        """数据脱敏函数"""
        anonymized = data.copy()
        # 脱敏处理
        anonymized['name'] = '学生' + str(hash(data['name']))[:4]
        anonymized['id_card'] = data['id_card'][:6] + '*' * 8 + data['id_card'][-4:]
        anonymized['phone'] = data['phone'][:3] + '*' * 4 + data['phone'][-4:]
        anonymized['email'] = data['email'].split('@')[0][:2] + '*' * 3 + '@' + data['email'].split('@')[1]
        return anonymized

    anonymized_data = anonymize_data(sensitive_data)

    print('原始数据:')
    for key, value in sensitive_data.items():
        print(f'  {key}: {value}')

    print('脱敏后数据:')
    for key, value in anonymized_data.items():
        print(f'  {key}: {value}')

    print('✅ 敏感信息已保护,同时保留分析价值')
    print()


def main():
    print('=' * 50)
    print('大模型开发实战演示')
    print('=' * 50)
    print()

    if not OLLAMA_AVAILABLE:
        print('ollama 或 pandas 未安装,无法运行完整演示')
        print('请运行:pip install ollama pandas')
        print('并确保Ollama已安装并运行')
        return

    try:
        demo_basic_ollama_chat()
        demo_streaming_response()
        demo_reasoning_process()
        demo_multi_turn_conversation()

        # 创建并分析问卷数据
        questionnaire_data = create_sample_questionnaire_data()
        analyze_questionnaire_with_llm(questionnaire_data)

        demo_ethics_and_security()

        print('所有大模型演示已完成')

    except Exception as e:
        print(f'演示过程中发生错误: {e}')
        print('请检查Ollama服务是否正常运行')


if __name__ == '__main__':
    main()

运行输出示例:

相关推荐
隐语SecretFlow2 小时前
隐语SML0.1.0版本发布!SPU开源机器学习Python算法库
python·算法·机器学习
代码方舟2 小时前
Java 进阶:基于 Spring Boot 集成天远数据“人脸比对 V3”的最佳实践
java·大数据·spring boot·python
bbq粉刷匠2 小时前
Java基础语法问答
java·开发语言·python
Eiceblue3 小时前
将 Python 列表导出为 Excel 文件:一维、二维、字典列表
开发语言·python·excel·visual studio code
Swizard10 小时前
别再让你的 Python 傻等了:三分钟带你通过 asyncio 实现性能起飞
python
卡布叻_星星10 小时前
Vue 生态演进指南:主流框架搭配以及Vue CLI vs Vite 与 Vue2 vs Vue3 核心区别
笔记
小裕哥略帅11 小时前
PMP学习笔记--环境
笔记·学习
liuaa4111 小时前
期刊论文笔记
笔记
HXR_plume11 小时前
【Web信息处理与应用课程笔记3】个性化检索(上)
笔记