【问题标题】:What should I do with the docstring of a decorated function if the decorator changes the API?如果装饰器更改了 API,我应该如何处理装饰函数的文档字符串?
【发布时间】:2012-12-18 17:20:47
【问题描述】:

背景

我有一个库,其中包含多个读取或写入文件的函数。每个函数都接受文件作为第一个参数,可以是文件对象,也可以是文件名。因此,所有函数的开头都有相同的代码,类似于以下内容:

if isinstance(f, str):
    file_obj = open(f, 'w')
else:
    file_obj = f

现在我想我可以在装饰器中编写一次并将所有功能包装在其中,而不是重复自己。 (我也在考虑在同一个装饰器中实现上下文管理器接口。)

所以如果我这样做,函数将如下所示:

@file_aware('w')
def function(f, *args, **kwargs):
    """Do stuff. `f` can be file object or file name"""
    for line in f:
       ....

问题

我担心现在函数的文档字符串与它下面的代码不对应。 (我打算用functools.wraps 保留装饰函数的文档字符串。) 它会降低代码的可读性/可维护性/透明度吗? 据我了解,装饰器可以很容易地来来去去,但与此同时,这有点改变了 API(我不打算删除该功能)。这种情况下的“最佳实践”方式是什么?

我可以考虑在装饰器中自动处理文档字符串,但是:

  • 这不是最自然的事情;
  • 这仅对在线文档有意义,不会帮助(相反)阅读源代码的人。

【问题讨论】:

  • 我不认为更改文档字符串比 functools.wraps() 更改之类的其他内容更糟糕——所以去吧。
  • @martineau 你的意思是改变装饰器中的__doc__?但是,“真正的”文档字符串(以及代码的某些部分)将看不到阅读函数源的人,您不认为这是一件坏事吗?
  • 是的,我就是这个意思。装饰器可以在装饰函数的现有文档字符串中添加(预先或附加)一些东西。
  • @martineau 如果你能告诉我它在代码可读性或可维护性方面是合理的,那就太好了。
  • 阅读源代码的人会看到正在应用的装饰器并可能阅读了它的文档。文档工具将使用修改后的文档字符串并显示它。听起来它可以减少一定数量的样板编码和记录——它是否合理是一个判断,取决于装饰器的作用和使用频率。

标签: python api decorator docstring


【解决方案1】:

一种选择是将文档字符串传递给装饰器。这样,您仍然可以通过函数定义获得一个文档字符串来读取源代码,但是如果您要更改或删除装饰器,您最终不会得到一个不正确的文档字符串。

例如:

@file_aware(docstring="Do stuff. `f` can be file object or file name", mode="r")
def function(f, *args, **kwargs):
    for line in f:
       ....

你的 file_aware 装饰器可能看起来像这样:

def file_aware(docstring, mode):
    def deco(func):
        @functools.wraps(func)
        def wrapped(f, *args, **kwargs):
            if isinstance(f, str):
                file_obj = open(f, mode)
            else:
                file_obj = f
            return func(file_obj, *args, **kwargs)
        wrapped.__doc__ = docstring
        return wrapped
    return deco

【讨论】:

  • 这听起来不错,谢谢。答案是正确的,所以它可以帮助我更好地理解我真正想要实现的目标。附言mode 应该是装饰器的参数,因为不同的函数使用不同的模式。
猜你喜欢
  • 2010-12-19
  • 1970-01-01
  • 2020-06-08
  • 2022-08-15
  • 1970-01-01
  • 2016-09-27
  • 2023-03-18
  • 2015-03-22
  • 2022-10-18
相关资源
最近更新 更多