✨ 29. 最佳实践
本章概述
Python 开发最佳实践:代码规范、项目结构、测试、调试、性能优化等。 预计学习时间:60 分钟
29.1 代码规范 PEP8
命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 模块/包 | 小写,下划线 | my_module, my_package |
| 类名 | 大驼峰 | MyClass, Person |
| 函数/方法 | 小写,下划线 | my_function, get_name |
| 变量 | 小写,下划线 | my_var, user_name |
| 常量 | 全大写,下划线 | MAX_SIZE, DEFAULT_VALUE |
| 私有属性/方法 | 单下划线开头 | _private_var, _internal_method |
缩进
- 用 4 个空格缩进
- 不要用 Tab
- 同一代码块缩进要一致
# ✅ 正确
if True:
print("hello")
print("world")
# ❌ 错误
if True:
print("hello") # 2个空格
print("world") # 4个空格行长度
- 每行不超过 79 个字符(PEP8)
- 实际开发中常用 88 或 120
- 太长的行要换行
# ✅ 正确:括号内换行
result = some_function(
arg1,
arg2,
arg3
)
# ✅ 正确:反斜杠换行(不推荐)
long_string = "这是一段非常非常非常非常非常非常" \
"长的字符串"空行
- 函数之间空两行
- 类的方法之间空一行
- 逻辑块之间空一行
def func1():
pass
def func2():
pass
class MyClass:
def method1(self):
pass
def method2(self):
pass空格
# ✅ 正确
x = 1
y = 2
if x == 1:
print(x)
# 函数参数
def func(a, b, c=10):
pass
# 列表、字典
lst = [1, 2, 3]
d = {"a": 1, "b": 2}
# ❌ 错误
x=1
if x==1:
print(x)导入
# ✅ 正确:按顺序分组
# 1. 标准库
import os
import sys
# 2. 第三方库
import requests
import numpy as np
# 3. 本地模块
from my_module import my_function
# 每组之间空一行自动格式化工具
- black:代码格式化
- flake8:代码检查
- pylint:代码质量分析
- isort:导入排序
29.2 项目结构
标准项目结构
my_project/
├── my_project/ # 主包
│ ├── __init__.py
│ ├── main.py # 入口
│ ├── module1.py
│ ├── module2.py
│ └── utils/ # 工具包
│ ├── __init__.py
│ └── helpers.py
├── tests/ # 测试
│ ├── __init__.py
│ ├── test_module1.py
│ └── test_module2.py
├── docs/ # 文档
├── data/ # 数据文件
├── examples/ # 示例
├── requirements.txt # 依赖
├── setup.py # 安装配置
├── README.md # 项目说明
└── .gitignore # Git忽略
requirements.txt
# 项目依赖
requests==2.31.0
numpy>=1.24.0
pandas<2.0.0安装依赖:
pip install -r requirements.txt生成依赖:
pip freeze > requirements.txt29.3 文档字符串
函数文档字符串
def add(a, b):
"""两个数相加。
Args:
a: 第一个数
b: 第二个数
Returns:
两个数的和
Examples:
>>> add(1, 2)
3
"""
return a + b类文档字符串
class Person:
"""人类。
Attributes:
name: 姓名
age: 年龄
"""
def __init__(self, name, age):
"""初始化。
Args:
name: 姓名
age: 年龄
"""
self.name = name
self.age = age文档字符串格式
- Google 风格(上面的例子)
- NumPy 风格
- reStructuredText 风格
29.4 单元测试
pytest
安装
pip install pytest
编写测试
# test_math.py
def add(a, b):
return a + b
def test_add():
assert add(1, 2) == 3
assert add(-1, 1) == 0
assert add(0, 0) == 0
def test_add_negative():
assert add(-1, -1) == -2运行测试
pytest test_math.py
pytest tests/ # 运行目录下所有测试
pytest -v # 详细输出
pytest -k "test_add" # 按名称筛选常用断言
assert x == y # 相等
assert x != y # 不等
assert x > y # 大于
assert x in lst # 包含
assert x is None # 是None
assert bool(x) # 为真测试异常
import pytest
def divide(a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
def test_divide_zero():
with pytest.raises(ValueError):
divide(10, 0)fixture
import pytest
@pytest.fixture
def user():
return {"name": "张三", "age": 18}
def test_user_name(user):
assert user["name"] == "张三"
def test_user_age(user):
assert user["age"] == 1829.5 调试技巧
print 调试
最简单的调试方法:
def func(x):
print(f"x = {x}") # 打印变量
result = x * 2
print(f"result = {result}")
return resultlogging 模块
更专业的日志记录:
import logging
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def func(x):
logger.debug(f"x = {x}")
logger.info("函数开始执行")
logger.warning("这是一个警告")
logger.error("这是一个错误")日志级别(从低到高):
DEBUG:调试信息INFO:一般信息WARNING:警告ERROR:错误CRITICAL:严重错误
pdb 调试器
Python 内置的调试器:
import pdb
def func(x):
y = x * 2
pdb.set_trace() # 设置断点
z = y + 10
return z
func(5)常用命令:
n:下一行s:进入函数c:继续执行p 变量:打印变量l:查看代码q:退出
29.6 性能优化
时间复杂度
| 操作 | 列表 | 字典 | 集合 |
|---|---|---|---|
| 查找 | O(n) | O(1) | O(1) |
| 插入 | O(n) | O(1) | O(1) |
| 删除 | O(n) | O(1) | O(1) |
选择合适的数据结构
- 需要频繁查找:用字典或集合
- 需要顺序:用列表
- 需要去重:用集合
生成器节省内存
# ❌ 列表:一次性生成所有元素,占内存
def get_numbers(n):
return [i for i in range(n)]
# ✅ 生成器:逐个生成,省内存
def get_numbers(n):
for i in range(n):
yield i字符串拼接
# ❌ 慢:每次拼接都创建新字符串
result = ""
for s in strings:
result += s
# ✅ 快:join一次性拼接
result = "".join(strings)列表推导式
# ✅ 列表推导式比for循环快
result = [x**2 for x in range(1000)]
# ❌ for循环慢
result = []
for x in range(1000):
result.append(x**2)内置函数
# ✅ 用内置函数,C实现,速度快
sum(lst)
max(lst)
min(lst)
sorted(lst)
any(lst)
all(lst)性能分析
import cProfile
def slow_function():
# 你的代码
pass
cProfile.run("slow_function()")或者用 timeit:
import timeit
def func():
return [x**2 for x in range(1000)]
# 测试运行时间
t = timeit.timeit(func, number=1000)
print(f"平均耗时:{t/1000:.6f}秒")29.7 常见错误避免
不要用裸 except
# ❌ 不好:捕获所有异常
try:
...
except:
pass
# ✅ 好:只捕获你能处理的
try:
...
except ValueError:
...
except FileNotFoundError:
...不要用可变默认参数
# ❌ 不好:可变默认参数
def func(lst=[]):
lst.append(1)
return lst
# ✅ 好:用None
def func(lst=None):
if lst is None:
lst = []
lst.append(1)
return lst不要用 == None
# ❌ 不好
if x == None:
...
# ✅ 好
if x is None:
...不要用 eval
# ❌ 危险:安全风险
result = eval(user_input)
# ✅ 好:用具体的转换
result = int(user_input)29.8 Pythonic 写法
遍历
# ❌ 不 Pythonic
for i in range(len(lst)):
print(lst[i])
# ✅ Pythonic
for item in lst:
print(item)
# 需要索引
for i, item in enumerate(lst):
print(i, item)字典遍历
# ❌ 不 Pythonic
for key in d:
print(key, d[key])
# ✅ Pythonic
for key, value in d.items():
print(key, value)交换变量
# ❌ 不 Pythonic
temp = a
a = b
b = temp
# ✅ Pythonic
a, b = b, a真值判断
# ❌ 不 Pythonic
if len(lst) == 0:
...
if x == True:
...
# ✅ Pythonic
if not lst:
...
if x:
...上下文管理器
# ❌ 不 Pythonic
f = open("file.txt", "r")
content = f.read()
f.close()
# ✅ Pythonic
with open("file.txt", "r") as f:
content = f.read()🔗 相关章节
📝 我的笔记
在这里记录你的最佳实践心得
# 你的实践代码
✅ 本章检查清单
- 了解 PEP8 代码规范
- 知道标准的项目结构
- 会写文档字符串
- 会用 pytest 写单元测试
- 了解基本的调试技巧
- 知道常见的性能优化方法
- 了解 Pythonic 的写法
- 知道常见的错误避免方法