在Python学习过程中,初学者往往满足于写出能运行的代码。但当项目规模扩大到数百行,或是需要与他人协作开发时,代码组织能力和错误处理机制就成为区分新手与进阶开发者的关键。本文通过真实项目案例,拆解模块化开发的核心技巧和异常处理的最佳实践。

想象你正在开发一个电商系统,最初把所有功能塞在一个文件里:
# 糟糕的示例:所有功能堆砌在一个文件
def add_to_cart(user_id, product_id): ...
def calculate_total(cart): ...
def apply_discount(total, coupon): ...
def process_payment(total, payment_method): ...
def send_order_email(order_data): ...
# 1000行代码后...
这种"意大利面条式代码"的三大弊端:
模块本质上是保存了Python代码的.py文件。创建第一个模块:
步骤1:创建模块文件
# math_utils.py
def add(a, b):
"""加法运算"""
return a + b
def multiply(a, b):
"""乘法运算"""
return a * b
步骤2:在另一个文件中使用
# main.py
import math_utils
result = math_utils.add(3, 5)
print(math_utils.multiply(result, 2)) # 输出16
导入方式 | 示例 | 适用场景 |
|---|---|---|
基础导入 | import math_utils | 需要使用完整命名空间 |
别名导入 | import math_utils as mu | 模块名过长时 |
函数导入 | from math_utils import add | 只需使用部分功能 |
多函数导入 | from math_utils import add, multiply | 需要多个功能时 |
通配符导入 | from math_utils import * | 不推荐(易命名冲突) |
最佳实践建议:
import module或import module as aliasfrom module import *(除非是测试环境)from datetime import datetime)每个模块自动包含的隐藏变量:
# math_utils.py
__name__ # 模块名(当直接运行时为'__main__')
__file__ # 模块文件路径
__doc__ # 模块文档字符串
if __name__ == '__main__':
# 测试代码放在这里
print(add(2, 3)) # 直接运行时执行,被导入时不执行
实用技巧:
__name__判断模块是被直接运行还是被导入当项目包含多个模块时,需要组织成包(Package)。包本质上是包含__init__.py文件的目录:
my_project/
├── __init__.py
├── utils/
│ ├── __init__.py
│ ├── math_utils.py
│ └── string_utils.py
└── core/
├── __init__.py
├── order.py
└── payment.py
__init__.py(可为空文件)示例:跨包调用
# core/order.py
from ..utils import math_utils # 相对导入上级目录的模块
def calculate_order_total(items):
total = 0
for item in items:
total += math_utils.multiply(item.price, item.quantity)
return total
__init__.py的3种用法__all__:控制from package import *的行为 # 标准库 → 第三方库 → 本地包 import os import requests from my_project import utils

email.py)
a.py导入b.py,同时b.py导入a.py考虑以下代码:
def divide(a, b):
return a / b
print(divide(10, 0)) # 程序崩溃,输出Traceback
在生产环境中,这种崩溃会导致:
try:
# 可能出错的代码
result = 10 / 0
except ZeroDivisionError:
# 处理特定异常
print("不能除以零!")
except Exception as e:
# 处理其他异常
print(f"发生未知错误: {e}")
else:
# 没有异常时执行
print("计算成功")
finally:
# 无论是否异常都执行
print("计算结束")
异常类型 | 触发场景 | 示例 |
|---|---|---|
SyntaxError | 语法错误 | print("hello |
IndentationError | 缩进错误 | 混合使用空格和制表符 |
NameError | 变量未定义 | print(x) |
TypeError | 类型错误 | 1 + "a" |
ValueError | 值错误 | int("abc") |
KeyError | 字典键不存在 | {}["key"] |
FileNotFoundError | 文件不存在 | open("nonexist.txt") |
技巧1:自定义异常
class InvalidInputError(Exception):
"""自定义异常类"""
pass
def validate_age(age):
if age < 0:
raise InvalidInputError("年龄不能为负数")
return age
技巧2:异常链
try:
# 业务代码
process_data()
except DatabaseError as e:
raise ConnectionError("数据库连接失败") from e
技巧3:上下文管理器
# 使用with语句自动处理资源
with open("file.txt") as f:
data = f.read()
# 无需手动调用f.close()
技巧4:异常日志记录
import logging
logging.basicConfig(filename='app.log', level=logging.ERROR)
try:
risky_operation()
except Exception as e:
logging.error(f"操作失败: {str(e)}", exc_info=True)
技巧5:断言调试
def calculate_discount(price, discount):
assert 0 <= discount <= 1, "折扣必须在0-1之间"
return price * (1 - discount)



web_crawler/
├── __init__.py
├── exceptions.py
├── requester.py
└── parser.py
# exceptions.py
class CrawlerError(Exception):
"""爬虫基础异常"""
pass
class RequestError(CrawlerError):
"""请求相关异常"""
pass
class ParseError(CrawlerError):
"""解析相关异常"""
pass
# requester.py
import requests
from .exceptions import RequestError
def fetch_url(url, max_retries=3):
for attempt in range(max_retries):
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status() # 自动处理HTTP错误
return resp.text
except requests.exceptions.RequestException as e:
if attempt == max_retries - 1:
raise RequestError(f"请求失败: {str(e)}") from e
# parser.py
from bs4 import BeautifulSoup
from .exceptions import ParseError
def extract_titles(html):
try:
soup = BeautifulSoup(html, 'html.parser')
titles = [h.get_text() for h in soup.find_all(['h1', 'h2', 'h3'])]
if not titles:
raise ParseError("未找到标题")
return titles
except Exception as e:
raise ParseError(f"解析失败: {str(e)}") from e
# __init__.py
from .requester import fetch_url
from .parser import extract_titles
from .exceptions import CrawlerError
def crawl_website(url):
try:
html = fetch_url(url)
titles = extract_titles(html)
return titles
except CrawlerError as e:
print(f"爬取失败: {str(e)}")
return []
Q1:模块和脚本有什么区别? A:
.py文件,通常包含函数/类if __name__ == '__main__':兼顾两种角色Q2:如何解决模块导入循环? A:
Q3:异常处理会影响性能吗? A:
Q4:如何记录完整的异常堆栈? A:
import traceback
try:
risky_operation()
except Exception:
print("完整堆栈:")
traceback.print_exc() # 打印到控制台
# 或写入文件: traceback.format_exc()
Q5:什么时候应该自定义异常? A:
通过本文的模块化实践和异常处理技巧,开发者可以:
记住:好的代码不仅应该能运行,更应该在出错时保持优雅。模块化是代码复用的基础,而异常处理是程序健壮性的保障。在实际开发中,建议从项目初期就建立良好的模块结构和异常处理机制,这将在后期维护中节省大量时间。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。