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")

我也不喜欢在函数中附加到文档字符串,但在这里找到了解决方案:Signature-changing decorator: properly documenting additional argument。仅更改修饰函数的文档字符串是否更有意义?

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

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")

解决方法

考虑可维护性。

维护您代码的其他人会看到 my_function 只有一个参数。 PyCharm 和 mypy 会大喊大叫,用多个参数调用 my_function 是错误的。然后其他人会“修复”所有“错误”。

你的程序中断了。

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

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

所以,是的,这是一种不好的做法,一种反模式,一种代码异味,。