【问题标题】:Change the docstring of a class method within a decorator based on class attribute根据类属性更改装饰器中类方法的文档字符串
【发布时间】:2026-02-03 08:25:01
【问题描述】:

让我们从这个开始:

class Example(object):

    change_docstring = True

    @add_to_docstring(" (additional content!)")
    def example_method(self):
        """Example docstring."""
        pass

我想要做的是允许@add_to_docstring 装饰器将其参数字符串附加到方法的文档字符串中987654326@。我确实想将其他任何东西传递给装饰器。

此解决方案有效,但不是我想要的。

def add_to_docstring(text):

    def decorator(original_method):

        def wrapper(self):
            """wrapper docstring."""
            wrapper.__doc__ = original_method.__doc__

            if self.change_docstring:
                wrapper.__doc__ += text

            return original_method(self)

        return wrapper

    return decorator

让我解释一下。

上述解决方案仅在执行example_method 时更改文档字符串。加载类、方法等时,文档字符串不会更改。

>>> Example.example_method.__doc__
"wrapper docstring."
>>>
>>> Example().example_method()
>>> Example.example_method.__doc__
"Example docstring. (additional content!)"

这就是我希望上述命令的输出:

>>> Example.example_method.__doc__
"Example docstring. (additional content!)"

同样,我不想将任何其他内容传递给装饰器。

更新

为了进一步说明,这是为了允许装饰器更改方法的文档字符串,并将该更改反映在 Sphinx 生成的文档中。 Sphinx 加载所有内容并收集文档字符串,但它没有做任何其他事情。

基于选择的解决方案,我在装饰器模块中添加了一个模块变量,并公开了一种方法来禁用装饰器中的文档字符串更改功能。为了普遍禁用该功能,我随后在我的 Sphinx conf.py 文件中调用了该禁用函数,如下所示:

# import the decorators module
from some_modules import decorators
# disable the docstring change feature
decorators.disable_docstring_change()

然后装饰器可以用于项目中的任何方法,文档字符串的更改将被启用或禁用。

【问题讨论】:

  • 由于类作用域的工作方式,我认为这是不可能的(不将change_docstring 传递给装饰器,或使其成为全局对象)。有关详细信息,请参阅here。另见*.com/questions/47223764/…
  • 在很可能不可能的情况下,你有什么建议让装饰者可以使用change_docstring?本质上,它是一个应该默认为 False 的设置。我只是希望将设置作为类的属性以使其易于设置。
  • 如果change_docstring 未设置或为假,为什么不能省略装饰器?
  • 在这种情况下,装饰器不会做任何其他事情,但在实际情况下,文档字符串的更改将只是装饰器逻辑的一部分。
  • 不,您不能在那里访问类本身,因为在执行其定义之前,该类不作为对象存在。

标签: python python-sphinx python-decorators


【解决方案1】:

装饰和标记方法

这里我们不需要太关心函数签名,无论它是绑定的还是未绑定的——我们只是在函数对象上放置一个带有附加文本的属性。

def add_to_docstring(text):
    def func(f):
        f.__add_to_docstring = text
        return f
    return func

装饰类以表明我们希望标记方法得到尊重

通过使用类装饰器,我们可以表明我们希望尊重标记的方法并更改文档字符串。我们扫描可调用对象,检查它们是否是包含要添加到文档字符串中的内容的修饰对象,并在返回具有不同函数文档字符串的新类型之前进行适当的更改。

def change_docstrings(cls):
    for obj in vars(cls).values():
        if callable(obj) and hasattr(obj, '__add_to_docstring'):
            obj.__doc__ = (obj.__doc__ or '') + obj.__add_to_docstring
            del obj.__add_to_docstring
    return cls

把它们放在一起

@change_docstrings
class Example:
    @add_to_docstring('(cabbage!)')
    def example(self):
        """ something here """
        pass

检查 Example.example.__doc__ 我们得到 - ' something here (cabbage!)' 并且如果您删除 @change_docstrings 类装饰器 - 您不会得到任何更改。

请注意,这会将change_docstrings 移出类以及是否进行装饰,但是,它允许以下构造:

unchanged_docstrings = Example
changed_docstrings = change_docstrings(Example)

【讨论】:

    【解决方案2】:

    正如Martijn Pieter's answer“从类定义中的列表理解访问类变量”中所述,如果您在类的新范围内,则无法访问类属性。该答案主要关注类范围内的理解和生成器表达式,但同样适用于普通函数,包括装饰器。

    解决此问题的一种简单方法是将change_docstring 设为全局,并在类之前定义它,以便您可以轻松地逐个类地设置它。另一种选择是让它成为装饰器的参数,但你说你不想这样做。这是一个适用于 Python 2 和 3 的简短演示。

    def add_to_docstring(text):
        def decorator(original_method):
            def wrapper(self):
                return original_method(self)
            wrapper.__doc__ = original_method.__doc__
            if change_docstring:
                wrapper.__doc__ += text
            return wrapper
        return decorator
    
    change_docstring = True
    class Example(object):
        @add_to_docstring(" (additional content!)")
        def example_method(self):
            """Example docstring."""
            pass
    
    change_docstring = False
    class Other(object):
        @add_to_docstring(" (more content!)")
        def example_method(self):
            """Other docstring."""
            pass
    
    print(Example.example_method.__doc__)
    print(Other.example_method.__doc__)
    

    输出

    Example docstring. (additional content!)
    Other docstring.
    

    【讨论】:

    • 我喜欢这个答案和 Jon Clements 的答案,但这最终是我最终的想法。在此示例之外的特定情况下,我将其设置为模块变量并设置了一个函数来更改默认值。这样,我可以将值设置一次以应用于其方法被修饰的所有类。与此示例相关的区别是可以选择在类上方添加from module import decoratorsdecorators.disable_docstring_change() 行。