簡體   English   中英

Python 裝飾器顯示來自裝飾器和裝飾器的簽名和文檔字符串 function

[英]Python decorator show signature and docstring from both decorator and decorated function

我正在嘗試編寫一個裝飾器,其包裝器 function 采用額外的輸入參數,基於該參數在包裝器內完成一些處理。 但是,我很難 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 function 或我的foo function 的簽名和文檔字符串,但不是兩者。 對於文檔字符串,我可以做類似的事情

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

但這似乎不是很好的格式,也沒有解決簽名不反映兩個函數的參數的問題。 我將如何解決這個問題?

如果你看一下functools.wraps想要做什么,以及它是如何實現的,你會發現它非常簡單:它只是用一些被包裝的東西替換了包裝器的一些屬性,因此包裝器看起來像被包裝器。

如果您想在兩個文檔字符串之間進行精美的合並,請自己編寫,或者如果您想將此裝飾器用於許多功能,請創建一個 function 來自動完成。 這是一個概念證明:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM