作为一名有十年Python开发经验的工程师,我经常看到新手开发者止步于基础示例代码,而无法将其转化为真正可用的生产代码。今天我想通过一个简单的数值处理函数,展示如何逐步构建健壮、可维护的Python代码。
让我们从最基础的示例开始:
python复制def example_function(input_data):
processed_data = input_data * 2
return processed_data
这个函数虽然简单,但已经包含了函数定义、参数传递、数据处理和返回结果等基本要素。在开发初期,这样的原型代码完全合理,但如果要投入实际使用,我们需要考虑更多因素。
生产环境中,我们不能假设用户总是会传入正确的参数类型。让我们添加类型检查和异常处理:
python复制def safe_double(input_data):
"""将输入值乘以2并返回结果
参数:
input_data (int|float): 需要处理的数值
返回:
int|float: 处理后的结果
或
None: 当输入无效时
抛出:
ValueError: 当输入不是数字时
"""
if not isinstance(input_data, (int, float)):
raise ValueError("输入必须是整数或浮点数")
return input_data * 2
注意:在Python中,使用isinstance()比type()检查更灵活,因为它考虑了继承关系。这里我们允许int和float两种数字类型。
即使是简单的数值运算,在大规模数据处理时性能也很关键。我们可以通过一些简单的优化提升效率:
python复制from functools import lru_cache
@lru_cache(maxsize=128)
def cached_double(input_data):
"""带缓存的加倍函数,适用于重复计算相同值的场景"""
return input_data * 2
这个装饰器会缓存最近的128个不同输入的计算结果,当相同参数再次出现时直接返回缓存值,避免重复计算。
让我们扩展功能,将处理结果保存到SQLite数据库:
python复制import sqlite3
from contextlib import contextmanager
@contextmanager
def db_connection(db_path='example.db'):
"""数据库连接上下文管理器"""
conn = sqlite3.connect(db_path)
try:
yield conn
finally:
conn.close()
def save_processed_data(data):
"""将处理后的数据保存到数据库"""
processed = safe_double(data)
with db_connection() as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS processed_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
original_value REAL NOT NULL,
processed_value REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
cursor.execute(
"INSERT INTO processed_data (original_value, processed_value) VALUES (?, ?)",
(data, processed)
)
conn.commit()
在实际项目中,我们还需要考虑:
python复制from sqlalchemy import create_engine, Column, Integer, Float, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class ProcessedData(Base):
__tablename__ = 'processed_data'
id = Column(Integer, primary_key=True)
original_value = Column(Float, nullable=False)
processed_value = Column(Float, nullable=False)
created_at = Column(DateTime, server_default='CURRENT_TIMESTAMP')
engine = create_engine('sqlite:///example.db')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
def save_with_orm(data):
processed = safe_double(data)
session = Session()
try:
record = ProcessedData(
original_value=data,
processed_value=processed
)
session.add(record)
session.commit()
except Exception as e:
session.rollback()
raise
finally:
session.close()
没有测试的代码就像没有保险的汽车。让我们为我们的函数编写测试:
python复制import unittest
from unittest.mock import patch
class TestDoubleFunctions(unittest.TestCase):
def test_safe_double_with_valid_input(self):
self.assertEqual(safe_double(5), 10)
self.assertEqual(safe_double(3.5), 7.0)
def test_safe_double_with_invalid_input(self):
with self.assertRaises(ValueError):
safe_double("not a number")
@patch('sqlite3.connect')
def test_save_processed_data(self, mock_connect):
mock_conn = mock_connect.return_value
mock_cursor = mock_conn.cursor.return_value
save_processed_data(10)
mock_cursor.execute.assert_any_call("""
CREATE TABLE IF NOT EXISTS processed_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
original_value REAL NOT NULL,
processed_value REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
mock_cursor.execute.assert_any_call(
"INSERT INTO processed_data (original_value, processed_value) VALUES (?, ?)",
(10, 20)
)
mock_conn.commit.assert_called_once()
if __name__ == '__main__':
unittest.main()
对于关键路径代码,我们需要进行性能测试:
python复制import timeit
def performance_test():
setup = "from __main__ import cached_double"
stmt = "cached_double(42)"
time = timeit.timeit(stmt, setup, number=1000000)
print(f"执行100万次耗时: {time:.2f}秒")
performance_test()
生产代码需要完善的日志系统:
python复制import logging
from logging.handlers import RotatingFileHandler
def setup_logging():
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(
'app.log',
maxBytes=1024*1024, # 1MB
backupCount=5
)
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
logger = setup_logging()
def logged_double(input_data):
try:
result = safe_double(input_data)
logger.info(f"成功处理数据: {input_data} -> {result}")
return result
except ValueError as e:
logger.error(f"处理数据失败: {input_data} - {str(e)}")
raise
硬编码的配置不利于维护,我们应该使用配置管理:
python复制import configparser
import os
def load_config():
config = configparser.ConfigParser()
config.read([
os.path.expanduser('~/.myapp.cfg'), # 用户级配置
'/etc/myapp.cfg', # 系统级配置
'local.cfg' # 本地开发配置
])
return {
'db_path': config.get('database', 'path', fallback='example.db'),
'log_level': config.get('logging', 'level', fallback='INFO'),
'cache_size': config.getint('performance', 'cache_size', fallback=128)
}
使用工具保证代码质量:
bash复制# 安装静态分析工具
pip install pylint black mypy
# 运行代码检查
pylint your_module.py
black --check your_module.py
mypy your_module.py
示例.gitlab-ci.yml配置:
yaml复制stages:
- test
- lint
- deploy
unit_test:
stage: test
script:
- python -m unittest discover
static_analysis:
stage: lint
script:
- pip install pylint black mypy
- pylint your_module.py
- black --check your_module.py
- mypy your_module.py
deploy_prod:
stage: deploy
script:
- echo "部署到生产环境"
only:
- master
使用Flask将我们的功能暴露为API:
python复制from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/double', methods=['POST'])
def double_api():
data = request.get_json()
try:
number = float(data['number'])
result = safe_double(number)
return jsonify({
'status': 'success',
'result': result,
'original': number
}), 200
except (ValueError, KeyError) as e:
return jsonify({
'status': 'error',
'message': str(e)
}), 400
if __name__ == '__main__':
app.run(debug=True)
对于耗时操作,可以使用Celery实现异步处理:
python复制from celery import Celery
app = Celery('tasks', broker='pyamqp://guest@localhost//')
@app.task
def async_double(number):
return safe_double(number)
# 调用示例
result = async_double.delay(42)
print(result.get(timeout=10)) # 获取结果,最多等待10秒
即使使用参数化查询,也要注意其他安全问题:
python复制def safe_query(user_input):
with db_connection() as conn:
cursor = conn.cursor()
# 危险:直接拼接SQL
# cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'")
# 安全:参数化查询
cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))
# 额外的输入清理
cleaned_input = ''.join(c for c in user_input if c.isalnum())
return cursor.fetchall()
处理敏感数据时需要特别小心:
python复制import hashlib
import os
from cryptography.fernet import Fernet
def encrypt_data(data, key=None):
if key is None:
key = Fernet.generate_key()
cipher = Fernet(key)
return cipher.encrypt(data.encode()), key
def decrypt_data(encrypted_data, key):
cipher = Fernet(key)
return cipher.decrypt(encrypted_data).decode()
# 使用示例
data = "敏感信息"
encrypted, key = encrypt_data(data)
decrypted = decrypt_data(encrypted, key)
使用cProfile分析代码性能:
python复制import cProfile
def profile_function():
pr = cProfile.Profile()
pr.enable()
# 要分析的代码
for i in range(10000):
safe_double(i)
pr.disable()
pr.print_stats(sort='cumulative')
profile_function()
使用memory_profiler检查内存消耗:
python复制from memory_profiler import profile
@profile
def memory_intensive_operation():
data = [safe_double(i) for i in range(100000)]
return sum(data) / len(data)
memory_intensive_operation()
使用Sphinx生成专业文档:
bash复制# 安装Sphinx
pip install sphinx
# 初始化文档项目
sphinx-quickstart docs
# 编写文档后生成HTML
cd docs && make html
完善的类型提示和文档:
python复制from typing import Union, Optional
def annotated_double(
input_data: Union[int, float],
verbose: bool = False
) -> Optional[float]:
"""带类型注解的加倍函数
Args:
input_data: 要处理的数值,可以是整数或浮点数
verbose: 是否打印详细日志
Returns:
处理后的结果,如果输入无效则返回None
Raises:
ValueError: 当输入不是数字时
"""
try:
result = safe_double(input_data)
if verbose:
print(f"处理结果: {result}")
return result
except ValueError as e:
if verbose:
print(f"错误: {e}")
return None
在实际项目中,我通常会将这些小技巧和最佳实践组合使用,根据项目规模和团队习惯进行适当调整。记住,好的代码不是一蹴而就的,而是通过不断重构和优化逐步形成的。