【问题标题】:Python decorator show signature and docstring from both decorator and decorated functionPython 装饰器显示来自装饰器和装饰函数的签名和文档字符串
【发布时间】:2022-01-19 12:55:28
【问题描述】:

我正在尝试编写一个装饰器,其包装函数接受一个额外的输入参数,基于该参数在包装器内完成一些处理。然而,我很难过 a) 弄清楚如何在签名中显示这个附加参数,尤其是参数预览/帮助器(在 ipynb 和 PyCharm 上测试) b) 弄清楚如何显示两个文档字符串中的信息。

设置是这样的

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(self, *args, fancy_processing=False, **kwargs):
        """Some nice docstring
        :param fancy_processing: If set, does fancy processing before executing the function
        """
        output = func(self, *args, **kwargs)
        if fancy_processing:
            output += 1 # some dummy action
        return output
    return wrapper


def foo(important_param, other_important_param=4):
    """This is a well-written docstring.
    :param important_param: Super important parameter
    :param other_important_param: Other super important parameter
    :returns: Something incredibly useful
    """
    print(important_param)  # dummy action
    return (other_important_param +1)  # some other dummy action

现在取决于我是否使用wraps,我会得到wrapper 函数或foo 函数的签名和文档字符串,但不能同时获得两者。对于文档字符串,我可以做类似的事情

...
# @wraps(func) # don't use the `wraps` functionality
...
    return output
wrapper.__doc__ += f"\n {func.__doc__}"
return wrapper

但这似乎不是很好的格式,也没有解决签名不反映两个函数的参数的问题。我将如何解决这个问题?

【问题讨论】:

    标签: python decorator


    【解决方案1】:

    如果你看看functools.wrapshow it is implemented 要做的事情,你会注意到它非常简单:它只是用一些被包装的东西替换了包装器的一些属性,所以包装器看起来像包装。

    如果您想在两个文档字符串之间进行精美的合并,请自己编写,或者如果您想将此装饰器用于许多功能,请创建一个自动执行此操作的函数。这是一个概念证明:

    import docstring_parser  # installed with PIP
    
    
    def merge_docstrings(decorated_func, wrapper_func) -> str:
        reST_style = docstring_parser.Style.REST
        decorated_doc = docstring_parser.parse(decorated_func.__doc__, style=reST_style)
        wrapper_doc = docstring_parser.parse(wrapper_func.__doc__, style=reST_style)
        # adding the wrapper doc into the decorated doc
        decorated_doc.short_description += "\n" + wrapper_doc.short_description
        return_index = next((index for index, meta_elem in enumerate(decorated_doc.meta)
                             if isinstance(meta_elem, docstring_parser.DocstringReturns)
                             ), 0)
        for offset, wrapper_doc_param in enumerate(wrapper_doc.params):
            decorated_doc.meta.insert(return_index + offset, wrapper_doc_param)
    
        return docstring_parser.compose(decorated_doc, style=reST_style)
    
    
    def my_decorator(func):
        def wrapper(self, *args, fancy_processing=False, **kwargs):
            """Some nice docstring
            :param fancy_processing: If set, does fancy processing before executing the function
            """
            output = func(self, *args, **kwargs)
            if fancy_processing:
                output += 1 # some dummy action
            return output
        wrapper.__doc__ = merge_docstrings(func, wrapper)
        return wrapper
    
    
    def foo(important_param, other_important_param=4):
        """This is a well-written docstring.
        :param important_param: Super important parameter
        :param other_important_param: Other super important parameter
        :returns: Something incredibly useful
        """
        print(important_param)  # dummy action
        return (other_important_param +1)  # some other dummy action
    
    
    decorated_foo = my_decorator(foo)  # the alternative way to decorate a function, and allows to keep the original too
    
    
    assert foo.__doc__ == ('This is a well-written docstring.\n    '
                           ':param important_param: Super important parameter\n    '
                           ':param other_important_param: Other super important parameter\n    '
                           ':returns: Something incredibly useful\n    ')
    assert decorated_foo.__doc__ == \
                          ('This is a well-written docstring.\n'
                           'Some nice docstring\n'
                           ':param important_param: Super important parameter\n'
                           ':param other_important_param: Other super important parameter\n'
                           ':param fancy_processing: If set, does fancy processing before executing the function\n'
                           # ^^^ this one has been added
                           ':returns: Something incredibly useful')
    help(decorated_foo)
    # wrapper(self, *args, fancy_processing=False, **kwargs)
    #     This is a well-written docstring.
    #     Some nice docstring
    #     :param important_param: Super important parameter
    #     :param other_important_param: Other super important parameter
    #     :param fancy_processing: If set, does fancy processing before executing the function
    #     :returns: Something incredibly useful
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-06
      • 2010-12-19
      • 2020-06-08
      • 1970-01-01
      • 2014-07-21
      • 1970-01-01
      • 2019-07-09
      • 2018-03-07
      相关资源
      最近更新 更多