4. Python基础快速回顾(二):函数定义与参数、模块与包导入、文件读写(CSV/JSON)、异常处理基础

本章是Python基础快速回顾的第二部分,聚焦于构建可复用代码、组织项目结构、处理外部数据以及应对运行时错误的核心技能。这些知识是后续开发额度模型评分沙盘时,编写模块化、健壮且可维护代码的基石。

4.1 函数定义与参数

函数是组织代码、避免重复、提升可读性的基本单元。在评分沙盘中,我们会将数据清洗、特征计算、模型评分等逻辑封装成独立的函数。

4.1.1 函数定义与调用

使用 def 关键字定义函数,后跟函数名、参数列表和冒号。函数体需缩进。

def calculate_score(income, debt):
    """根据收入和负债计算一个简单的信用分数(文档字符串)"""
    if income <= 0:
        return 0
    ratio = debt / income
    if ratio < 0.3:
        return 800
    elif ratio < 0.6:
        return 600
    else:
        return 300

# 调用函数
score = calculate_score(10000, 2000)
print(score)  # 输出: 800

4.1.2 参数类型详解

Python函数的参数机制非常灵活,理解不同参数类型是编写通用函数的关键。

参数类型 定义方式 调用示例 说明
位置参数 def func(a, b): func(1, 2) 按顺序传递,必须提供
默认参数 def func(a, b=10): func(1) 或 func(1, 5) 调用时可省略,提供默认值
关键字参数 def func(a, b): func(b=2, a=1) 通过参数名指定,顺序可调
可变位置参数 def func(*args): func(1, 2, 3) 接收任意数量位置参数,打包为元组
可变关键字参数 def func(**kwargs): func(name='Alice', age=30) 接收任意数量关键字参数,打包为字典
实战提示:在评分沙盘中,*args 和 **kwargs 常用于编写装饰器或封装通用数据处理函数,例如一个可以接受任意特征列名的评分函数。

4.1.3 作用域与 return

函数内部定义的变量默认为局部变量,外部无法访问。使用 global 关键字可在函数内修改全局变量,但应谨慎使用。函数通过 return 返回值,不写 return 则返回 None。

global_threshold = 600

def check_approval(score):
    # 尝试修改全局变量(不推荐)
    # global global_threshold
    # global_threshold = 650
    return score >= global_threshold

print(check_approval(700))  # 输出: True
函数参数传递与作用域示意 全局作用域 def calculate(income, debt=0): income (参数) debt=0 (默认参数) ratio (局部变量) return score global_threshold 返回给调用者

4.2 模块与包导入

随着项目增长,将函数和变量分散到不同文件中是必要的。模块(单个 .py 文件)和包(包含 __init__.py 的文件夹)是Python组织代码的标准方式。

4.2.1 导入语法

导入方式 代码示例 使用方式 适用场景
直接导入模块 import math math.sqrt(16) 标准库或第三方库
导入特定对象 from math import sqrt sqrt(16) 仅需模块中少数函数
导入并起别名 import pandas as pd pd.DataFrame() 简化长模块名(如numpy, pandas)
导入所有对象 from math import * sqrt(16) 不推荐,易导致命名冲突

4.2.2 包结构示例

在评分沙盘项目中,我们可能会这样组织代码:

scorecard_project/
├── main.py                  # 主程序入口
├── data/
│   ├── __init__.py
│   ├── loader.py            # 数据加载函数
│   └── preprocessor.py      # 数据预处理函数
├── models/
│   ├── __init__.py
│   ├── score_calculator.py  # 评分计算函数
│   └── validator.py         # 模型验证函数
└── utils/
    ├── __init__.py
    └── config.py            # 配置文件读取
导入实践:在 main.py 中,我们可以这样导入:
from data.loader import load_application_data
from models.score_calculator import calculate_credit_score

4.3 文件读写:CSV与JSON

评分沙盘的数据通常以CSV(结构化表格)和JSON(嵌套配置/结果)格式存储。Python内置的 csv 和 json 模块提供了便捷的读写接口。

4.3.1 CSV文件操作

CSV是最常见的表格数据格式。使用 csv.reader 和 csv.writer 进行读写。

import csv

