📘 Day 26 实战作业:从"调包"到"造轮子" ------ 函数专题 (上)
1. 作业综述
核心目标 :
从今天开始,我们将不再满足于调用 print(), len() 或 pandas.read_csv(),而是开始编写属于自己的自定义函数 。
这是构建大型项目(如我们之前的 ResNet 或 信贷预测 Pipeline)的基石。只有将复杂的逻辑封装成函数,代码才能具备复用性 (Reusability) 和 可读性 (Readability)。
涉及知识点:
- 函数定义 :
def关键字与文档字符串 (Docstring)。 - 参数魔法: 位置参数、关键字参数、默认参数。
- 无限接口 :
*args(可变位置参数) 与**kwargs(可变关键字参数) 的实战应用。
步骤 1:定义你的第一个工具
场景描述 :
在机器学习中,我们经常需要计算各种指标(如 Precision, Recall, F1)。
虽然 sklearn 有现成的,但为了理解原理,我们需要手写一个。
任务:
- 编写一个函数
calculate_f1(precision, recall)。 - 要求 :
- 包含清晰的 文档字符串 (Docstring),说明参数和返回值。
- 处理分母为 0 的情况(防止除零报错,返回 0)。
- 返回计算好的 F1 分数。
python
def calculate_f1(precision, recall):
"""
计算 F1 Score 的自定义函数。
参数:
precision (float): 精确率
recall (float): 召回率
返回:
float: F1 Score。如果分母为0,则返回 0.0。
"""
# 1. 检查分母是否为 0
if (precision + recall) == 0:
return 0.0
# 2. 计算公式: 2 * (P * R) / (P + R)
f1 = 2 * (precision * recall) / (precision + recall)
return f1
# --- 测试你的工具 ---
p = 0.85
r = 0.76
# 调用函数
score = calculate_f1(p, r)
print(f"Precision: {p}, Recall: {r}")
print(f"F1 Score: {score:.4f}")
# 测试极端情况
print(f"极端情况 F1: {calculate_f1(0, 0)}")
Precision: 0.85, Recall: 0.76
F1 Score: 0.8025
极端情况 F1: 0.0
步骤 2:灵活的默认参数
场景描述 :
写代码时,有些参数 90% 的情况都是固定的,但我们希望保留修改它的权利。
比如数据加载函数,默认读取 .csv,但偶尔也需要读 .txt。
任务:
- 编写函数
mock_data_loader(file_path, mode='r', file_type='csv')。 - 如果
file_type是 'csv',打印 "正在解析表格...";如果是 'json',打印 "正在解析字典..."。 - 分别使用默认参数 和修改参数的方式调用它。
python
def mock_data_loader(file_path, mode='r', file_type='csv'):
"""
模拟数据加载器。
参数:
file_path (str): 文件路径
mode (str): 打开模式,默认为 'r' (只读)
file_type (str): 文件类型,默认为 'csv'
"""
print(f"\n📂 正在打开文件: {file_path} (模式: {mode})")
if file_type == 'csv':
print(" > 正在解析 CSV 表格数据...")
elif file_type == 'json':
print(" > 正在解析 JSON 结构化数据...")
else:
print(f" > ⚠️ 未知文件类型: {file_type}")
print(" ✅ 加载完成!")
# --- 调用测试 ---
# 1. 最简单的调用 (使用全部默认值)
mock_data_loader("train_data.csv")
# 2. 修改部分默认值 (关键字传参)
mock_data_loader("config.json", file_type='json')
# 3. 全部自定义
mock_data_loader("log.txt", mode='w', file_type='text')
📂 正在打开文件: train_data.csv (模式: r)
> 正在解析 CSV 表格数据...
✅ 加载完成!
📂 正在打开文件: config.json (模式: r)
> 正在解析 JSON 结构化数据...
✅ 加载完成!
📂 正在打开文件: log.txt (模式: w)
> ⚠️ 未知文件类型: text
✅ 加载完成!
步骤 3:万能接口 *args 与 **kwargs
场景描述 :
这是 Python 高级编程中最帅气的特性,在 PyTorch 和 Transformers 源码中随处可见。
*args: 用于接收任意数量的无名参数(打包成元组)。**kwargs: 用于接收任意数量的键值对参数(打包成字典)。
任务 :
编写一个"超级模型训练器" train_model(model_name, *metrics, **config):
model_name: 必填参数。*metrics: 接收任意数量的评估指标(如 "Accuracy", "F1", "AUC")。**config: 接收任意数量的超参数(如 lr=0.01, batch_size=32)。
python
def train_model(model_name, *metrics, **config):
print(f"\n🚀 开始训练模型: 【{model_name}】")
# 1. 处理 *args (metrics)
if metrics:
print("📊 需要计算的指标:")
for m in metrics:
print(f" - {m}")
else:
print("⚠️ 未指定评估指标!")
# 2. 处理 **kwargs (config)
print("⚙️ 训练配置 (Hyperparameters):")
for key, value in config.items():
print(f" {key}: {value}")
print("... 模型训练中 ...")
print("✅ 训练结束!")
# --- 实战调用 ---
# 场景 A: 简单训练
train_model("LogisticRegression", "Accuracy", lr=0.1)
# 场景 B: 复杂深度学习训练
# 注意看:我可以传 2 个指标,也可以传 10 个配置,函数都能接得住!
train_model(
"ResNet50",
"Accuracy", "Precision", "Recall", "F1_Score", # *metrics
batch_size=64, # **config
learning_rate=0.001,
optimizer="Adam",
device="cuda:0"
)
🚀 开始训练模型: 【LogisticRegression】
📊 需要计算的指标:
- Accuracy
⚙️ 训练配置 (Hyperparameters):
lr: 0.1
... 模型训练中 ...
✅ 训练结束!
🚀 开始训练模型: 【ResNet50】
📊 需要计算的指标:
- Accuracy
- Precision
- Recall
- F1_Score
⚙️ 训练配置 (Hyperparameters):
batch_size: 64
learning_rate: 0.001
optimizer: Adam
device: cuda:0
... 模型训练中 ...
✅ 训练结束!
🎓 Day 26 总结:掌控代码的控制权
今天我们完成了一次身份的转变:
- 封装思维 :通过
def,我们将一堆代码打包成了一个function。以后遇到计算 F1 Score,直接调用即可,这就是 DRY 原则 (Don't Repeat Yourself)。 - 接口设计 :
- 默认参数 让函数好用(大部分时候不用传参)。
*args/**kwargs让函数强大(想传多少传多少)。
深度学习伏笔 :
你今天写的 train_model(name, *args, **kwargs) 其实就是 PyTorch 中模型定义和训练循环的雏形。在后续的大型项目中,这种写法能让你的代码极具扩展性。
Next Level : 下一次,我们将探索函数的高阶玩法(Lambda, Map, Filter),让数据处理效率翻倍!