如何以编程方式设置文档字符串?

67

我有一个返回函数的包装器函数。是否有办法以编程方式设置返回函数的文档字符串?如果我可以写入 __doc__,那么我会这样做:

def wrapper(a):
    def add_something(b):
       return a + b
    add_something.__doc__ = 'Adds ' + str(a) + ' to `b`'
    return add_something

那么我可以这样做:

>>> add_three = wrapper(3)
>>> add_three.__doc__
'Adds 3 to `b`

然而,由于__doc__是只读的,我无法这样做。有什么正确的方法吗?


编辑:好吧,我想让这个简单点,但当然这不是我实际要做的。尽管通常情况下__doc__是可写的,但在我的情况下它不是。

我正在尝试自动创建unittest的测试用例。我有一个包装函数,它创建了一个类对象,该对象是unittest.TestCase的子类:

import unittest
def makeTestCase(filename, my_func):
    class ATest(unittest.TestCase):
        def testSomething(self):
            # Running test in here with data in filename and function my_func
            data  = loadmat(filename)
            result = my_func(data)
            self.assertTrue(result > 0)

    return ATest

如果我创建这个类并试图设置testSomething的文档字符串,我会遇到一个错误:

>>> def my_func(): pass
>>> MyTest = makeTestCase('some_filename', my_func)
>>> MyTest.testSomething.__doc__ = 'This should be my docstring'
AttributeError: attribute '__doc__' of 'instancemethod' objects is not writable

2
你为什么不写一个文档字符串呢? - Rafe Kettler
8
因为如果您更新它,就必须始终记得手动更新所有其他包装函数中的所有其他副本。 - endolith
6个回答

61

一个instancemethod的文档字符串是从其对应的__func__对象中获取的。因此,应该修改__func__对象的文档字符串而不是instancemethod的文档字符串。(函数的__doc__属性是可写的。)

>>> class Foo(object):
...     def bar(self):
...         pass
...
>>> Foo.bar.__func__.__doc__ = "A super docstring"
>>> help(Foo.bar)
Help on method bar in module __main__:

bar(self) unbound __main__.Foo method
    A super docstring

>>> foo = Foo()
>>> help(foo.bar)
Help on method bar in module __main__:

bar(self) method of __main__.Foo instance
    A super docstring

来自2.7文档:

用户定义的方法

用户定义的方法对象结合了一个类、一个类实例(或None)和任何可调用对象(通常是用户定义的函数)。

特殊的只读属性:im_self是类实例对象,im_func是函数对象;im_class是绑定方法的im_self的类或未绑定方法请求该方法的类;__doc__ 是方法的文档(与im_func.__doc__相同);__name__是方法名称(与im_func.__name__相同); __module__是定义该方法的模块名称,如果不可用则为None。

版本2.2中更改:im_self曾用于引用定义方法的类。

版本2.6中更改:为了向3.0兼容性前进,im_func也可用作__func__ 并且im_self可用作__self__


24

我会将文档字符串传递到工厂函数中,并使用 type 手动构建类。

def make_testcase(filename, myfunc, docstring):
    def test_something(self):
        data = loadmat(filename)
        result = myfunc(data)
        self.assertTrue(result > 0)

    clsdict = {'test_something': test_something,
               '__doc__': docstring}
    return type('ATest', (unittest.TestCase,), clsdict)

MyTest = makeTestCase('some_filename', my_func, 'This is a docstring')

12

只需使用装饰器即可。以下是您的情况:

def add_doc(value):
    def _doc(func):
        func.__doc__ = value
        return func
    return _doc

import unittest
def makeTestCase(filename, my_func):
    class ATest(unittest.TestCase):
        @add_doc('This should be my docstring')
        def testSomething(self):
            # Running test in here with data in filename and function my_func
            data  = loadmat(filename)
            result = my_func(data)
            self.assertTrue(result > 0)

    return ATest

def my_func(): pass

MyTest = makeTestCase('some_filename', my_func)
print MyTest.testSomething.__doc__
> 'This should be my docstring'

这里有一个类似的用例:Python 动态帮助和自动完成生成


11

除了 type 类型的类的 __doc__ 属性不能被更改之外,这是一个补充事实。有趣的一点是,只要使用 type 创建类就是如此。一旦您使用元类,您实际上可以更改 __doc__

该示例使用 ABCMeta 元类,它是通过 abc(AbstractBaseClass)模块工作的。

import abc

class MyNewClass(object):
    __metaclass__ = abc.ABCMeta

MyClass.__doc__ = "Changing the docstring works !"

help(MyNewClass)

会导致

"""
Help on class MyNewClass in module __main__:

class MyNewClass(__builtin__.object)
 |  Changing the docstring works !
"""

5

__doc__ 属性只有当对象的类型为 'type' 时才不可写。

在您的情况下,add_three 是一个函数,您可以将 __doc__ 设置为任何字符串。


0

如果你想要自动生成unittest.TestCase子类,你可以尝试重写shortDescription方法。

这个方法会将底层的文档字符串截取到第一行,正如在普通的unittest输出中所看到的;重写它就足以让我们控制报告工具(如TeamCity)中显示的内容,这正是我们所需要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接