2017-07-31 78 views
2

我有一個函數集合(主要是)共享參數但不同的進程。我想使用裝飾器將每個參數的描述添加到函數的標題級別文檔字符串中。具有參數的裝飾器

我試圖通過在appender中嵌入嵌套函數來模仿this answer中的結構,但失敗。我也試過functools.partial,但有些東西稍微偏離。

我嘗試:

def appender(func, *args): 
    """Appends additional parameter descriptions to func's __doc__.""" 
    def _doc(func): 
     params = ''.join([defaultdocs[arg] for arg in args]) 
     func.__doc__ += '\n' + params 
     return func 
    return _doc 

defaultdocs = { 

    'a' : 
    """ 
    a : int, default 0 
     the first parameter 
    """, 

    'b' : 
    """ 
    b : int, default 1 
     the second parameter 
    """ 
    } 

@appender('a') 
def f(a): 
    """Title-level docstring.""" 
    return a 

@appender('a', 'b') 
def g(a, b): 
    """Title-level docstring.""" 
    return a + b 

這種失敗,並且失敗了,我相信,因爲傳遞給appender的第一個參數被解釋爲func。所以,當我查看生成的文檔字符串爲g我得到:

print(g.__doc__) 
Title-level docstring. 

    b : int, default 1 
     the second parameter 

,因爲再次,'a'被解釋爲'func'時,我希望它是*args的第一要素。我該如何解決這個問題?

期望的結果:

print(g.__doc__) 
Title-level docstring. 

    a : int, default 0 
     the first parameter 

    b : int, default 1 
     the second parameter 

回答

4

這是因爲變量名傳遞真正得到捕獲到func說法。

爲了做裝飾調用在Python中,你需要編寫兩倍的功能,具有外部函數接受裝飾參數和內部函數接受原來的功能。可調用裝飾器只是返回其他裝飾器的高階函數。例如:

def appender(*args): # This is called when a decorator is called, 
         # e. g. @appender('a', 'b') 
    """Appends additional parameter descriptions to func's __doc__.""" 
    def _doc(func): # This is called when the function is about 
        # to be decorated 
     params = ''.join([defaultdocs[arg] for arg in args]) 
     func.__doc__ += '\n' + params 
     return func 
    return _doc 

外部(appender)函數作爲一個工廠新的裝飾而_doc功能是一個實際的裝飾。

  • 通原有的功能

    • 通裝飾參數傳遞給外部函數的內部函數

    一旦Python看到這一點::總是通過這種方式

    @appender('a', 'b') 
    def foo(): pass 
    

    .. 。他會做這樣的事情:

    foo = appender('a', 'b')(foo) 
    

    ...它可以擴展成這樣:

    decorator = appender('a', 'b') 
    foo = decorator(foo) 
    

    因爲如何在Python工作的作用域,每個新返回_doc功能實例都會有自己的本地args從外部函數值。

  • 1

    使用inspect.signature收集傳遞函數參數的備用解決方案。

    import inspect 
    import textwrap 
    
    def appender(defaultdocs): 
        def _doc(func): 
         params = inspect.signature(func).parameters 
         params = [param.name for param in params.values()] 
         params = ''.join([textwrap.dedent(defaultdocs[param]) 
             for param in params]) 
         func.__doc__ += '\n\nParameters\n' + 10 * '=' + params 
         return func 
    
        return _doc 
    

    實施例:

    # default docstrings for parameters that are re-used often 
    # class implementation not a good alternative in my specific case 
    
    defaultdocs = { 
    
        'a' : 
        """ 
        a : int, default 0 
         the first parameter""", 
    
        'b' : 
        """ 
        b : int, default 1 
         the second parameter""" 
        } 
    
    @appender 
    def f(a): 
        """Title-level docstring.""" 
        return a 
    
    @appender 
    def g(a, b): 
        """Title-level docstring.""" 
        return a + b 
    

    此追加用於abg.__doc__的描述,而無需在裝飾指定它們:

    help(g) 
    Help on function g in module __main__: 
    
    g(a, b) 
        Title-level docstring. 
    
        Parameters 
        ========== 
        a : int, default 0 
         the first parameter 
        b : int, default 1 
         the second parameter