Python代码重构技巧
Python代码重构技巧
一、什么是重构
重构是在不改变代码外部行为的前提下,改善代码内部结构的过程。
1.1 重构的目标
- 提高代码可读性
- 降低代码复杂度
- 提高可维护性
- 消除代码异味
- 为新功能做准备
1.2 重构的原则
- 小步前进
- 频繁测试
- 保持功能不变
- 一次只做一件事
二、提取函数
将复杂的代码块提取为独立的函数。
# 重构前
def process_order(order):
# 计算总价
total = 0
for item in order['items']:
total += item['price'] * item['quantity']
# 应用折扣
if order['customer_type'] == 'vip':
total *= 0.9
# 计算税费
tax = total * 0.1
total += tax
return total
# 重构后
def calculate_subtotal(items):
return sum(item['price'] * item['quantity'] for item in items)
def apply_discount(total, customer_type):
if customer_type == 'vip':
return total * 0.9
return total
def calculate_tax(total):
return total * 0.1
def process_order(order):
subtotal = calculate_subtotal(order['items'])
discounted = apply_discount(subtotal, order['customer_type'])
tax = calculate_tax(discounted)
return discounted + tax
三、简化条件表达式
3.1 提取条件到函数
# 重构前
if user.age >= 18 and user.has_license and not user.is_suspended:
allow_driving()
# 重构后
def can_drive(user):
return user.age >= 18 and user.has_license and not user.is_suspended
if can_drive(user):
allow_driving()
3.2 使用卫语句
# 重构前
def calculate_discount(customer):
if customer is not None:
if customer.is_active:
if customer.total_purchases > 1000:
return 0.1
else:
return 0.05
else:
return 0
else:
return 0
# 重构后
def calculate_discount(customer):
if customer is None:
return 0
if not customer.is_active:
return 0
if customer.total_purchases > 1000:
return 0.1
return 0.05
3.3 用多态替换条件
# 重构前
def calculate_area(shape):
if shape['type'] == 'circle':
return 3.14 * shape['radius'] ** 2
elif shape['type'] == 'rectangle':
return shape['width'] * shape['height']
elif shape['type'] == 'triangle':
return 0.5 * shape['base'] * shape['height']
# 重构后
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
四、消除重复代码
4.1 提取公共代码
# 重构前
def send_email_to_customer(customer):
email = customer.email
subject = "Thank you"
body = "Thank you for your purchase"
send_email(email, subject, body)
def send_email_to_supplier(supplier):
email = supplier.email
subject = "Order placed"
body = "A new order has been placed"
send_email(email, subject, body)
# 重构后
def send_notification_email(recipient, subject, body):
send_email(recipient.email, subject, body)
def send_email_to_customer(customer):
send_notification_email(
customer,
"Thank you",
"Thank you for your purchase"
)
def send_email_to_supplier(supplier):
send_notification_email(
supplier,
"Order placed",
"A new order has been placed"
)
五、改善命名
5.1 使用有意义的名称
# 重构前
def calc(a, b, c):
return a * b * c
# 重构后
def calculate_volume(length, width, height):
return length * width * height
5.2 避免魔法数字
# 重构前
def calculate_price(quantity):
if quantity > 100:
return quantity * 10 * 0.9
return quantity * 10
# 重构后
UNIT_PRICE = 10
BULK_DISCOUNT = 0.9
BULK_THRESHOLD = 100
def calculate_price(quantity):
total = quantity * UNIT_PRICE
if quantity > BULK_THRESHOLD:
total *= BULK_DISCOUNT
return total
六、简化函数参数
6.1 使用对象替代参数列表
# 重构前
def create_user(name, email, age, address, phone, city, country):
pass
# 重构后
from dataclasses import dataclass
@dataclass
class UserInfo:
name: str
email: str
age: int
address: str
phone: str
city: str
country: str
def create_user(user_info: UserInfo):
pass
6.2 使用关键字参数
# 重构前
def create_connection(True, False, 30, 'localhost', 5432)
# 重构后
def create_connection(
ssl=True,
verify=False,
timeout=30,
host='localhost',
port=5432
):
pass
七、分解大类
7.1 提取类
# 重构前
class Order:
def __init__(self):
self.items = []
self.customer_name = ""
self.customer_email = ""
self.customer_address = ""
def add_item(self, item):
self.items.append(item)
def send_confirmation_email(self):
send_email(self.customer_email, "Order confirmed")
# 重构后
class Customer:
def __init__(self, name, email, address):
self.name = name
self.email = email
self.address = address
class Order:
def __init__(self, customer):
self.items = []
self.customer = customer
def add_item(self, item):
self.items.append(item)
def send_confirmation_email(self):
send_email(self.customer.email, "Order confirmed")
八、移除死代码
8.1 删除未使用的代码
# 重构前
def process_data(data):
# old_method(data) # 旧方法,已弃用
new_method(data)
# 这个功能已经不需要了
# if data.needs_special_handling:
# special_handler(data)
# 重构后
def process_data(data):
new_method(data)
九、改善数据结构
9.1 用对象替换数据值
# 重构前
orders = [
{'id': 1, 'customer': 'Alice', 'total': 100},
{'id': 2, 'customer': 'Bob', 'total': 200}
]
# 重构后
from dataclasses import dataclass
@dataclass
class Order:
id: int
customer: str
total: float
orders = [
Order(1, 'Alice', 100),
Order(2, 'Bob', 200)
]
9.2 用枚举替换类型码
# 重构前
STATUS_PENDING = 0
STATUS_PROCESSING = 1
STATUS_COMPLETED = 2
order_status = STATUS_PENDING
# 重构后
from enum import Enum
class OrderStatus(Enum):
PENDING = 0
PROCESSING = 1
COMPLETED = 2
order_status = OrderStatus.PENDING
十、简化循环
10.1 使用列表推导式
# 重构前
result = []
for item in items:
if item.is_valid():
result.append(item.value)
# 重构后
result = [item.value for item in items if item.is_valid()]
10.2 使用生成器表达式
# 重构前
def process_large_file(filename):
lines = []
with open(filename) as f:
for line in f:
if line.strip():
lines.append(line.strip())
return lines
# 重构后
def process_large_file(filename):
with open(filename) as f:
return (line.strip() for line in f if line.strip())
十一、改善错误处理
11.1 使用异常而不是返回码
# 重构前
def divide(a, b):
if b == 0:
return None, "Division by zero"
return a / b, None
result, error = divide(10, 0)
if error:
print(error)
# 重构后
def divide(a, b):
if b == 0:
raise ValueError("Division by zero")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(e)
11.2 创建自定义异常
# 重构前
def process_payment(amount):
if amount <= 0:
raise ValueError("Invalid amount")
if not has_sufficient_funds(amount):
raise ValueError("Insufficient funds")
# 重构后
class PaymentError(Exception):
pass
class InvalidAmountError(PaymentError):
pass
class InsufficientFundsError(PaymentError):
pass
def process_payment(amount):
if amount <= 0:
raise InvalidAmountError("Amount must be positive")
if not has_sufficient_funds(amount):
raise InsufficientFundsError("Insufficient funds")
十二、使用上下文管理器
# 重构前
def process_file(filename):
f = open(filename)
try:
data = f.read()
process(data)
finally:
f.close()
# 重构后
def process_file(filename):
with open(filename) as f:
data = f.read()
process(data)
十三、重构的工具
13.1 自动化重构工具
- PyCharm:内置重构功能
- rope:Python重构库
- autopep8:自动格式化
- black:代码格式化
- isort:导入排序
13.2 代码质量检查
- pylint:代码质量检查
- flake8:风格检查
- mypy:类型检查
- bandit:安全检查
十四、重构的步骤
1. 确保有测试覆盖
2. 识别代码异味
3. 选择重构技术
4. 小步重构
5. 运行测试
6. 提交更改
7. 重复以上步骤
十五、常见代码异味
1. 重复代码
2. 过长函数
3. 过大类
4. 过长参数列表
5. 发散式变化
6. 霰弹式修改
7. 依恋情结
8. 数据泥团
9. 基本类型偏执
10. 过多的条件语句
十六、重构的时机
1. 添加新功能前
2. 修复bug时
3. 代码审查时
4. 性能优化前
5. 定期重构
十七、重构的注意事项
1. 不要同时重构和添加功能
2. 保持小步前进
3. 频繁提交
4. 确保测试通过
5. 与团队沟通
6. 记录重构原因
十八、总结
重构是持续改进代码质量的过程。通过识别代码异味,应用合适的重构技术,可以使代码更加清晰、易维护。重构应该是日常开发的一部分,而不是一次性的大规模改造。保持代码整洁,为未来的自己和团队成员创造更好的开发体验。
一、什么是重构
重构是在不改变代码外部行为的前提下,改善代码内部结构的过程。
1.1 重构的目标
- 提高代码可读性
- 降低代码复杂度
- 提高可维护性
- 消除代码异味
- 为新功能做准备
1.2 重构的原则
- 小步前进
- 频繁测试
- 保持功能不变
- 一次只做一件事
二、提取函数
将复杂的代码块提取为独立的函数。
# 重构前
def process_order(order):
# 计算总价
total = 0
for item in order['items']:
total += item['price'] * item['quantity']
# 应用折扣
if order['customer_type'] == 'vip':
total *= 0.9
# 计算税费
tax = total * 0.1
total += tax
return total
# 重构后
def calculate_subtotal(items):
return sum(item['price'] * item['quantity'] for item in items)
def apply_discount(total, customer_type):
if customer_type == 'vip':
return total * 0.9
return total
def calculate_tax(total):
return total * 0.1
def process_order(order):
subtotal = calculate_subtotal(order['items'])
discounted = apply_discount(subtotal, order['customer_type'])
tax = calculate_tax(discounted)
return discounted + tax
三、简化条件表达式
3.1 提取条件到函数
# 重构前
if user.age >= 18 and user.has_license and not user.is_suspended:
allow_driving()
# 重构后
def can_drive(user):
return user.age >= 18 and user.has_license and not user.is_suspended
if can_drive(user):
allow_driving()
3.2 使用卫语句
# 重构前
def calculate_discount(customer):
if customer is not None:
if customer.is_active:
if customer.total_purchases > 1000:
return 0.1
else:
return 0.05
else:
return 0
else:
return 0
# 重构后
def calculate_discount(customer):
if customer is None:
return 0
if not customer.is_active:
return 0
if customer.total_purchases > 1000:
return 0.1
return 0.05
3.3 用多态替换条件
# 重构前
def calculate_area(shape):
if shape['type'] == 'circle':
return 3.14 * shape['radius'] ** 2
elif shape['type'] == 'rectangle':
return shape['width'] * shape['height']
elif shape['type'] == 'triangle':
return 0.5 * shape['base'] * shape['height']
# 重构后
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
四、消除重复代码
4.1 提取公共代码
# 重构前
def send_email_to_customer(customer):
email = customer.email
subject = "Thank you"
body = "Thank you for your purchase"
send_email(email, subject, body)
def send_email_to_supplier(supplier):
email = supplier.email
subject = "Order placed"
body = "A new order has been placed"
send_email(email, subject, body)
# 重构后
def send_notification_email(recipient, subject, body):
send_email(recipient.email, subject, body)
def send_email_to_customer(customer):
send_notification_email(
customer,
"Thank you",
"Thank you for your purchase"
)
def send_email_to_supplier(supplier):
send_notification_email(
supplier,
"Order placed",
"A new order has been placed"
)
五、改善命名
5.1 使用有意义的名称
# 重构前
def calc(a, b, c):
return a * b * c
# 重构后
def calculate_volume(length, width, height):
return length * width * height
5.2 避免魔法数字
# 重构前
def calculate_price(quantity):
if quantity > 100:
return quantity * 10 * 0.9
return quantity * 10
# 重构后
UNIT_PRICE = 10
BULK_DISCOUNT = 0.9
BULK_THRESHOLD = 100
def calculate_price(quantity):
total = quantity * UNIT_PRICE
if quantity > BULK_THRESHOLD:
total *= BULK_DISCOUNT
return total
六、简化函数参数
6.1 使用对象替代参数列表
# 重构前
def create_user(name, email, age, address, phone, city, country):
pass
# 重构后
from dataclasses import dataclass
@dataclass
class UserInfo:
name: str
email: str
age: int
address: str
phone: str
city: str
country: str
def create_user(user_info: UserInfo):
pass
6.2 使用关键字参数
# 重构前
def create_connection(True, False, 30, 'localhost', 5432)
# 重构后
def create_connection(
ssl=True,
verify=False,
timeout=30,
host='localhost',
port=5432
):
pass
七、分解大类
7.1 提取类
# 重构前
class Order:
def __init__(self):
self.items = []
self.customer_name = ""
self.customer_email = ""
self.customer_address = ""
def add_item(self, item):
self.items.append(item)
def send_confirmation_email(self):
send_email(self.customer_email, "Order confirmed")
# 重构后
class Customer:
def __init__(self, name, email, address):
self.name = name
self.email = email
self.address = address
class Order:
def __init__(self, customer):
self.items = []
self.customer = customer
def add_item(self, item):
self.items.append(item)
def send_confirmation_email(self):
send_email(self.customer.email, "Order confirmed")
八、移除死代码
8.1 删除未使用的代码
# 重构前
def process_data(data):
# old_method(data) # 旧方法,已弃用
new_method(data)
# 这个功能已经不需要了
# if data.needs_special_handling:
# special_handler(data)
# 重构后
def process_data(data):
new_method(data)
九、改善数据结构
9.1 用对象替换数据值
# 重构前
orders = [
{'id': 1, 'customer': 'Alice', 'total': 100},
{'id': 2, 'customer': 'Bob', 'total': 200}
]
# 重构后
from dataclasses import dataclass
@dataclass
class Order:
id: int
customer: str
total: float
orders = [
Order(1, 'Alice', 100),
Order(2, 'Bob', 200)
]
9.2 用枚举替换类型码
# 重构前
STATUS_PENDING = 0
STATUS_PROCESSING = 1
STATUS_COMPLETED = 2
order_status = STATUS_PENDING
# 重构后
from enum import Enum
class OrderStatus(Enum):
PENDING = 0
PROCESSING = 1
COMPLETED = 2
order_status = OrderStatus.PENDING
十、简化循环
10.1 使用列表推导式
# 重构前
result = []
for item in items:
if item.is_valid():
result.append(item.value)
# 重构后
result = [item.value for item in items if item.is_valid()]
10.2 使用生成器表达式
# 重构前
def process_large_file(filename):
lines = []
with open(filename) as f:
for line in f:
if line.strip():
lines.append(line.strip())
return lines
# 重构后
def process_large_file(filename):
with open(filename) as f:
return (line.strip() for line in f if line.strip())
十一、改善错误处理
11.1 使用异常而不是返回码
# 重构前
def divide(a, b):
if b == 0:
return None, "Division by zero"
return a / b, None
result, error = divide(10, 0)
if error:
print(error)
# 重构后
def divide(a, b):
if b == 0:
raise ValueError("Division by zero")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(e)
11.2 创建自定义异常
# 重构前
def process_payment(amount):
if amount <= 0:
raise ValueError("Invalid amount")
if not has_sufficient_funds(amount):
raise ValueError("Insufficient funds")
# 重构后
class PaymentError(Exception):
pass
class InvalidAmountError(PaymentError):
pass
class InsufficientFundsError(PaymentError):
pass
def process_payment(amount):
if amount <= 0:
raise InvalidAmountError("Amount must be positive")
if not has_sufficient_funds(amount):
raise InsufficientFundsError("Insufficient funds")
十二、使用上下文管理器
# 重构前
def process_file(filename):
f = open(filename)
try:
data = f.read()
process(data)
finally:
f.close()
# 重构后
def process_file(filename):
with open(filename) as f:
data = f.read()
process(data)
十三、重构的工具
13.1 自动化重构工具
- PyCharm:内置重构功能
- rope:Python重构库
- autopep8:自动格式化
- black:代码格式化
- isort:导入排序
13.2 代码质量检查
- pylint:代码质量检查
- flake8:风格检查
- mypy:类型检查
- bandit:安全检查
十四、重构的步骤
1. 确保有测试覆盖
2. 识别代码异味
3. 选择重构技术
4. 小步重构
5. 运行测试
6. 提交更改
7. 重复以上步骤
十五、常见代码异味
1. 重复代码
2. 过长函数
3. 过大类
4. 过长参数列表
5. 发散式变化
6. 霰弹式修改
7. 依恋情结
8. 数据泥团
9. 基本类型偏执
10. 过多的条件语句
十六、重构的时机
1. 添加新功能前
2. 修复bug时
3. 代码审查时
4. 性能优化前
5. 定期重构
十七、重构的注意事项
1. 不要同时重构和添加功能
2. 保持小步前进
3. 频繁提交
4. 确保测试通过
5. 与团队沟通
6. 记录重构原因
十八、总结
重构是持续改进代码质量的过程。通过识别代码异味,应用合适的重构技术,可以使代码更加清晰、易维护。重构应该是日常开发的一部分,而不是一次性的大规模改造。保持代码整洁,为未来的自己和团队成员创造更好的开发体验。
