首页 论坛 置顶 结构化Python:使用装饰器进行返回值转换

正在查看 1 个帖子:1-1 (共 1 个帖子)
  • 作者
    帖子
  • #27726

    蒂莫西正在开发一个文本处理模块时,注意到了一些奇怪的事情。“玛格丽特,我有五个函数都返回字符串,并且它们都需要将结果转换为大写。我在每个函数的末尾都调用了.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 结构查看器 – 一款免费的工具,可以以树形和简单英语视图显示代码结构。支持离线使用,无需安装。

正在查看 1 个帖子:1-1 (共 1 个帖子)
  • 哎呀,回复话题必需登录。