How to modify class docstrings using classmethods
我已经创建了一系列节点,每个节点都有一组与它们相关联的属性对象。属性对象是用每个属性的描述和名称初始化的。我希望这些属性及其描述能够显示在我的sphinx文档中,而不必在两个位置维护名称/描述,一个在类的文档字符串中,另一个在属性的初始化中。
要说明问题,请考虑以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Foo(object): """My doc string """ @classmethod def default_attributes(cls): return {'foo':'description of foo attribute', 'bar':'description of bar attribute'} @classmethod def attributes_string(cls): attributes = cls.default_attributes() result = ' Default Attributes: ' for key, value in attributes.iteritems(): result += '%s: %s ' % (key, value) return result print Foo.__doc__ |
我希望foo.attributes_string的结果显示在foo的文档字符串中,这样我可以得到:
1 2 3 4 5 | My doc string Default Attributes: foo: description of foo attribute bar: description of bar attribute |
我的解决方案尝试:
首先我想"嘿,那很简单!我来安排一个班级装饰员!":
1 2 3 4 5 6 7 8 9 10 | def my_decorator(cls): doc = getattr(cls, '__doc__', '') doc += cls.attributes_string() cls.__doc__ = doc return cls @my_decorator class Foo(object): """My doc string """ |
此操作因以下错误而失败:
1 | AttributeError: attribute '__doc__' of 'type' objects is not writable |
于是我想,"好吧,那我就用一个元类在类被创建之前设置好文档!".当我开始实现这一点时,我立即遇到了一个问题:对于尚未创建的类,如何调用ClassMethod?
我通过一个让我感到不安的非常简单的变通方法规避了这个问题:我创建了两次类,一次不修改它,这样我就可以称它为ClassMethod,然后再次使用适当的文档创建类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Meta(type): def __new__(meta_cls, name, bases, cls_dict): tmpcls = super(Meta, meta_cls).__new__(meta_cls, name, bases, cls_dict) doc = cls_dict.get('__doc__', '') doc += tmpcls.attributes_string() cls_dict['__doc__'] = doc return super(Meta, meta_cls).__new__(meta_cls, name, bases, cls_dict) class Foo(object): """My doc string """ __metaclass__ = Meta |
这完全有效,并且给了我我想要的结果:
1 2 3 4 5 | My doc string Default Attributes: foo: description of foo attribute bar: description of bar attribute |
然而,两次创建类不是非常低效吗?重要吗?有更好的方法吗?我想做的事真的很蠢吗?
做了一些四处寻找你,没有骰子,因为它是一个基本上打破,并没有修复,直到Python3.3。因此,如果您计划发布大于3.3的程序,那么
然而,这似乎对您没有帮助,但是有一些方法可以更巧妙地将这个问题解决,使其提交,只需在元类中提供一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class Meta(type): @property def __doc__(self): return self.attributes_string() class Foo(object): """My doc string """ __metaclass__ = Meta @classmethod def default_attributes(cls): return {'foo':'description of foo attribute', 'bar':'description of bar attribute'} @classmethod def attributes_string(cls): attributes = cls.default_attributes() result = ' Default Attributes: ' for key, value in attributes.items(): result += '%s: %s ' % (key, value) return result |
不需要破坏
1 2 3 4 5 6 7 8 9 10 | CLASSES __builtin__.object Foo __builtin__.type(__builtin__.object) Meta class Foo(__builtin__.object) | Default Attributes: | foo: description of foo attribute | bar: description of bar attribute |
在python 2.7下测试。
注意:它将覆盖声明docstring的标准方法,因此您可能必须将整个内容放在其中,除非您碰巧也覆盖了
1 2 3 4 5 6 7 8 9 | class Meta(type): def __new__(cls, name, bases, attrs): attrs['_doc'] = attrs.get('__doc__', '') return super(Meta, cls).__new__(cls, name, bases, attrs) @property def __doc__(self): return self._doc + self.attributes_string() |
你的结果是:
1 2 3 4 5 6 | class Foo(__builtin__.object) | My doc string | | Default Attributes: | foo: description of foo attribute | bar: description of bar attribute |
如果要为未注释的函数生成docstring,也可以使用pyment。
它不会提供您所期望的特定格式,但目前它会生成以sphinxs、numpydoc或google-doc样式添加(或转换)docstring格式的补丁。