Files
DailyChallenge_ProgrammingD…/Day_2/README.md
T

142 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Day_2 · Django 视角下的 Python 基础
> 今天还不用学 Django,也不需要会面向对象。
> 下面的题目只用到基础语法(字典、列表、字符串、函数、异常处理),
> 但每一题都是在模拟后端开发中**真正会遇到**的场景。
> 做完你会发现:Django 里那些看似神秘的东西,底层其实就是这些基础语法。
## 今日知识点 → 以后在 Django / Celery 里对应什么
| 今天练的 | 以后在 Django/Celery 里会看到 |
| --- | --- |
| 字典、列表的增删改查 | ORM 查询结果、`request.GET`/`request.POST`、JSON 响应 |
| 字符串分割与格式化 | URL 路由解析、日志输出、模板渲染 |
| 函数、`*args`/`**kwargs` | 视图函数 `def index(request, **kwargs)`、中间件、信号 |
| 异常处理(自定义异常 + try/except) | `Http404`、`ValidationError`、Celery 任务失败与重试 |
---
## 题目一(必做):解析 URL 查询参数
**背景**:浏览器访问 `https://example.com/register?username=zhangsan&age=20&city=qingdao`,
`?` 后面的 `username=zhangsan&age=20&city=qingdao` 叫做**查询参数(query string)**。
Django 里 `request.GET` 就是帮你解析好的字典。今天我们手写一个解析器,看清它背后发生了什么。
**要求**:写函数 `parse_query_string(query)`,输入一行查询参数(不含 `?`),返回一个字典。
必须处理的边界情况:
1. 空输入 → 返回空字典 `{}`
2. 重复键,如 `name=a&name=b` → 后者覆盖前者:`{'name': 'b'}`
3. 参数没有 `=`,如 `flag` → 值为空字符串:`{'flag': ''}`
4. 值为空,如 `name=` → `{'name': ''}`
**输入输出示例**
```
输入: username=zhangsan&age=20&city=qingdao
输出: {'username': 'zhangsan', 'age': '20', 'city': 'qingdao'}
输入: name=a&name=b&flag
输出: {'name': 'b', 'flag': ''}
输入: (空字符串)
输出: {}
```
**进阶(加分)**:URL 编码还原——把值中的 `+` 还原为空格,`%20` 也还原为空格。
如 `name=Zhang+San` → `{'name': 'Zhang San'}`。
> 提示:
> - 先 `split('&')` 把整体切成若干段,再对每一段 `split('=', 1)`。
> 想想为什么 `maxsplit` 要写成 1?(如果值里本身就含 `=` 会怎样?)
> - 字典赋值 `d[key] = value` 天然处理了"覆盖"和"空值"两种情况。
**出题意图**:Django 的 `QueryDict`(`request.GET`)底层干的就是这件事。
把它吃透,以后看到 `request.GET.get('page')` 就不再是黑魔法,而是老朋友。
---
## 题目二(必做):万能日志记录器(函数 + *args/**kwargs)
**背景**:后端程序要记录"哪个函数被调用了、用了多久",这叫**日志(logging)**。
Django 中间件、Celery 任务、接口调用统计里,到处都是这种"给函数包一层"的操作。
**要求**:写函数 `log_call(func)`,它接收一个**函数**作为参数,返回一个**新函数**。
新函数在被调用时:
1. 打印 `[LOG] 调用函数 <函数名>,参数为 args=(...), kwargs={...}`
2. 调用原函数,拿到结果
3. 打印 `[LOG] <函数名> 执行完成,耗时 x.xxxx 秒`(用 `time.time()` 算耗时)
4. 返回原函数的结果
参数要用 `*args` 和 `**kwargs` 打包——因为你不知道原函数会接收什么参数。
**示例**
```python
import time
def add(a, b):
return a + b
logged_add = log_call(add)
print(logged_add(1, 2))
# [LOG] 调用函数 add,参数为 args=(1, 2), kwargs={}
# [LOG] add 执行完成,耗时 0.0001 秒
# 3
```
**进阶(加分)**:给 `log_call` 加第二个参数 `label`(默认空字符串),
打印日志时带上标签,如 `log_call(add, label="核心计算")` → `[核心计算][LOG] ...`。
> 提示:如果你已经看过装饰器语法,可以用 `@log_call` 直接写在函数头上;
> 没学过就写成 `log_call(add)(1, 2)` 这种普通调用,两种都行。
**出题意图**:Django 视图函数接收 `request`、URL 参数通过 `**kwargs` 传进来;
Celery 的任务、日志中间件全部建立在"函数作为参数 + `*args/**kwargs`"之上。
这是后端开发里出现频率最高的 Python 技巧,没有之一。
---
## 题目三(进阶):模拟注册接口的参数校验
**背景**:后端接口收到请求后的第一件事永远是**校验参数**。
字段缺失、格式不对,都要返回明确的错误信息,绝不能带着脏数据往下走。
Django 里抛 `ValidationError`、Celery 里任务失败后重试——这一切的起点都是 try/except。
**要求**:
1. 定义一个字典模拟前端传来的注册数据:
`data = {"username": "zhangsan", "email": "zs@163.com", "age": "20"}`(注意 age 是字符串)
2. 自定义一个异常类 `ValidationError(Exception)`。
3. 写函数 `validate_registration(data)`,按以下规则校验,**任何一个不满足就抛 `ValidationError`**:
- `username` 必须存在且长度 ≥ 3
- `email` 必须存在且同时包含 `@` 和 `.`
- `age` 必须存在、能转成 int、且 `1 <= age <= 120`
4. 在调用方用 try/except 捕获:失败打印 `校验失败: <异常信息>`,成功打印 `注册成功,欢迎 <username>!`
**进阶(加分)**:把规则本身做成数据——写一个通用函数
`validate(data, rules)`,其中 `rules` 是一个字典:
`{字段名: (校验函数, 错误信息)}`,返回所有不满足的字段的错误列表。
(提示:校验函数就是你平时写的普通函数或 lambda。)
这是后端参数校验框架(比如 DRF 的 Serializer)的雏形,能想出来说明你已经开始有"抽象"的意识了。
**出题意图**:Django forms / serializers 的校验、Celery 任务的失败处理,
核心模式都是"**校验 → 抛异常 → 上层捕获处理**"。
今天的题目就是把这个模式焊死在你的肌肉记忆里。
---
## 完成标准
- [ ] 题目一 4 个边界情况全部通过(含进阶则 +1)
- [ ] 题目二能打印出完整的调用日志(含进阶则 +1)
- [ ] 题目三能对"缺字段 / 格式错 / 年龄越界"三种情况分别报错(含进阶则 +1)
完成后打开 `参考解答.py` 对照。建议看完后关掉文件,自己再默写一遍。
> 写给还没开始的你:这三天是递进关系——
> Day_2 用基础语法模拟后端场景,Day_3 用面向对象复刻 ORM 的雏形,Day_4 真正上手 Django。
> 每天只做必做题,也足够支撑你开学后跟上节奏。