如何在Python中记录类属性?

How to document class attributes in Python?

我正在编写一个轻量级类,它的属性旨在公开访问,并且有时只在特定的实例化中被重写。在Python语言中没有为类属性或任何类型的属性创建docstring的规定。应该有一种公认的方法来记录这些属性吗?目前我正在做这种事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Albatross(object):
   """A bird with a flight speed exceeding that of an unladen swallow.

    Attributes:
   """


    flight_speed = 691
    __doc__ +="""
        flight_speed (691)
          The maximum speed that such a bird can attain.
   """


    nesting_grounds ="Raymond Luxury-Yacht"
    __doc__ +="""
        nesting_grounds ("Raymond Luxury-Yacht")
          The locale where these birds congregate to reproduce.
   """


    def __init__(self, **keyargs):
       """Initialize the Albatross from the keyword arguments."""
        self.__dict__.update(keyargs)

这将导致类的docstring包含初始标准docstring部分,以及通过对__doc__的增强赋值为每个属性添加的行。

尽管在docstring样式指南中似乎没有明确禁止这种样式,但也没有将其作为一种选项提及。这里的优点是,它提供了一种将属性与其定义一起记录的方法,同时仍然创建一个可显示的类docstring,并且避免了编写重复docstring中信息的注释。我仍然有点恼火,因为我必须将属性实际写入两次;我正在考虑使用docstring中值的字符串表示,以至少避免默认值的重复。

这是严重违反特别社区公约吗?没事吧?有更好的方法吗?例如,可以为属性创建一个包含值和docstring的字典,然后在类声明的末尾向类__dict__和docstring添加内容;这样可以减少两次输入属性名称和值的需要。编辑:我认为,最后一个想法实际上是不可能的,至少在没有动态地从数据构建整个类的情况下是不可能的,这看起来是一个非常糟糕的想法,除非有其他原因。

我对python还很陌生,仍然在研究编码风格的细节,因此不相关的评论也很受欢迎。


为了避免混淆:术语属性在Python中有特定的含义。你说的是我们所谓的类属性。因为它们总是通过类进行操作,所以我发现在类的文档字符串中记录它们是有意义的。像这样:

1
2
3
4
5
6
7
8
9
class Albatross(object):
   """A bird with a flight speed exceeding that of an unladen swallow.

    Attributes:
        flight_speed     The maximum speed that such a bird can attain.
        nesting_grounds  The locale where these birds congregate to reproduce.
   """

    flight_speed = 691
    nesting_grounds ="Throatwarbler Man Grove"

我认为这在眼睛上比你例子中的方法容易得多。如果我真的希望属性值的副本出现在文档字符串中,我会将它们放在每个属性描述的旁边或下面。

请记住,在Python中,文档字符串是它们所记录对象的实际成员,而不仅仅是源代码注释。由于类属性变量不是对象本身,而是对对象的引用,因此它们无法保存自己的文档字符串。我想你可以为引用上的文档字符串做一个例子,也许描述"应该在这里做什么"而不是"实际在这里做什么",但是我发现在包含类文档字符串中这样做很容易。


在"什么是docstring"一节中,您引用了pep257:docstring约定:

String literals occurring elsewhere in Python code may also act as documentation. They are not recognized by the Python bytecode compiler and are not accessible as runtime object attributes (i.e. not assigned to __doc__), but two types of extra docstrings may be extracted by software tools:

String literals occurring immediately after a simple assignment at the top level of a module, class, or __init__ method are called"attribute docstrings".

在PEP258:attribute docstrings中可以更详细地解释这一点。如上所述?S???.属性不是可以拥有文档的对象,因此它们不会出现在help()或pydoc中。这些文档字符串只能用于生成的文档。

它们在具有指令autoattribute的sphinx中使用

sphinx可以在赋值前的行上使用注释,或者在赋值后使用特殊注释,或者在将自动记录的定义后使用docstring。


你可以滥用财产达到这种效果。属性包含getter、setter、deleter和docstring。天真地说,这将变得非常冗长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
       """Docstring goes here."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

然后您将拥有属于c.x的docstring:

1
2
In [24]: print(C.x.__doc__)
Docstring goes here.

对许多属性执行此操作很麻烦,但您可以设想一个助手函数myprop:

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
def myprop(x, doc):
    def getx(self):
        return getattr(self, '_' + x)

    def setx(self, val):
        setattr(self, '_' + x, val)

    def delx(self):
        delattr(self, '_' + x)

    return property(getx, setx, delx, doc)

class C:
    a = myprop("a","Hi, I'm A!")
    b = myprop("b","Hi, I'm B!")

In [44]: c = C()

In [46]: c.b = 42

In [47]: c.b
Out[47]: 42

In [49]: print(C.b.__doc__)
Hi, I'm B!

然后,调用pythons interactive help将给出:

1
2
3
4
5
6
7
8
9
10
Help on class C in module __main__:

class C
 |  Data descriptors defined here:
 |  
 |  a
 |      Hi, I'm A!
 |  
 |  b
 |      Hi, I'
m B!

我认为这应该是你想要的。

编辑:我现在意识到,也许我们可以避免将第一个参数传递给myprop,因为内部名称并不重要。如果随后的myprop调用可以以某种方式相互通信,它可以自动决定一个长的、不太可能的内部属性名。我确信有很多方法可以实现这一点,但我不确定它们是否值得。