如何在 Python 中记录类属性?

我正在编写一个轻量级类,它的属性应该是可公开访问的,并且只有在特定的实例化中才会被覆盖。Python 语言中没有为类属性或任何类型的属性创建 docstring 的规定。如果存在一种记录这些属性的预期和支持的方式,那么它是什么呢?目前我正在做这样的事情:

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 还很陌生,仍然在研究编码风格的细节,所以也欢迎不相关的批评。

104666 次浏览

为了避免混淆: 术语 财产在 python 中有一个 具体的意义。你所说的就是我们所说的 类属性。由于它们总是通过它们的类执行,我发现在类的 doc 字符串中记录它们是有意义的。就像这样:

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"

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

请记住,在 Python 中,doc 字符串是它们记录的对象的实际成员,而不仅仅是源代码注释。因为类属性变量本身不是对象,而是对对象的引用,所以它们无法保存自己的 doc 字符串。我想您可以对引用的 doc 字符串进行说明,也许是描述“ what should go here”而不是“ what is really here”,但是我发现在包含 doc 字符串的类中这样做很容易。

您引用了 PEP257: Docstring 约定,在 什么是 docstring节中指出:

Python 代码中其他地方出现的字符串文本也可以作为文档。它们不被 Python 字节码编译器识别,也不能作为运行时对象属性访问(例如,不赋给 _ _ doc _ _) ,但是软件工具可以提取两种类型的额外 docstring:

在模块、类或 _ _ init _ _ 方法的顶层的简单赋值之后立即出现的字符串文字称为“属性 docstring”。

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

它们在 Sphinx 中使用 < a href = “ https://www.Sphinx-doc.org/en/update/using/ autodoc.html # direct-autoAttribute”rel = “ norefrer”> direct autoAttribute

Sphinx 可以在赋值之前的一行使用注释,或者在赋值之后使用特殊注释,或者在定义之后使用 docstring,这些都将自动记录在文档中。

你可以这样滥用财产。属性包含 getter、 setter、 deleter 和 还有一个文档字符串。天真地说,这会变得非常冗长:

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:

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

对于许多属性来说,这样做是很麻烦的,但是您可以设想一个 helper 函数 myprop:

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!

然后,调用 Python 交互式 help将得到:

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的后续调用可以以某种方式相互通信,它可以自动决定一个冗长且不太可能的内部属性名。我相信有很多方法可以实现这一点,但我不确定这是否值得。

其他的答案都已经过时了。PEP-224(可在 Python 2.1中获得!)描述如何将 docstring 用于属性。他们来 之后的属性,奇怪。

class C:
"class C doc-string"


a = 1
"attribute C.a doc-string (1)"


b = 2
"attribute C.b doc-string (2)"

它也适用于类型注释,如下所示:

class C:
"class C doc-string"


a: int
"attribute C.a doc-string (1)"


b: str
"attribute C.b doc-string (2)"

VSCode 支持显示这些。