5

我对装饰器还不够好,还没有做到这一点......是否可以定义一个装饰器live_doc,它允许我在方法或函数调用之后获得一个插值的 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 和返回值隐藏在某处。

4

3 回答 3

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.

于 2012-11-29T05:30:04.240 回答
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)
于 2012-11-29T04:32:48.040 回答
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

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

>>> 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',)
于 2012-11-29T05:18:08.757 回答