
上周review代码,同样的业务逻辑,有的写了50行,有的写了200行。不是算法多高明,而是几个简单的代码习惯,让同样的功能实现得更优雅、更易维护。
今天就来分享这些让Python代码变优雅的秘诀——不是炫技的技巧,而是每个Python开发者都应该掌握的基本功。
新手写法:
# 判断列表是否包含某个元素
defcontains_value(lst, target):
foriteminlst:
ifitem == target:
returnTrue
returnFalse
# 反转字符串
defreverse_string(s):
result = ""
foriinrange(len(s)-1, -1, -1):
result += s[i]
returnresult
# 统计单词频率
defcount_words(text):
words = text.split()
freq = {}
forwordinwords:
ifwordinfreq:
freq[word] += 1
else:
freq[word] = 1
returnfreq优雅写法:
# 使用in运算符
targetinmy_list
# 使用切片反转
reversed_str = s[::-1]
# 使用collections.Counter
fromcollectionsimportCounter
freq = Counter(text.split())
# 更多内置函数示例
# 获取最大/最小值
max_value = max(numbers)
min_value = min(numbers, key=lambdax: x.score)
# 排序
sorted_users = sorted(users, key=lambdau: u.age, reverse=True)
# 映射和过滤
squares = map(lambdax: x**2, numbers)
evens = filter(lambdax: x%2 == 0, numbers)
# 使用列表推导式
squares = [x**2forxinnumbersifx>0]Python内置的“瑞士军刀”:
collections:专用容器类型(Counter、defaultdict、deque)itertools:迭代器工具(permutations、combinations、cycle)functools:高阶函数(lru_cache、partial、reduce)contextlib:上下文管理器工具pathlib:现代化路径操作传统循环(啰嗦且易错):
# 过滤并转换数据
result = []
foritemindata:
ifitem.is_valid():
processed = process_item(item)
ifprocessedisnotNone:
result.append(processed)
# 嵌套循环生成矩阵
matrix = []
foriinrange(3):
row = []
forjinrange(3):
row.append(i*j)
matrix.append(row)列表推导式(清晰且高效):
# 单行搞定过滤和转换
result = [process_item(item) foritemindata
ifitem.is_valid() andprocess_item(item) isnotNone]
# 嵌套推导式生成矩阵
matrix = [[i*jforjinrange(3)] foriinrange(3)]
# 字典推导式
user_dict = {user.id: user.nameforuserinusersifuser.active}
# 集合推导式
unique_words = {word.lower() forwordintext.split() iflen(word) >3}什么时候用推导式?
多行推导式的最佳实践:
# 不好的写法(太长了)
result = [transform(x) forxindataifcondition1(x) andcondition2(x) andcomplex_check(x)]
# 好的写法(清晰易读)
result = [
transform(x)
forxindata
ifcondition1(x)
andcondition2(x)
andcomplex_check(x)
]糟糕的函数(什么都要做):
defprocess_user_data(user_data, output_format='json', save_to_db=True,
send_email=False, generate_report=True, log_level='info'):
"""
这个函数做了太多事情:
1. 验证数据
2. 转换格式
3. 保存到数据库
4. 发送邮件
5. 生成报告
6. 记录日志
"""
# 200行代码...
pass优雅的函数(单一职责):
# 拆分成多个小函数
defvalidate_user_data(data):
"""只负责验证"""
ifnotdata.get('name'):
raiseValidationError("姓名必填")
ifnotdata.get('email'):
raiseValidationError("邮箱必填")
returnTrue
deftransform_to_model(validated_data):
"""只负责转换"""
returnUserModel(**validated_data)
defsave_user(user_model):
"""只负责保存"""
returndb.session.add(user_model)
defsend_welcome_email(user):
"""只负责发邮件"""
email_service.send(user.email, "welcome_template")
# 组合使用
defcreate_user(user_data):
"""协调函数,组合小功能"""
validate_user_data(user_data)
user = transform_to_model(user_data)
save_user(user)
send_welcome_email(user)
returnuser函数设计原则:
使用类型提示:
fromtypingimportOptional, List, Dict
defcalculate_stats(
numbers: List[float],
method: str = 'mean'
) ->Dict[str, float]:
"""
计算统计信息
Args:
numbers: 数字列表
method: 计算方法 ('mean', 'median', 'mode')
Returns:
包含统计结果的字典
"""
# 实现...
pass新手写法(容易忘记关闭资源):
# 文件操作
file = open('data.txt', 'r')
try:
content = file.read()
# 处理内容...
finally:
file.close()
# 数据库连接
conn = create_connection()
try:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
# 处理结果...
finally:
conn.close()
# 锁操作
lock = threading.Lock()
lock.acquire()
try:
# 临界区代码...
finally:
lock.release()优雅写法(自动管理资源):
# 使用with语句
withopen('data.txt', 'r') asfile:
content = file.read()
# 文件会自动关闭
withcreate_connection() asconn:
withconn.cursor() ascursor:
cursor.execute("SELECT * FROM users")
# 连接和游标都会自动关闭
withthreading.Lock():
# 临界区代码,锁会自动释放
# 更多内置上下文管理器
importtempfile
withtempfile.TemporaryDirectory() astmpdir:
# 临时目录会自动清理
importsqlite3
withsqlite3.connect('database.db') asconn:
# 数据库连接自动管理自定义上下文管理器:
fromcontextlibimportcontextmanager
importtime
@contextmanager
deftimer(name: str):
"""计时上下文管理器"""
start = time.time()
try:
yield
finally:
elapsed = time.time() -start
print(f"{name}耗时: {elapsed:.3f}秒")
# 使用
withtimer("数据处理"):
process_large_dataset()
# 更复杂的例子
@contextmanager
defdatabase_transaction(db_url: str):
"""数据库事务管理器"""
conn = create_connection(db_url)
try:
yieldconn
conn.commit() # 成功时提交
exceptException:
conn.rollback() # 失败时回滚
raise
finally:
conn.close()
withdatabase_transaction("postgresql://localhost/db") asconn:
conn.execute("INSERT INTO users VALUES (...)")新手写法(重复代码):
defprocess_order(order_data):
# 记录开始时间
start = time.time()
try:
# 业务逻辑
result = validate_order(order_data)
result = calculate_price(result)
result = apply_discount(result)
# 记录结束时间
elapsed = time.time() -start
print(f"订单处理耗时: {elapsed:.3f}秒")
returnresult
exceptExceptionase:
# 记录错误
print(f"订单处理失败: {e}")
raise
defgenerate_report(report_data):
# 重复的计时和错误处理代码
start = time.time()
try:
# 业务逻辑
result = analyze_data(report_data)
result = format_report(result)
elapsed = time.time() -start
print(f"报告生成耗时: {elapsed:.3f}秒")
returnresult
exceptExceptionase:
print(f"报告生成失败: {e}")
raise优雅写法(使用装饰器):
fromfunctoolsimportwraps
importtime
importlogging
logger = logging.getLogger(__name__)
deftimer(func):
"""计时装饰器"""
@wraps(func)
defwrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
elapsed = time.time() -start
logger.info(f"{func.__name__}耗时: {elapsed:.3f}秒")
returnresult
returnwrapper
deflog_errors(func):
"""错误日志装饰器"""
@wraps(func)
defwrapper(*args, **kwargs):
try:
returnfunc(*args, **kwargs)
exceptExceptionase:
logger.error(f"{func.__name__}执行失败: {e}", exc_info=True)
raise
returnwrapper
defretry(max_attempts=3, delay=1):
"""重试装饰器"""
defdecorator(func):
@wraps(func)
defwrapper(*args, **kwargs):
forattemptinrange(max_attempts):
try:
returnfunc(*args, **kwargs)
exceptExceptionase:
ifattempt == max_attempts-1:
raise
logger.warning(f"第{attempt+1}次尝试失败,{delay}秒后重试: {e}")
time.sleep(delay)
returnNone
returnwrapper
returndecorator
# 使用装饰器
@timer
@log_errors
@retry(max_attempts=3)
defprocess_order(order_data):
"""现在只关注业务逻辑"""
result = validate_order(order_data)
result = calculate_price(result)
result = apply_discount(result)
returnresult
@timer
@log_errors
defgenerate_report(report_data):
"""同样只关注业务逻辑"""
result = analyze_data(report_data)
result = format_report(result)
returnresult常用装饰器模式:
# 缓存装饰器
fromfunctoolsimportlru_cache
@lru_cache(maxsize=128)
defexpensive_calculation(x):
# 耗时计算,结果会被缓存
time.sleep(1)
returnx*x
# 类型检查装饰器
fromtypingimportget_type_hints
deftypechecked(func):
"""运行时类型检查"""
@wraps(func)
defwrapper(*args, **kwargs):
# 获取类型提示
hints = get_type_hints(func)
# 检查参数类型
for (arg_name, arg_value), (param_name, param_type) inzip(
enumerate(args), hints.items()
):
ifnotisinstance(arg_value, param_type):
raiseTypeError(f"参数{param_name}应该是{param_type}类型")
returnfunc(*args, **kwargs)
returnwrapper传统写法(样板代码多):
classUser:
def__init__(self, name, email, age, address=None, phone=None):
self.name = name
self.email = email
self.age = age
self.address = address
self.phone = phone
def__repr__(self):
returnf"User(name={self.name!r}, email={self.email!r}, age={self.age})"
def__eq__(self, other):
ifnotisinstance(other, User):
returnFalse
return (self.name == other.nameand
self.email == other.emailand
self.age == other.age)
defto_dict(self):
return {
'name': self.name,
'email': self.email,
'age': self.age,
'address': self.address,
'phone': self.phone
}优雅写法(使用数据类):
fromdataclassesimportdataclass, asdict, field
fromtypingimportOptional
@dataclass
classUser:
name: str
email: str
age: int
address: Optional[str] = None
phone: Optional[str] = None
tags: list[str] = field(default_factory=list)
@property
defis_adult(self):
returnself.age>= 18
defgreet(self):
returnf"你好,{self.name}!"
# 自动获得的功能:
# 1. __init__方法
# 2. __repr__方法
# 3. __eq__方法
# 4. 不可变版本(frozen=True)
# 5. 排序支持(order=True)
# 使用
user = User("张三", "zhang@example.com", 25)
print(user) # User(name='张三', email='zhang@example.com', age=25)
print(user == User("张三", "zhang@example.com", 25)) # True
print(asdict(user)) # 转换为字典过时的写法:
# %格式化(Python 2风格)
message = "用户%s,年龄%d,余额%.2f"% (name, age, balance)
# str.format()(Python 3早期)
message = "用户{},年龄{},余额{:.2f}".format(name, age, balance)
message = "用户{name},年龄{age}".format(name=name, age=age)
# 字符串拼接(最差)
message = "用户"+name+",年龄"+str(age) +",余额"+format(balance, ".2f")现代写法(f-string):
# Python 3.6+ 推荐
name = "张三"
age = 25
balance = 1234.5678
# 基本用法
message = f"用户{name},年龄{age},余额{balance:.2f}"
# 表达式支持
score = 85
result = f"成绩: {score}, 状态: {'及格' if score >= 60 else '不及格'}"
# 函数调用
defget_title(user):
return"先生"ifuser.gender == 'M'else"女士"
message = f"尊敬的{get_title(user)}{user.name},您好!"
# 多行f-string
message = f"""
用户信息:
姓名:{user.name}
年龄:{user.age}
邮箱:{user.email}
注册时间:{user.created_at:%Y-%m-%d %H:%M}
"""
# 调试专用(Python 3.8+)
print(f"{user.name=} {user.age=} {user.score=}")
# 输出:user.name='张三' user.age=25 user.score=85糟糕的错误处理:
# 过于宽泛
try:
result = do_something()
result = do_another_thing(result)
save_to_database(result)
exceptException: # 捕获所有异常,隐藏问题
pass
# 重复的异常处理
try:
file = open('data.txt', 'r')
content = file.read()
exceptFileNotFoundError:
print("文件不存在")
exceptPermissionError:
print("没有权限")
exceptExceptionase:
print(f"未知错误: {e}")
# 忽略异常
try:
risky_operation()
except:
pass # 静默失败,难以调试优雅的错误处理:
# 1. 只捕获你能处理的异常
try:
config = load_config('config.yaml')
exceptFileNotFoundError:
# 文件不存在,使用默认配置
config = get_default_config()
exceptyaml.YAMLErrorase:
# 配置文件格式错误,记录并退出
logger.error(f"配置文件格式错误: {e}")
sys.exit(1)
# 让其他异常向上传播
# 2. 使用上下文管理器简化资源清理
fromcontextlibimportsuppress
# 忽略特定异常
withsuppress(FileNotFoundError):
os.remove('temp_file.txt')
# 3. 自定义异常层次
classAppError(Exception):
"""应用基础异常"""
pass
classValidationError(AppError):
"""验证错误"""
pass
classDatabaseError(AppError):
"""数据库错误"""
pass
# 使用时
defcreate_user(user_data):
ifnotuser_data.get('email'):
raiseValidationError("邮箱不能为空")
try:
db.save(user_data)
exceptConnectionErrorase:
raiseDatabaseError("数据库连接失败") frome
# 4. 使用else和finally
try:
result = risky_operation()
exceptOperationErrorase:
logger.error(f"操作失败: {e}")
result = None
else:
# 只有在try成功时才执行
logger.info(f"操作成功,结果: {result}")
finally:
# 无论成功失败都执行
cleanup_resources()第一层:基础规范
第二层:Python特性
第三层:设计模式
第四层:工程实践
下次review代码时,问自己这几个问题:
“无他,惟手熟尔”!有需要的用起来!
如果你觉得这篇文章有用,欢迎点赞、转发、收藏、留言、推荐❤!
本文分享自 Nicholas与Pypi 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!