{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d5d1cdb4",
   "metadata": {},
   "source": [
    "# 第 5 讲｜Python 组合数据类型：列表、元组、字典与集合\n",
    "\n",
    "本课聚焦 Python 的列表、元组、集合和字典。每个知识点都按照“概念 → 示例代码 →\n",
    "代码解读 → 动手练习”展开，最后再把这些知识迁移到名仕、科图和青娅三套真题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11bd1b72",
   "metadata": {},
   "source": [
    "## 训练路线\n",
    "\n",
    "| 阶段 | 学习内容 | 能解决的问题 |\n",
    "|---|---|---|\n",
    "| 一 | 列表及常用操作 | 保存有顺序的数据、收集多页记录 |\n",
    "| 二 | 元组与集合 | 表示固定组合、去除重复值 |\n",
    "| 三 | 字典与嵌套结构 | 表示一条记录、请求参数和 JSON 响应 |\n",
    "| 四 | 真题迁移 | 读懂列名列表、规则字典和嵌套接口数据 |\n",
    "\n",
    "### 三套真题的背景\n",
    "\n",
    "- **名仕**：从多个网店分页获取商品记录，再汇总实际销量、销售总额和平均价格；\n",
    "- **科图**：从订单表选择规定字段，按年度和区域分析付款方式与客户特征；\n",
    "- **青娅**：从现金流量表选择指定项目和年度数据，再保存结果。\n",
    "\n",
    "三套题都使用 pandas，但其中承载数据关系的仍是本课要学习的列表和字典。\n",
    "本课先理解这些 Python 结构为什么这样写；pandas 的分组、筛选和聚合在后续专题继续学习。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb59fc89",
   "metadata": {},
   "source": [
    "## 教材内容取舍\n",
    "\n",
    "5f 教材完整介绍了列表、元组、集合、字典及其大量方法。结合三套真题，本课优先掌握：\n",
    "\n",
    "- 列表的创建、访问、遍历、追加、扩展、成员判断和基础排序；\n",
    "- 元组的固定结构与解包；\n",
    "- 集合的去重特点；\n",
    "- 字典的访问、修改、遍历和嵌套。\n",
    "\n",
    "insert、remove、pop、clear、复杂集合操作以及列表/集合/字典推导式暂列为课后拓展，\n",
    "避免主线被低频方法打断。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f74836f",
   "metadata": {},
   "source": [
    "## 1. 四种组合数据类型的特点\n",
    "\n",
    "**学习目标：** 能根据数据关系选择列表、元组、集合或字典。\n",
    "\n",
    "- 列表 list：有顺序、可修改、允许重复；\n",
    "- 元组 tuple：有顺序、创建后不能修改；\n",
    "- 集合 set：元素不重复，不依赖位置顺序；\n",
    "- 字典 dict：通过键找到对应的值。\n",
    "\n",
    "`type(对象)` 用于查看对象的数据类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4c49c87",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e90c2c9e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.725421Z",
     "iopub.status.busy": "2026-07-29T17:23:14.725049Z",
     "iopub.status.idle": "2026-07-29T17:23:14.736511Z",
     "shell.execute_reply": "2026-07-29T17:23:14.736005Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['年', '区域', '付款方式'] <class 'list'>\n",
      "(2024, '华东') <class 'tuple'>\n",
      "{'直播', '搜索'} <class 'set'>\n",
      "{'商品名称': '双肩包', '销量': 125} <class 'dict'>\n"
     ]
    }
   ],
   "source": [
    "columns = [\"年\", \"区域\", \"付款方式\"]\n",
    "group_key = (2024, \"华东\")\n",
    "channels = {\"直播\", \"搜索\"}\n",
    "product = {\"商品名称\": \"双肩包\", \"销量\": 125}\n",
    "\n",
    "print(columns, type(columns))\n",
    "print(group_key, type(group_key))\n",
    "print(channels, type(channels))\n",
    "print(product, type(product))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3a323ad",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "columns 需要保持字段顺序，因此使用列表；group_key 表示固定的“年份与区域”组合，\n",
    "因此可使用元组；channels 只关心不重复的渠道，因此使用集合；product 需要通过\n",
    "“商品名称”“销量”等键查找值，因此使用字典。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2f7d106",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7938bbf7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.738818Z",
     "iopub.status.busy": "2026-07-29T17:23:14.738596Z",
     "iopub.status.idle": "2026-07-29T17:23:14.741018Z",
     "shell.execute_reply": "2026-07-29T17:23:14.740558Z"
    }
   },
   "outputs": [],
   "source": [
    "# 目标：根据关系创建四种组合数据。\n",
    "# columns =                    # 列表：年、区域、付款方式\n",
    "# group_key =                  # 元组：2024、华东\n",
    "# channels =                   # 集合：直播、搜索\n",
    "# product =                    # 字典：商品名称为双肩包，销量为125\n",
    "# print(type(columns))\n",
    "# print(type(group_key))\n",
    "# print(type(channels))\n",
    "# print(type(product))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bf540dc",
   "metadata": {},
   "source": [
    "## 2. 列表：创建、索引、切片与遍历\n",
    "\n",
    "**学习目标：**创建有顺序的数据，并按位置读取一个或多个元素。\n",
    "\n",
    "列表使用中括号创建。索引从 0 开始，-1 表示最后一个元素；切片的右边界不包含在结果中。\n",
    "len(列表) 返回元素个数，for 循环可以按顺序逐个访问元素。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74a2e36b",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0ce00b03",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.743418Z",
     "iopub.status.busy": "2026-07-29T17:23:14.743228Z",
     "iopub.status.idle": "2026-07-29T17:23:14.746777Z",
     "shell.execute_reply": "2026-07-29T17:23:14.746278Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "字段数量： 6\n",
      "第一个字段： 年度\n",
      "最后一个字段： 售价\n",
      "索引 2 到 4： ['网店名称', '销量', '退单量']\n",
      "将处理： 年度\n",
      "将处理： 季度\n",
      "将处理： 网店名称\n",
      "将处理： 销量\n",
      "将处理： 退单量\n",
      "将处理： 售价\n"
     ]
    }
   ],
   "source": [
    "columns = [\"年度\", \"季度\", \"网店名称\", \"销量\", \"退单量\", \"售价\"]\n",
    "\n",
    "print(\"字段数量：\", len(columns))\n",
    "print(\"第一个字段：\", columns[0])\n",
    "print(\"最后一个字段：\", columns[-1])\n",
    "print(\"索引 2 到 4：\", columns[2:5])\n",
    "\n",
    "for column in columns:\n",
    "    print(\"将处理：\", column)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba3a6107",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "len(columns) 得到列表长度 6；columns[0] 与 columns[-1] 分别读取首尾元素；\n",
    "columns[2:5] 只包含索引 2、3、4。for 循环每次把一个字段名放入 column。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad47a9d4",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6b055ff8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.749106Z",
     "iopub.status.busy": "2026-07-29T17:23:14.748922Z",
     "iopub.status.idle": "2026-07-29T17:23:14.751835Z",
     "shell.execute_reply": "2026-07-29T17:23:14.751288Z"
    }
   },
   "outputs": [],
   "source": [
    "# fields = [\"姓名\", \"班级\", \"成绩\", \"状态\"]\n",
    "# print(len(fields))\n",
    "# print(fields[0])              # 第一个字段\n",
    "# print(fields[-1])             # 最后一个字段\n",
    "# print(fields[1:3])            # 班级和成绩\n",
    "# for field in fields:\n",
    "#     print(field)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87673a4d",
   "metadata": {},
   "source": [
    "## 3. 列表添加：append 与 extend\n",
    "\n",
    "**学习目标：**区分“加入一个整体”和“加入多个元素”。\n",
    "\n",
    "- 列表.append(x)：把 x 作为一个新元素加入列表末尾；\n",
    "- 列表.extend(另一个列表)：把另一个列表中的元素逐个加入。\n",
    "\n",
    "两个方法都会直接修改原列表。真题分页收集数据时，这个区别非常重要。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c55de6fd",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "23b62fa0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.755215Z",
     "iopub.status.busy": "2026-07-29T17:23:14.755027Z",
     "iopub.status.idle": "2026-07-29T17:23:14.758691Z",
     "shell.execute_reply": "2026-07-29T17:23:14.758046Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "记录数量： 3\n",
      "[{'商品': '双肩包', '销量': 12}, {'商品': '旅行箱', '销量': 8}, {'商品': '钱包', '销量': 20}]\n",
      "把整页作为一个元素： [[{'商品': '旅行箱', '销量': 8}, {'商品': '钱包', '销量': 20}]]\n"
     ]
    }
   ],
   "source": [
    "records = []\n",
    "records.append({\"商品\": \"双肩包\", \"销量\": 12})\n",
    "\n",
    "next_page = [\n",
    "    {\"商品\": \"旅行箱\", \"销量\": 8},\n",
    "    {\"商品\": \"钱包\", \"销量\": 20},\n",
    "]\n",
    "records.extend(next_page)\n",
    "\n",
    "print(\"记录数量：\", len(records))\n",
    "print(records)\n",
    "\n",
    "page_box = []\n",
    "page_box.append(next_page)\n",
    "print(\"把整页作为一个元素：\", page_box)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb204ba1",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "records.append(字典) 加入一条记录；records.extend(next_page) 加入 next_page 中的\n",
    "两条记录，所以 records 最终有三条记录。page_box.append(next_page) 则把整个列表\n",
    "当成一个元素，形成嵌套列表。不要写 result = records.append(x)，因为 append 直接修改\n",
    "原列表，不会返回修改后的列表。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb288ca8",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ff6b06a7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.760748Z",
     "iopub.status.busy": "2026-07-29T17:23:14.760570Z",
     "iopub.status.idle": "2026-07-29T17:23:14.762722Z",
     "shell.execute_reply": "2026-07-29T17:23:14.762393Z"
    }
   },
   "outputs": [],
   "source": [
    "# records = []\n",
    "# first_record = {\"商品\": \"双肩包\", \"销量\": 12}\n",
    "# next_page = [\n",
    "#     {\"商品\": \"旅行箱\", \"销量\": 8},\n",
    "#     {\"商品\": \"钱包\", \"销量\": 20},\n",
    "# ]\n",
    "# records.                 # 用 append 加入 first_record\n",
    "# records.                 # 用 extend 加入 next_page 中的两条记录\n",
    "# print(len(records))      # 预期：3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "898844b8",
   "metadata": {},
   "source": [
    "## 4. 列表成员判断：in 与 not in\n",
    "\n",
    "**学习目标：**判断一个值是否在列表中。\n",
    "\n",
    "- 值 in 列表：在列表中时得到 True；\n",
    "- 值 not in 列表：不在列表中时得到 True。\n",
    "\n",
    "这两个成员运算符也可用于字符串、元组、集合和字典。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89dbca65",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4c98370c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.764558Z",
     "iopub.status.busy": "2026-07-29T17:23:14.764396Z",
     "iopub.status.idle": "2026-07-29T17:23:14.767630Z",
     "shell.execute_reply": "2026-07-29T17:23:14.767208Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "已提交： True\n",
      "缺考： False\n",
      "缺考不在清单中： True\n",
      "状态可以继续处理\n"
     ]
    }
   ],
   "source": [
    "allowed_statuses = [\"已提交\", \"待补交\"]\n",
    "\n",
    "print(\"已提交：\", \"已提交\" in allowed_statuses)\n",
    "print(\"缺考：\", \"缺考\" in allowed_statuses)\n",
    "print(\"缺考不在清单中：\", \"缺考\" not in allowed_statuses)\n",
    "\n",
    "current_status = \"待补交\"\n",
    "if current_status in allowed_statuses:\n",
    "    print(\"状态可以继续处理\")\n",
    "else:\n",
    "    print(\"状态不在允许清单中\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23935519",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "in 会逐项检查列表中是否存在相同的值；not in 的判断方向相反。\n",
    "成员判断的结果就是 True 或 False，因此可以直接写在 if 条件中。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37c1c3b3",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "99f4a531",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.769387Z",
     "iopub.status.busy": "2026-07-29T17:23:14.769267Z",
     "iopub.status.idle": "2026-07-29T17:23:14.771164Z",
     "shell.execute_reply": "2026-07-29T17:23:14.770854Z"
    }
   },
   "outputs": [],
   "source": [
    "# payment_methods = [\"支付宝\", \"微信\", \"银行卡\"]\n",
    "# print(\"微信\"                 payment_methods)       # 请补全 in\n",
    "# print(\"现金\"                 payment_methods)       # 请补全 not in\n",
    "#\n",
    "# current_method = \"支付宝\"\n",
    "# if current_method             payment_methods:      # 请补全\n",
    "#     print(\"允许的付款方式\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fbaf49f",
   "metadata": {},
   "source": [
    "## 5. 列表排序：sorted 与 sort\n",
    "\n",
    "**学习目标：**区分“得到排序副本”和“直接修改原列表”。\n",
    "\n",
    "- sorted(列表)：返回排好序的新列表，原列表不变；\n",
    "- 列表.sort()：直接修改原列表；\n",
    "- reverse=True：表示降序。\n",
    "\n",
    "本节只学习数字的升序与降序，不引入自定义排序函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e51c6f3",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "fcbdf74a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.772852Z",
     "iopub.status.busy": "2026-07-29T17:23:14.772747Z",
     "iopub.status.idle": "2026-07-29T17:23:14.775406Z",
     "shell.execute_reply": "2026-07-29T17:23:14.775083Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "排序副本： [72, 88, 95]\n",
      "原列表仍然是： [88, 72, 95]\n",
      "原列表改为降序： [95, 88, 72]\n"
     ]
    }
   ],
   "source": [
    "scores = [88, 72, 95]\n",
    "\n",
    "ascending_scores = sorted(scores)\n",
    "print(\"排序副本：\", ascending_scores)\n",
    "print(\"原列表仍然是：\", scores)\n",
    "\n",
    "scores.sort(reverse=True)\n",
    "print(\"原列表改为降序：\", scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b9feb67",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "sorted(scores) 新建并返回升序列表，所以第一次打印原列表时仍是原顺序。\n",
    "scores.sort(reverse=True) 直接把 scores 改成降序，不需要再赋值给其他变量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4dd7ead8",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "48a4f2d6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.777237Z",
     "iopub.status.busy": "2026-07-29T17:23:14.777114Z",
     "iopub.status.idle": "2026-07-29T17:23:14.779097Z",
     "shell.execute_reply": "2026-07-29T17:23:14.778770Z"
    }
   },
   "outputs": [],
   "source": [
    "# sales = [125, 80, 160]\n",
    "# ascending_sales =             # 使用 sorted 得到升序副本\n",
    "# print(ascending_sales)\n",
    "# print(sales)                  # 原列表不变\n",
    "#\n",
    "# sales.                        # 使用 sort 和 reverse=True 改为降序\n",
    "# print(sales)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09e7ccb5",
   "metadata": {},
   "source": [
    "## 6. 元组：固定组合与解包\n",
    "\n",
    "**学习目标：**表示一组位置固定、不希望被修改的数据。\n",
    "\n",
    "元组通常使用小括号创建。只有一个元素时必须保留逗号，例如 (2024,)。\n",
    "元组可以按位置读取，也可以一次把多个位置的值分别赋给多个变量，这称为解包。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e46bb2e6",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "37c5b96d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.780617Z",
     "iopub.status.busy": "2026-07-29T17:23:14.780474Z",
     "iopub.status.idle": "2026-07-29T17:23:14.783168Z",
     "shell.execute_reply": "2026-07-29T17:23:14.782832Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一个元素： 2024\n",
      "单元素元组： (2024,) <class 'tuple'>\n",
      "年份： 2024\n",
      "区域： 华东\n"
     ]
    }
   ],
   "source": [
    "group_key = (2024, \"华东\")\n",
    "single_year = (2024,)\n",
    "\n",
    "print(\"第一个元素：\", group_key[0])\n",
    "print(\"单元素元组：\", single_year, type(single_year))\n",
    "\n",
    "year, region = group_key\n",
    "print(\"年份：\", year)\n",
    "print(\"区域：\", region)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ebf3670",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "group_key 的两个位置分别表示年份和区域。year, region = group_key 按位置把两个值\n",
    "分别交给两个变量。单元素元组中的逗号不能省略，否则 (2024) 只是普通整数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68fb823c",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "600e1adc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.785146Z",
     "iopub.status.busy": "2026-07-29T17:23:14.784996Z",
     "iopub.status.idle": "2026-07-29T17:23:14.786732Z",
     "shell.execute_reply": "2026-07-29T17:23:14.786478Z"
    }
   },
   "outputs": [],
   "source": [
    "# group_key = (2023, \"华北\")\n",
    "# year, region =                # 请解包 group_key\n",
    "# print(year)\n",
    "# print(region)\n",
    "#\n",
    "# one_item =                   # 创建只包含“支付宝”的单元素元组\n",
    "# print(type(one_item))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cf0a4ce",
   "metadata": {},
   "source": [
    "## 7. 集合：元素唯一与快速去重\n",
    "\n",
    "**学习目标：**理解集合的“不重复、无位置顺序”特点。\n",
    "\n",
    "非空集合可以用大括号创建；空集合必须写 set()，因为 {} 表示空字典。\n",
    "set(列表) 可以删除列表中的重复值，集合.add(x) 可以添加元素。\n",
    "集合不适合保存要求固定输出顺序的数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7590dad9",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "8276ea7f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.788462Z",
     "iopub.status.busy": "2026-07-29T17:23:14.788307Z",
     "iopub.status.idle": "2026-07-29T17:23:14.790880Z",
     "shell.execute_reply": "2026-07-29T17:23:14.790559Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "去重前数量： 4\n",
      "去重后数量： 3\n",
      "是否包含直播： True\n",
      "加入后数量： 4\n"
     ]
    }
   ],
   "source": [
    "raw_channels = [\"直播\", \"搜索\", \"直播\", \"推荐\"]\n",
    "unique_channels = set(raw_channels)\n",
    "\n",
    "print(\"去重前数量：\", len(raw_channels))\n",
    "print(\"去重后数量：\", len(unique_channels))\n",
    "print(\"是否包含直播：\", \"直播\" in unique_channels)\n",
    "\n",
    "unique_channels.add(\"短视频\")\n",
    "print(\"加入后数量：\", len(unique_channels))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03aa61a0",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "set(raw_channels) 自动合并重复的“直播”，所以数量从 4 变成 3。\n",
    "add(\"短视频\") 只添加一个元素。集合本身不保证打印顺序，因此这里只检查数量和成员关系。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7af6448a",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "56955a2d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.792561Z",
     "iopub.status.busy": "2026-07-29T17:23:14.792389Z",
     "iopub.status.idle": "2026-07-29T17:23:14.794346Z",
     "shell.execute_reply": "2026-07-29T17:23:14.794053Z"
    }
   },
   "outputs": [],
   "source": [
    "# customer_ids = [\"C01\", \"C02\", \"C01\", \"C03\"]\n",
    "# unique_ids =                    # 使用 set 去重\n",
    "# print(len(unique_ids))          # 预期：3\n",
    "# print(\"C02\" in unique_ids)\n",
    "# unique_ids.                     # 使用 add 加入 C04"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2ccea12",
   "metadata": {},
   "source": [
    "## 8. 字典：创建、按键访问与 get\n",
    "\n",
    "**学习目标：**用键值对表示一条记录，并通过键读取值。\n",
    "\n",
    "字典写成 {键: 值}。字典[键] 用于读取确定存在的字段；\n",
    "字典.get(键, 默认值) 可在字段不存在时返回默认值。\n",
    "默认值是程序采用的处理规则，并不等于原始数据真实为该值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ae1f336",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "57a06cb7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.795991Z",
     "iopub.status.busy": "2026-07-29T17:23:14.795868Z",
     "iopub.status.idle": "2026-07-29T17:23:14.798551Z",
     "shell.execute_reply": "2026-07-29T17:23:14.798234Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "商品名称： 双肩包\n",
      "销量： 125\n",
      "退单量： 0\n",
      "备注： 无\n"
     ]
    }
   ],
   "source": [
    "product = {\n",
    "    \"商品名称\": \"双肩包\",\n",
    "    \"销量\": 125,\n",
    "    \"售价\": 199.0,\n",
    "}\n",
    "\n",
    "print(\"商品名称：\", product[\"商品名称\"])\n",
    "print(\"销量：\", product[\"销量\"])\n",
    "print(\"退单量：\", product.get(\"退单量\", 0))\n",
    "print(\"备注：\", product.get(\"备注\", \"无\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8905769f",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "product[\"销量\"] 适合读取必须存在的字段。当前字典没有“退单量”和“备注”，\n",
    "get 分别返回给定的默认值 0 和“无”，程序不会因为缺少这两个键而中断。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5bcd866d",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "1a640a6e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.800429Z",
     "iopub.status.busy": "2026-07-29T17:23:14.800202Z",
     "iopub.status.idle": "2026-07-29T17:23:14.802079Z",
     "shell.execute_reply": "2026-07-29T17:23:14.801786Z"
    }
   },
   "outputs": [],
   "source": [
    "# record = {\"网店名称\": \"名仕旗舰店\", \"销量\": 80}\n",
    "# print(record[                 ])            # 读取网店名称\n",
    "# return_count = record.                     # 缺少退单量时返回 0\n",
    "# print(return_count)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5943d749",
   "metadata": {},
   "source": [
    "## 9. 字典添加与修改：赋值和 update\n",
    "\n",
    "**学习目标：**更新一条记录或请求参数。\n",
    "\n",
    "字典[键] = 值：键不存在时添加，键存在时修改；\n",
    "字典.update(...)：可以一次更新一个或多个键值对。\n",
    "真题中切换网店、重置页码时会反复修改请求参数字典。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1eab7df",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "eee6ca31",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.803604Z",
     "iopub.status.busy": "2026-07-29T17:23:14.803437Z",
     "iopub.status.idle": "2026-07-29T17:23:14.806364Z",
     "shell.execute_reply": "2026-07-29T17:23:14.806061Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "逐项更新： {'pageNum': 1, 'pageSize': 1000, 'clvalue': '名仕专营店', 'tableName': 'ms'}\n",
      "批量更新： {'pageNum': 2, 'pageSize': 500, 'clvalue': '名仕专营店', 'tableName': 'ms'}\n"
     ]
    }
   ],
   "source": [
    "goods_req = {\n",
    "    \"pageNum\": 3,\n",
    "    \"pageSize\": 1000,\n",
    "    \"clvalue\": \"名仕旗舰店\",\n",
    "}\n",
    "\n",
    "goods_req[\"pageNum\"] = 1\n",
    "goods_req[\"clvalue\"] = \"名仕专营店\"\n",
    "goods_req[\"tableName\"] = \"ms\"\n",
    "print(\"逐项更新：\", goods_req)\n",
    "\n",
    "goods_req.update({\"pageNum\": 2, \"pageSize\": 500})\n",
    "print(\"批量更新：\", goods_req)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7928ea42",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "前两次赋值修改已有键；为 tableName 赋值时，由于该键原先不存在，所以新增了键值对。\n",
    "update 接收另一个字典，并一次修改 pageNum 和 pageSize。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ac05399",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "fa07aeec",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.808164Z",
     "iopub.status.busy": "2026-07-29T17:23:14.808018Z",
     "iopub.status.idle": "2026-07-29T17:23:14.810153Z",
     "shell.execute_reply": "2026-07-29T17:23:14.809835Z"
    }
   },
   "outputs": [],
   "source": [
    "# request_data = {\"pageNum\": 5, \"pageSize\": 1000, \"shop\": \"A店\"}\n",
    "# request_data[                 ] = 1         # 重置页码\n",
    "# request_data[                 ] = \"B店\"     # 切换店铺\n",
    "# request_data.                              # 用 update 把 pageSize 改为 500\n",
    "# print(request_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c4dc3a5",
   "metadata": {},
   "source": [
    "## 10. 字典遍历：keys、values 与 items\n",
    "\n",
    "**学习目标：**遍历字典的键、值或键值对。\n",
    "\n",
    "- 字典.keys()：得到所有键；\n",
    "- 字典.values()：得到所有值；\n",
    "- 字典.items()：每次得到一组“键和值”。\n",
    "\n",
    "items 最适合遍历“字段名对应处理规则”的字典。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee14e356",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "8abecdb6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.811682Z",
     "iopub.status.busy": "2026-07-29T17:23:14.811566Z",
     "iopub.status.idle": "2026-07-29T17:23:14.814315Z",
     "shell.execute_reply": "2026-07-29T17:23:14.813884Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "所有键： dict_keys(['实际销量', '实际销售额', '售价'])\n",
      "所有值： dict_values(['sum', 'sum', 'mean'])\n",
      "实际销量 使用 sum\n",
      "实际销售额 使用 sum\n",
      "售价 使用 mean\n"
     ]
    }
   ],
   "source": [
    "aggregation_rules = {\n",
    "    \"实际销量\": \"sum\",\n",
    "    \"实际销售额\": \"sum\",\n",
    "    \"售价\": \"mean\",\n",
    "}\n",
    "\n",
    "print(\"所有键：\", aggregation_rules.keys())\n",
    "print(\"所有值：\", aggregation_rules.values())\n",
    "\n",
    "for column, function_name in aggregation_rules.items():\n",
    "    print(column, \"使用\", function_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db0fb490",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "items() 每轮提供一组键值对，for column, function_name 会把这一组值解包到两个变量。\n",
    "因此输出能清楚展示“哪一列使用哪一种处理规则”。科图和名仕的聚合代码会使用这种规则字典。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ee44de1",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "8af892f1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.816315Z",
     "iopub.status.busy": "2026-07-29T17:23:14.816171Z",
     "iopub.status.idle": "2026-07-29T17:23:14.818082Z",
     "shell.execute_reply": "2026-07-29T17:23:14.817748Z"
    }
   },
   "outputs": [],
   "source": [
    "# rename_rules = {\"销量\": \"实际销量\", \"售价\": \"平均价格\"}\n",
    "# for old_name, new_name in rename_rules.             # 请补全 items()\n",
    "#     print(old_name, \"改名为\", new_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "763a4729",
   "metadata": {},
   "source": [
    "## 11. 嵌套结构：字典中有字典，字典中有列表\n",
    "\n",
    "**学习目标：**沿着层级逐步读取 JSON 转换后的 Python 数据。\n",
    "\n",
    "网站接口返回 JSON 后，通常会转换成嵌套的字典和列表。读取时遵循：\n",
    "看到字典就用键，看到列表就用数字索引。先把每一层保存到变量中，比写一条很长的索引更清楚。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2032600",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "c3d0fce5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.819745Z",
     "iopub.status.busy": "2026-07-29T17:23:14.819610Z",
     "iopub.status.idle": "2026-07-29T17:23:14.822549Z",
     "shell.execute_reply": "2026-07-29T17:23:14.822158Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一件商品： 双肩包\n",
      "第一件商品销量： 125\n",
      "是否还有下一页： False\n"
     ]
    }
   ],
   "source": [
    "response_data = {\n",
    "    \"code\": 200,\n",
    "    \"data\": {\n",
    "        \"list\": [\n",
    "            {\"商品名称\": \"双肩包\", \"销量\": 125},\n",
    "            {\"商品名称\": \"旅行箱\", \"销量\": 80},\n",
    "        ],\n",
    "        \"hasNextPage\": False,\n",
    "    },\n",
    "}\n",
    "\n",
    "data_block = response_data[\"data\"]\n",
    "records = data_block[\"list\"]\n",
    "first_record = records[0]\n",
    "\n",
    "print(\"第一件商品：\", first_record[\"商品名称\"])\n",
    "print(\"第一件商品销量：\", first_record[\"销量\"])\n",
    "print(\"是否还有下一页：\", data_block[\"hasNextPage\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45514553",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "response_data 是最外层字典；response_data[\"data\"] 仍是字典；\n",
    "data_block[\"list\"] 得到记录列表；records[0] 得到第一条商品字典；\n",
    "最后再通过“商品名称”或“销量”读取具体字段。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9576a79",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "2bc949a1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.824280Z",
     "iopub.status.busy": "2026-07-29T17:23:14.824122Z",
     "iopub.status.idle": "2026-07-29T17:23:14.826124Z",
     "shell.execute_reply": "2026-07-29T17:23:14.825795Z"
    }
   },
   "outputs": [],
   "source": [
    "# response_data = {\n",
    "#     \"data\": {\n",
    "#         \"list\": [{\"商品名称\": \"钱包\", \"销量\": 20}],\n",
    "#         \"hasNextPage\": False,\n",
    "#     }\n",
    "# }\n",
    "# data_block =                       # 读取 data\n",
    "# records =                          # 读取 list\n",
    "# first_record =                     # 读取第一条记录\n",
    "# print(first_record[               ])       # 输出商品名称"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c619818",
   "metadata": {},
   "source": [
    "## 12. 名仕真题迁移：请求字典、分页记录与列表收集\n",
    "\n",
    "**任务背景：**名仕题需要依次处理多个网店，并分页收集每家店的商品记录。\n",
    "\n",
    "本节不访问网络，只用微型数据模拟真题结构。重点观察四个已学知识点：\n",
    "店铺列表、请求参数字典、每页记录列表，以及保存全部记录的总列表。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4950f024",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "b700e151",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.827717Z",
     "iopub.status.busy": "2026-07-29T17:23:14.827579Z",
     "iopub.status.idle": "2026-07-29T17:23:14.831027Z",
     "shell.execute_reply": "2026-07-29T17:23:14.830621Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "收集到的记录数： 3\n",
      "[{'商品': '双肩包', '销量': 125}, {'商品': '旅行箱', '销量': 60}, {'商品': '钱包', '销量': 80}]\n"
     ]
    }
   ],
   "source": [
    "shop_names = [\"名仕旗舰店\", \"名仕专营店\"]\n",
    "goods_req = {\"pageNum\": 1, \"clvalue\": \"\"}\n",
    "mock_pages = {\n",
    "    \"名仕旗舰店\": [\n",
    "        [{\"商品\": \"双肩包\", \"销量\": 125}],\n",
    "        [{\"商品\": \"旅行箱\", \"销量\": 60}],\n",
    "    ],\n",
    "    \"名仕专营店\": [\n",
    "        [{\"商品\": \"钱包\", \"销量\": 80}],\n",
    "    ],\n",
    "}\n",
    "\n",
    "all_records = []\n",
    "for shop_name in shop_names:\n",
    "    goods_req[\"clvalue\"] = shop_name\n",
    "    goods_req[\"pageNum\"] = 1\n",
    "\n",
    "    for page_records in mock_pages[shop_name]:\n",
    "        all_records.extend(page_records)\n",
    "        goods_req[\"pageNum\"] += 1\n",
    "\n",
    "print(\"收集到的记录数：\", len(all_records))\n",
    "print(all_records)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9fe12b81",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "外层 for 切换网店，每次都修改 goods_req 中的店铺名并把页码重置为 1；\n",
    "内层 for 依次取得该店的每一页记录，extend 把记录逐条加入 all_records。\n",
    "\n",
    "真实参考代码把每页先转成 DataFrame，再使用 dataframe_list.append(...) 收集每页表格，\n",
    "最后用 pandas.concat 合并。本节只负责理解这些列表和字典为什么存在。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a24f2446",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "79f717de",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.833052Z",
     "iopub.status.busy": "2026-07-29T17:23:14.832932Z",
     "iopub.status.idle": "2026-07-29T17:23:14.834876Z",
     "shell.execute_reply": "2026-07-29T17:23:14.834431Z"
    }
   },
   "outputs": [],
   "source": [
    "# shop_names = [\"A店\", \"B店\"]\n",
    "# request_data = {\"pageNum\": 9, \"shop\": \"\"}\n",
    "# for shop_name in shop_names:\n",
    "#     request_data[\"shop\"] =                 # 当前店铺\n",
    "#     request_data[\"pageNum\"] =              # 每家店从第1页开始\n",
    "#     print(request_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53a5f55e",
   "metadata": {},
   "source": [
    "## 13. 科图真题迁移：列名列表与聚合规则字典\n",
    "\n",
    "**任务背景：**科图题先从订单表选择规定字段，再按年度和区域分析付款方式与客户特征。\n",
    "\n",
    "这里重点理解两种配置：\n",
    "列名列表保存“选择哪些列以及按什么顺序输出”，规则字典保存“每一列使用什么统计方法”。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8a73786",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "1aea2135",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.836506Z",
     "iopub.status.busy": "2026-07-29T17:23:14.836405Z",
     "iopub.status.idle": "2026-07-29T17:23:14.839722Z",
     "shell.execute_reply": "2026-07-29T17:23:14.839412Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "按规定顺序读取：\n",
      "年 -> 2023\n",
      "区域分布 -> 华东\n",
      "付款方式 -> 支付宝\n",
      "性别 需要计算 女性占比\n",
      "年龄 需要计算 平均值\n"
     ]
    }
   ],
   "source": [
    "selected_columns = [\"年\", \"区域分布\", \"付款方式\"]\n",
    "sample_record = {\n",
    "    \"付款方式\": \"支付宝\",\n",
    "    \"区域分布\": \"华东\",\n",
    "    \"年\": 2023,\n",
    "    \"订单编号\": \"A001\",\n",
    "}\n",
    "\n",
    "print(\"按规定顺序读取：\")\n",
    "for column in selected_columns:\n",
    "    print(column, \"->\", sample_record[column])\n",
    "\n",
    "aggregation_rules = {\n",
    "    \"性别\": \"女性占比\",\n",
    "    \"年龄\": \"平均值\",\n",
    "}\n",
    "for column, rule in aggregation_rules.items():\n",
    "    print(column, \"需要计算\", rule)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "430789b3",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "selected_columns 是列表，因此既能保存多个列名，也能保留题目规定的顺序。\n",
    "aggregation_rules 是字典，因为每个字段都对应一种明确的统计任务。\n",
    "\n",
    "在真实 pandas 代码中，df[selected_columns] 使用列名列表选列，\n",
    "grouped.agg(aggregation_rules) 使用规则字典指定聚合方法。pandas 的具体语法将在后续专题展开。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c75c3fc6",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "afc3d6d3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.841623Z",
     "iopub.status.busy": "2026-07-29T17:23:14.841485Z",
     "iopub.status.idle": "2026-07-29T17:23:14.843447Z",
     "shell.execute_reply": "2026-07-29T17:23:14.843107Z"
    }
   },
   "outputs": [],
   "source": [
    "# selected_columns =                         # 年、客户类型、性别、年龄\n",
    "# aggregation_rules = {\n",
    "#     \"性别\":                               # 女性占比\n",
    "#     \"年龄\":                               # 平均值\n",
    "# }\n",
    "# print(selected_columns)\n",
    "# print(aggregation_rules)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24a85188",
   "metadata": {},
   "source": [
    "## 14. 青娅真题迁移：目标列表与成员判断\n",
    "\n",
    "**任务背景：**青娅题需要从现金流量表中保留两个指定项目，并读取三个年度的金额。\n",
    "\n",
    "“要保留哪些项目”和“要读取哪些年度列”都适合用列表保存。\n",
    "先用普通 Python 的 in 完成筛选，就能理解后续 pandas 的 isin() 在做什么。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4109e06d",
   "metadata": {},
   "source": [
    "### 示例代码\n",
    "\n",
    "运行代码并观察输出。修改一个输入值后再次运行，比较结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "ede4f88b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.845256Z",
     "iopub.status.busy": "2026-07-29T17:23:14.845116Z",
     "iopub.status.idle": "2026-07-29T17:23:14.848412Z",
     "shell.execute_reply": "2026-07-29T17:23:14.847961Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "筛选结果： [{'项目': '加：期初现金及现金等价物余额', '2022年度': 12.3, '2021年度': 10.0, '2020年度': 8.0}]\n",
      "年度列顺序： ['2022年度', '2021年度', '2020年度']\n"
     ]
    }
   ],
   "source": [
    "target_projects = [\n",
    "    \"加：期初现金及现金等价物余额\",\n",
    "    \"五、现金及现金等价物净增加额\",\n",
    "]\n",
    "amount_columns = [\"2022年度\", \"2021年度\", \"2020年度\"]\n",
    "cashflow_records = [\n",
    "    {\n",
    "        \"项目\": \"加：期初现金及现金等价物余额\",\n",
    "        \"2022年度\": 12.3,\n",
    "        \"2021年度\": 10.0,\n",
    "        \"2020年度\": 8.0,\n",
    "    },\n",
    "    {\n",
    "        \"项目\": \"其他项目\",\n",
    "        \"2022年度\": 20.0,\n",
    "        \"2021年度\": 18.0,\n",
    "        \"2020年度\": 16.0,\n",
    "    },\n",
    "]\n",
    "\n",
    "selected_records = []\n",
    "for record in cashflow_records:\n",
    "    if record[\"项目\"] in target_projects:\n",
    "        selected_records.append(record)\n",
    "\n",
    "print(\"筛选结果：\", selected_records)\n",
    "print(\"年度列顺序：\", amount_columns)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cec85ce",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "target_projects 保存两个允许保留的项目名称，in 判断当前记录是否命中目标；\n",
    "命中后用 append 加入 selected_records。amount_columns 单独保存年度列及其输出顺序。\n",
    "\n",
    "真实 pandas 代码中的 df[\"项目\"].isin(target_projects) 会对“项目”整列完成同样的成员判断。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82115009",
   "metadata": {},
   "source": [
    "### 动手练习\n",
    "\n",
    "根据上方示例补全空位；先预测结果，再取消注释运行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "7ed7e6b5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-29T17:23:14.850341Z",
     "iopub.status.busy": "2026-07-29T17:23:14.850213Z",
     "iopub.status.idle": "2026-07-29T17:23:14.852711Z",
     "shell.execute_reply": "2026-07-29T17:23:14.852281Z"
    }
   },
   "outputs": [],
   "source": [
    "# targets = [\"期初现金\", \"净增加额\"]\n",
    "# records = [\n",
    "#     {\"项目\": \"期初现金\", \"金额\": 8.0},\n",
    "#     {\"项目\": \"其他项目\", \"金额\": 16.0},\n",
    "# ]\n",
    "# selected = []\n",
    "# for record in records:\n",
    "#     if record[\"项目\"]             targets:       # 补全成员判断\n",
    "#         selected.                                # 使用 append\n",
    "# print(selected)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "039272a3",
   "metadata": {},
   "source": [
    "## 真题代码中的数据结构地图\n",
    "\n",
    "| 真题代码片段的作用 | Python 数据结构 | 本课对应知识点 |\n",
    "|---|---|---|\n",
    "| 保存需要的列名 | 列表 | 创建、顺序、遍历 |\n",
    "| 保存多个目标项目 | 列表 | in / not in |\n",
    "| 收集每页记录或表格 | 列表 | append / extend |\n",
    "| 保存一条商品记录 | 字典 | 按键访问、get |\n",
    "| 保存请求参数 | 字典 | 添加、修改、update |\n",
    "| 保存聚合或改名规则 | 字典 | items 与键值映射 |\n",
    "| 表示接口返回结果 | 嵌套字典和列表 | 逐层访问 |\n",
    "| 表示固定的年份与区域组合 | 元组 | 解包 |\n",
    "\n",
    "集合在三套真题参考代码中没有直接出现，但教材要求理解其去重特点，因此仅保留基础用法。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "393b6dee",
   "metadata": {},
   "source": [
    "## 本章暂不作为必做\n",
    "\n",
    "- insert、remove、pop、clear 等增删方法；\n",
    "- 列表推导式、集合推导式和字典推导式；\n",
    "- 集合的 remove、discard、isdisjoint 等操作；\n",
    "- 列表和元组使用 +、* 的重复与拼接；\n",
    "- requests、BeautifulSoup、pandas groupby/agg/concat 的完整语法。\n",
    "\n",
    "这些内容不是错误或无用，而是当前三套真题的基础准备阶段不需要同时掌握。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "372df79b",
   "metadata": {},
   "source": [
    "## 离场检验\n",
    "\n",
    "1. 列表、元组、集合和字典分别适合表示什么关系？\n",
    "2. append 与 extend 的差别是什么？\n",
    "3. sorted 与 list.sort() 哪一个会直接修改原列表？\n",
    "4. in 与 not in 返回什么类型的结果？\n",
    "5. 为什么只有一个元素的元组仍要写逗号？\n",
    "6. 为什么空集合要写 set()，不能写 {}？\n",
    "7. 字典[键] 与字典.get(键, 默认值) 有什么差别？\n",
    "8. items() 每次循环提供几个值？\n",
    "9. 读取嵌套接口数据时，怎样判断下一步使用键还是数字索引？\n",
    "10. 名仕、科图和青娅真题分别在哪些地方使用了列表和字典？"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "vix",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  },
  "markdown_fixed": "移除误触发Markdown代码块的四空格缩进",
  "updated_for": "依据5f教材和三套真题重组：知识点清晰、示例先行、真题背景完整"
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
