如何使用装饰器自动记录我的类属性

时间:2014-06-25 10:27:55

标签: python python-decorators docstring

假设我有两个类(A& B),其中第二个类派生自第一个类。此外,我通过在B中编写新实现来隐藏A中实现的一些属性。但是,我为A编写的文档字符串仍然适用于B而我懒惰 - 我不想复制粘贴一切。

请注意,这里的一个关键问题是我的目标是属性,对于类方法一般有解决方案。

在代码中,类的最小示例可能如下所示:

In [1]: class A(object):
   ...:     @property
   ...:     def a(self):
   ...:         "The a:ness of it"
   ...:         return True
   ...:     

In [2]: class B(A):
   ...:     @property
   ...:     def a(self):
   ...:         return False
   ...:     

我基本上想要的是有办法实现以下目标:

In [8]: B.a.__doc__
Out[8]: 'The a:ness of it'

实际上B.a的文档字符串是空的,写B.a.__doc__是不可能的,因为它会引发TypeError

据我所知,以下是一个解决方案:

from inspect import getmro

def inheritDocFromA(f):
    """This decorator will copy the docstring from ``A`` for the
    matching function ``f`` if such exists and no docstring has been added
    manually.
    """
    if f.__doc__ is None:

        fname = f.__name__

        for c in getmro(A):
            if hasattr(c, fname):
                d = getattr(c, fname).__doc__
                if d is not None:
                    f.__doc__ = d
                    break

    return f

确实有效,但由于A被硬编码到装饰器函数中,因为我找到了 无法知道传递给装饰器时附加了哪个类f

In [15]: class C(A):
   ....:     @property
   ....:     @inheritDocFromA
   ....:     def a(self):
   ....:         return False
   ....:     

In [16]: C.a.__doc__
Out[16]: 'The a:ness of it'

问题: 是否有可能为类属性构建一个decorator应用文档字符串的通用解决方案而不需要在继承到装饰器函数中进行硬编码?

我也尝试过装饰课程,但后来我发现了文档字符串被写保护。

最后,如果可能,我希望解决方案适用于Python 2.7和3.4。

1 个答案:

答案 0 :(得分:1)

可以编写一个装饰器,当通过类访问时返回正确的__doc__ - 毕竟,__get__接收类型并可以通过其MRO,找到合适的__doc__ ,并将其设置为自身(或在为此目的创建和返回的代理上)。但解决__doc__不可写的问题要简单得多。

事实证明,由于property是作为一种类型实现的,因此将其实例的__doc__写为可写就像从它继承一样简单:

class property_writable_doc(property):
    pass

然后,您使用类装饰器继承__doc__属性的想法可以起作用:

def inherit_doc_class(cls):
    for name, obj in cls.__dict__.iteritems():
        if isinstance(obj, property_writable_doc) and obj.__doc__ is None:
            for t in cls.__mro__:
                if name in t.__dict__ and t.__dict__[name].__doc__ is not None:
                    obj.__doc__ = t.__dict__[name].__doc__
                    break
    return cls

class A(object):
    @property
    def a(self):
        "The a:ness of it"
        return True

@inherit_doc_class
class B(A):
    @property_writable_doc
    def a(self):
        return False

@inherit_doc_class
class C(A):
    @property_writable_doc
    def a(self):
        "The C:ness of it"
        return False