# 写入CSV
with open('applicants.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['id', 'income', 'debt', 'score'])
    writer.writerow([1, 50000, 10000, 700])
    writer.writerow([2, 30000, 15000, 550])

# 读取CSV
with open('applicants.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)  # 每行是一个列表
DictReader/DictWriter:使用 csv.DictReader 和 csv.DictWriter 可以将行映射为字典,通过列名访问数据,代码更可读。

4.3.2 JSON文件操作

JSON常用于存储配置、模型参数或API响应。使用 json.dump 和 json.load 进行读写。

import json

# 写入JSON
config = {
    "model_version": "v2.1",
    "features": ["income", "debt", "age"],
    "thresholds": {"low": 300, "medium": 600, "high": 800}
}
with open('config.json', 'w', encoding='utf-8') as f:
    json.dump(config, f, indent=4, ensure_ascii=False)

# 读取JSON
with open('config.json', 'r', encoding='utf-8') as f:
    loaded_config = json.load(f)
    print(loaded_config['thresholds']['medium'])  # 输出: 600
文件读写流程:CSV 与 JSON CSV 处理流程 csv.reader csv.writer 逐行读取为列表/字典 写入表格数据 JSON 处理流程 json.load json.dump 解析为字典/列表 序列化Python对象 数据转换

4.4 异常处理基础

程序运行中难免遇到错误(如文件不存在、类型错误、除零错误)。异常处理让程序能优雅地处理这些情况,而不是直接崩溃。

4.4.1 try-except 结构

将可能出错的代码放在 try 块中,捕获并处理异常。

try:
    with open('missing_file.csv', 'r') as f:
        data = f.read()
    score = 100 / 0
except FileNotFoundError:
    print("错误:文件未找到,使用默认配置。")
except ZeroDivisionError:
    print("错误:除零操作,请检查数据。")
except Exception as e:
    print(f"发生未知错误:{e}")
else:
    print("文件读取成功,无异常发生。")
finally:
    print("无论是否异常,此块都会执行(如关闭资源)。")

4.4.2 常见异常类型

异常类型 触发场景 评分沙盘中的典型场景
FileNotFoundError 尝试打开不存在的文件 加载历史申请数据时路径错误
ValueError 函数收到类型正确但值不合适的参数 将字符串 "abc" 转换为整数
TypeError 对不支持该操作的类型执行操作 对 None 值调用 len()
KeyError 访问字典中不存在的键 从JSON配置中读取缺失的字段
ZeroDivisionError 除数为零 计算负债收入比时收入为零

4.4.3 自定义异常与 raise

当业务逻辑出现问题时,可以主动抛出异常。

class InvalidScoreError(Exception):
    """自定义异常:分数超出有效范围"""
    pass

def validate_score(score):
    if not (0 <= score <= 1000):
        raise InvalidScoreError(f"分数 {score} 不在 0-1000 范围内")
    return True

try:
    validate_score(1200)
except InvalidScoreError as e:
    print(f"数据验证失败:{e}")
避坑指南:不要捕获所有异常而不做任何处理(如 except: pass),这会隐藏真正的bug。尽量捕获具体的异常类型,并在 except 块中记录日志或给出有意义的提示。

4.5 综合案例:安全的评分数据加载器

将本章知识点融合,编写一个健壮的数据加载函数,用于评分沙盘项目。

import csv
import json
import os

class DataLoadError(Exception):
    """数据加载自定义异常"""
    pass

def load_score_data(csv_path, config_path):
    """
    安全加载评分数据与配置。
    参数:
        csv_path: 申请人数据CSV文件路径
        config_path: 评分配置JSON文件路径
    返回:
        (applicants_list, config_dict) 元组
    """
    applicants = []
    config = {}

    # 加载配置
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
    except FileNotFoundError:
        print(f"警告:配置文件 {config_path} 未找到,使用默认配置。")
        config = {"threshold": 600}
    except json.JSONDecodeError as e:
        raise DataLoadError(f"配置文件格式错误:{e}")

    # 加载CSV数据
    try:
        with open(csv_path, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                try:
                    # 尝试转换数值字段
                    row['income'] = float(row.get('income', 0))
                    row['debt'] = float(row.get('debt', 0))
                    applicants.append(row)
                except (ValueError, TypeError) as e:
                    print(f"跳过无效数据行 {row.get('id', 'unknown')}: {e}")
    except FileNotFoundError:
        raise DataLoadError(f"数据文件 {csv_path} 不存在。")
    except Exception as e:
        raise DataLoadError(f"读取CSV时发生未知错误:{e}")

    return applicants, config

# 使用示例
try:
    data, cfg = load_score_data('applicants.csv', 'config.json')
    print(f"成功加载 {len(data)} 条记录,阈值为 {cfg.get('threshold')}")
except DataLoadError as e:
    print(f"程序终止:{e}")
本章核心收获:
  • 掌握 def 定义函数及位置、默认、可变参数的使用。
  • 理解模块与包的组织方式,熟练使用 import 语句。
  • 能够使用 csv 和 json 模块进行文件读写。
  • 运用 try-except-else-finally 编写健壮的异常处理代码。