首页 > 解决方案 > Python 装饰器采用额外的参数反模式

问题描述

通过包装器向函数添加参数是python反模式吗?我想添加一个包装器,将许多函数的输出保存到一个位置,所以包装器似乎很有意义。但是,Pycharm 无法自动完成修饰函数的参数(https://intellij-support.jetbrains.com/hc/en-us/community/posts/360002754060-Autocomplete-with-arguments-for-decorated-functions)。

并且一些与使用包装器更改函数签名相关的讨论似乎表明这是一种不好的做法(https://youtrack.jetbrains.com/issue/PY-33688#focus=Comments-27-3268273.0-0) .

一些装饰器可以更改函数的签名,以便正确处理这种情况 PyCharm 必须读取由于性能原因而无法完成的装饰器主体。

因此,执行以下操作会是一种反模式:

from functools import wraps
from typing import Callable

my_dict = {"very": {"deeply": {"nested": {"filepath": "hidden_filepath"}}}}


def decorator(function: Callable):
    @wraps(function)
    def wrapper(extra_arg: str, function_arg: str) -> str:
        file_path: str = my_dict["very"]["deeply"]["nested"][extra_arg]
        print(f"saving to: {file_path}")
        result: str = function(function_arg)
        print(f"result: {result}")
        return result

    wrapper.__doc__ += "/n:param extra_arg: an extra argument"
    return wrapper


@decorator
def my_function(an_arg: str) -> str:
    """
    my docstring
    :param an_arg:
    :return:
    """
    print(f"my_function arg: {an_arg}")
    return an_arg * 2


my_function("filepath", "cool_str")

我也不喜欢在函数中附加到文档字符串,但发现这是一个解决方案:签名更改装饰器:正确记录附加参数。只更改装饰函数的文档字符串会更有意义吗?

编辑:我能想到的唯一其他合理的解决方案是创建一个将另一个函数作为参数的函数,这是包装器应该解决的问题,例如。

def decorator(extra_arg:str, function: Callable, **kwargs)-> str:
    file_path: str = my_dict["very"]["deeply"]["nested"][extra_arg]
    print(f"saving to: {file_path}")
    result: str = function(**kwargs)
    print(f"result: {result}")
    return result

def my_function(an_arg: str) -> str:
    """
    my docstring
    :param an_arg:
    :return:
    """
    print(f"my_function arg: {an_arg}")
    return an_arg * 2

decorator("filepath", my_function, an_arg="cool_str")

标签: pythondecoratorpython-decoratorsanti-patternsfunctools

解决方案


考虑可维护性。

维护您的代码的其他人会看到它my_function只有一个 arg。PyCharm 并会尖叫使用多个 argmypy调用是错误的。my_function然后其他人去“修复”所有“错误”。

你的程序中断了。

在发现您的装饰器更改了函数的签名之前,进行了数小时的故障排除。

哎呀,不需要是其他人...将您的代码保留一两个月,当您返回时,您可能会忘记您的装饰器破坏了您的功能...

所以,是的,这是一种不好的做法,一种反模式,一种代码味道,一种<在此处插入你最喜欢的负面含义流行语>。


推荐阅读