如何定义一个装饰器,它将为函数/方法调用提供插值的文档字符串

时间:2012-11-29 04:21:35

标签: python decorator

我对装饰器还不够好...还是可以定义一个装饰器 live_doc ,它允许我在方法或函数调用后得到插值的文档字符串,填充用实际的参数和返回值。

@live_doc("f was called with %d, %s and returned %d")
def f(x, y):
  x + len(y)

在下面的代码之后:

f(3, "marty")

d = f.doc 

d 应该是“f被叫3,”marty“,并返回8”。在访问f.doc之前,我宁愿不构建字符串,但肯定需要松开调用args&某处返回价值。

3 个答案:

答案 0 :(得分:1)

这是一个有点概括的解决方案,可以处理原始文档字符串 作为模板,并维护有关装饰函数的其他信息 (就像它的名字一样):

from functools import wraps

def live_doc(func):
    template = func.__doc__
    @wraps(func)
    def wrapper(*args, **kwargs):
        ret_val = func(*args, **kwargs)
        args_pretty = ", ".join(repr(a) for a in args)
        kw_pretty = ", ".join("%s=%r" % (k, v) for k, v in kwargs.items())
        signature = ", ".join(x for x in (args_pretty, kw_pretty) if x)
        name =  func.__name__
        wrapper.__doc__ = template % locals()
        return ret_val
    return wrapper

@live_doc
def f(x, y):
    """%(name)s was called with %(signature)s and returned %(ret_val)r."""
    return x + len(y)

首次调用f之前,交互式解释器中的help(f)会为您提供:

Help on function f in module __main__:

f(*args, **kwargs)
    %(name)s was called with %(signature)s and returned %(ret_val)r.

调用之后,你得到:

f(*args, **kwargs)
    f was called with 3, 'marty' and returned 8.

或者使用更一般的功能,炫耀kwargs

@live_doc
def q(*args, **kwargs):
    """%(name)s was called with %(signature)s and returned %(ret_val)r."""
    return len(args) + len(kwargs)

>>> q(1, 2, 3, a=7, b="foo")
5
>>> help(q)
q(*args, **kwargs)
    q was called with 1, 2, 3, a=7, b='foo' and returned 5.

显然,您可以在wrapper内的模板中创建想要使用的任何变量。

答案 1 :(得分:0)

这是我的代码,(我觉得写它很傻,所以我可能做错了,特别是在t的中间部分):

def live_doc(d):
    def f_d(f):
        def f_new(*args):
            r = f(*args)
            t = [a for a in args]
            t.append(r)
            t = tuple(t)
            f_new.doc = d % t
            return r
        return f_new
    return f_d

@live_doc("f was called with %d, %s and returned %d")
def f(x,y):
    return x + len(y)

f(1,"hi")
print(f.doc)
// f was called with 1, hi and returned 3

我使用的是http://www.python.org/dev/peps/pep-0318/

@decomaker(argA, argB, ...)
def func(arg1, arg2, ...):
    pass

相当于

func = decomaker(argA, argB, ...)(func)

答案 2 :(得分:0)

我想出了这个:

#!/usr/bin/env python

def docme(func):
    def wrap(*args, **kwargs):
        retval = None
        wrap.__doc__ = wrap.__olddoc__ + """

Last called with args: %s, %s
""" % (args, kwargs)
        try:
            retval = func(*args, **kwargs)
            wrap.__doc__ += 'Last returned: %s' % retval
            return retval
        except Exception as exc:
            wrap.__doc__ += 'Failed and raised: %r' % exc
            raise

    wrap.__doc__ = func.__doc__ + '\n\nHas not been called yet'
    wrap.__name__ = func.__name__
    wrap.__olddoc__ = func.__doc__
    return wrap

@docme
def foo(x):
    """foo docs"""
    if x == 1:
        raise ValueError('baz')
    return x * 2

它维护函数的doc字符串,因此您仍然可以调用help(foo)来阅读其指令。在每次调用时,它都会使用参数和结果(或引发的异常)更新docstring:

>>> print foo.__doc__
foo docs

Has not been called yet
>>> foo(2)
4
>>> print foo.__doc__
foo docs

Last called with args: (2,), {}
Last returned: 4
>>> foo(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/foo.py", line 11, in wrap
    retval = func(*args, **kwargs)
  File "/tmp/foo.py", line 27, in foo
    raise ValueError('baz')
ValueError: baz
>>> print foo.__doc__
foo docs

Last called with args: (1,), {}
Failed and raised: ValueError('baz',)