首页 › 论坛 › 置顶 › 结构化Python:使用装饰器进行返回值转换
-
作者帖子
-
2025-12-05 10:09 #27726Q QPY课程团队管理员
蒂莫西正在开发一个文本处理模块时,注意到了一些奇怪的事情。“玛格丽特,我有五个函数都返回字符串,并且它们都需要将结果转换为大写。我在每个函数的末尾都调用了
.upper()。一定有更好的方法。”玛格丽特查看了他的代码。“你说得对——这太重复了。当你发现自己在多个函数的输出中添加相同的转换时,这正是使用装饰器的完美场景。”
“装饰器可以转换返回值?”蒂莫西问。“我以为装饰器只是用于日志记录和计时。”
问题:重复的输出转换
玛格丽特向他展示了他正在做的事情:
def get_greeting(name): return f"hello, {name}".upper()def get_status(): return "系统在线".upper() def get_error_message(code): return f"错误代码: {code}".upper()</span“看到了吗?”玛格丽特问道。“每个函数的末尾都有
.upper()。这就是实现细节渗透到你的业务逻辑中。”蒂莫西点了点头。“如果我决定要标题大小写,我就得在每个函数中都进行更改。”
“没错。现在看看装饰器是如何解决这个问题的。”
def uppercase_result(func): """装饰器,用于将函数'的字符串结果转换为大写。""" def wrapper(*args, **kwargs): result = func(*args, **kwargs) if isinstance(result, str):return result.upper() return result return wrapper @uppercase_result def get_greeting(name): """返回个性化的问候语。""" return f"你好, {name}" @uppercase_result def get_status(): """返回系统状态。""" return "系统在线" @uppercase_result def get_error_message(code): """返回错误信息。"""return f"错误代码: {code}" # 用法 print(get_greeting("爱丽丝")) print(get_status()) print(get_error_message(404))理解返回值拦截
蒂莫西研究了装饰器。“所以包装器调用我的原始函数,获取结果,然后在返回之前进行转换?”
玛格丽特向他展示了结构:
树状视图:
uppercase_result(func) "装饰器,用于将函数的字符串结果转换为大写。" wrapper() result = func(*args, **kwargs) 如果 isinstance(result, str) └── 返回 result.upper() 返回 result 返回 wrapper @uppercase_result get_greeting(name) '返回个性化问候。' 返回 f'hello, {name}' @uppercase_result get_status() '返回系统状态。' 返回 'system online' @uppercase_result get_error_message(code) '返回错误信息。' 返回 f'error code: {code}' print(get_greeting('Alice')) print(get_status()) print(get_error_message(404))中文视图:
函数 uppercase_result(func): 评估 "装饰器,将函数的字符串结果转换为大写。"。 函数包装器(): 设置结果为 func(*args, **kwargs)。 如果 isinstance(result, str): 返回 result.upper()。 返回结果。 返回包装器。 装饰器 @uppercase_result 函数 get_greeting(name): 评估 '返回个性化问候。'。 返回 f'你好, {name}'。 装饰器 @uppercase_result 函数 get_status(): 评估 '返回系统状态。'。 返回 '系统在线'。 装饰器 @uppercase_result 函数 get_error_message(code): 评估 '返回错误信息。'。 返回 f'错误代码: {code}'。 评估 print(get_greeting('Alice'))。 评估 print(get_status())。 评估 print(get_error_message(404))。“看看这个流程,”玛格丽特说。“包装器调用
func(*args, **kwargs),存储结果,检查它是否是字符串,然后才应用.upper()。如果结果不是字符串,它将原封不动地传递。”蒂莫西追踪了执行过程:HELLO, ALICE SYSTEM ONLINE ERROR CODE: 404“所以装饰器拦截返回值,转换它,然后返回转换后的版本?我的函数只是返回它们的正常值,而装饰器处理大写?”
“没错。你的函数保持干净,专注于它们的逻辑。转换由装饰器一致地应用。”
类型安全的转换
蒂莫西注意到装饰器中的一些事情。“你在调用
.upper()之前检查isinstance(result, str)。为什么?”“因为并不是所有函数都返回字符串,”玛格丽特说。“看看如果我们装饰一个返回数字的函数会发生什么:”
@uppercase_result def get_error_code(): """返回一个数字,不应受到影响。""" return 404 print(get_error_code()) # 输出: 404“装饰器检查类型,仅在适当时应用转换。非字符串结果保持不变。”
蒂莫西点了点头。“所以装饰器是防御性的——它转换它能转换的内容,其他的则不作处理。”
“没错。这是转换装饰器的一个好模式。不要假设你知道函数返回什么;先检查一下。”
何时使用返回值装饰器
“那么我什么时候会使用这个模式?”蒂莫西问。
玛格丽特列出了使用场景:
“当你需要时,使用返回值转换装饰器:
- 对函数输出应用一致的格式(大写、小写、标题格式)
- 一致地对数字结果进行四舍五入或格式化
- 对输出进行清理或转义以确保安全性
- 在数据格式之间转换(字典到JSON,对象到字符串)
- 添加元数据或将结果包装在标准结构中
她给他展示了另一个例子:
def round_result(decimals=2): """装饰器,用于将数值结果四舍五入到指定的小数位。""" def decorator(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs)if isinstance(result, (int, float)): return round(result, decimals) return result return wrapper return decorator @round_result(decimals=2) def calculate_average(numbers): """计算一组数字的平均值。""" return sum(numbers) / len(numbers)print(calculate_average([1.234, 2.567, 3.891])) # 输出: 2.56“看到了吗?同样的模式 – 调用函数,检查结果类型,如果合适则应用转换。”
蒂莫西开始看到可能性了。“所以我可以有一个
@to_json装饰器,将字典结果转换为 JSON 字符串,或者一个@sanitize_html装饰器,将字符串结果中的 HTML 进行转义?”“没错。你对函数输出应用的任何一致性转换都是装饰器的候选者。”
保持函数的清晰
玛格丽特总结了这个课程。“关键的见解是关注点的分离。你的函数专注于计算或生成它们的结果。装饰器处理展示、格式化或转换。”
蒂莫西看着他清理过的函数:
@uppercase_result def get_greeting(name): return f"hello, {name}"“现在干净多了。没有
.upper()让返回语句变得杂乱。这个函数只返回它计算的结果,而装饰器处理格式化。”“这就是返回值装饰器的力量,”玛格丽特说。“它们让你在不触碰函数核心逻辑的情况下,始终如一地应用转换。如果你改变了对转换的想法,只需更改一次装饰器,而不是每个函数。”
蒂莫西测试了更改装饰器:
def titlecase_result(func): def wrapper(*args, **kwargs):result = func(*args, **kwargs) if isinstance(result, str): return result.title() return result return wrapper“现在我只需将
@uppercase_result替换为@titlecase_result,我的所有函数的行为就会改变。结构清晰地显示了这一点——装饰器包装了函数,拦截返回值,并在传递之前进行转换。”
自己分析 Python 结构: 下载 Python 结构查看器 – 一款免费的工具,可以以树形和简单英语视图显示代码结构。支持离线使用,无需安装。
-
作者帖子
- 哎呀,回复话题必需登录。