如何在Python中记录类属性?

int*_*ted 101 python documentation docstring class class-attributes

我正在编写一个轻量级类,其属性旨在可公开访问,并且有时仅在特定实例中被覆盖.在Python语言中没有为类属性或任何类型的属性创建文档字符串的规定.记录这些属性的可接受方式是什么?目前我正在做这样的事情:

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)
Run Code Online (Sandbox Code Playgroud)

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

虽然在文档字符串样式指南中似乎没有明确禁止这种样式,但它也没有作为选项提及.这里的优点是它提供了一种方法来记录属性及其定义,同时仍然创建一个可呈现的类docstring,并避免编写重复来自docstring的信息的注释.我仍然有点生气,我必须实际写两次属性; 我正在考虑使用docstring中值的字符串表示来至少避免重复默认值.

这是否是对特设社区公约的毁灭性违反?好吗?有没有更好的办法?例如,可以创建包含属性值和文档字符串的字典,然后__dict__在类声明的末尾将内容添加到类和docstring中; 这样可以减少两次输入属性名称和值的需要. 编辑:我认为,这最后一个想法实际上是不可能的,至少不是没有从数据动态构建整个类,这似乎是一个非常糟糕的想法,除非有其他理由这样做.

我对python很新,并且仍在研究编码风格的细节,所以也欢迎无关的批评.

ʇsә*_*ɹoɈ 75

为避免混淆:术语属性在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"
Run Code Online (Sandbox Code Playgroud)

我认为眼睛比你的例子中的方法容易得多.如果我真的想要在doc字符串中显示属性值的副本,我会将它们放在每个属性的描述旁边或下面.

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

  • 那么如果它们是实例属性呢?仍然在类docstring中的文档或什么? (2认同)
  • @intuited 这是 PEP 吗?http://legacy.python.org/dev/peps/pep-0224/ (2认同)

Tim*_*mmm 54

其他答案已经非常过时了。PEP-257描述了如何使用属性的文档字符串。奇怪的是,他们在属性之后出现:

\n
\n

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

\n
    \n
  1. 在模块、类或 __init__ 方法顶层的简单赋值之后立即出现的字符串文字称为 \xe2\x80\x9cattribute docstrings\xe2\x80\x9d。
  2. \n
\n
\n
class C:\n    "class C doc-string"\n\n    a = 1\n    "attribute C.a doc-string (1)"\n\n    b = 2\n    "attribute C.b doc-string (2)"\n
Run Code Online (Sandbox Code Playgroud)\n

它也适用于这样的类型注释:

\n
class C:\n    "class C doc-string"\n\n    a: int\n    "attribute C.a doc-string (1)"\n\n    b: str\n    "attribute C.b doc-string (2)"\n
Run Code Online (Sandbox Code Playgroud)\n

VSCode 支持显示这些。

\n

  • 这种行为也记录在 PEP 257 中,因此它是标准的一部分:“在模块、类或 __init__ 方法顶层的简单赋值之后立即出现的字符串文字称为“属性文档字符串”。 ” (27认同)
  • PEP 224 被拒绝,但这个答案仍然有用,因为 Python 生态系统中的许多工具都支持这种定义属性文档字符串的方式。 (22认同)
  • 我不认为将文档字符串放在变量后面很奇怪,因为 Python 中的文档字符串总是位于:`def`、`class`、模块文件开始之后。(我承认在其他编程语言中这可能有所不同。)我非常欢迎对它们的官方支持。 (4认同)
  • 哦,是的,我错过了。然而,这只是一个惯例,而且似乎得到了广泛的支持,尽管 Guido 说他不喜欢它,因为字符串在文档之后(这很奇怪)。例如[此处](https://developer.lsst.io/v/DM-15183/python/numpydoc.html#placement-of-constant-and-class-attribute-docstrings),[Sphinx 支持](https: //www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#directive-autoattribute)并且 Pylance 支持它。这是事实上的标准。 (3认同)
  • [mkdocstrings](https://mkdocstrings.github.io/)也支持它。 (3认同)
  • 将文档字符串放在属性后面确实看起来很奇怪,但我希望它可以防止该情况与为类或模块本身编写的文档字符串之间出现歧义。 (3认同)
  • @phiresky 不过似乎没有办法检索它们。`help(C)` 没有描述属性文档字符串。 (3认同)

mar*_*rcz 28

您引用了PEP257:文档字符串约定,它在什么是文档字符串中说明:

Python代码中其他地方出现的字符串文字也可以作为文档.它们不被Python字节码编译器识别,并且不能作为运行时对象属性访问(即未分配给__doc__),但软件工具可以提取两种类型的额外文档字符串:

在模块,类或__init__方法的顶层进行简单赋值后立即出现的字符串文字称为"属性docstrings".

这在PEP 258:属性文档字符串中有更详细的解释.如上所述ʇsәɹoɈ.属性不是可以拥有__doc__的对象,因此它们不会出现在help()pydoc中.这些文档字符串只能用于生成的文档.

它们在Sphinx中与指令autoattribute一起使用

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

  • 我不知道这是何时引入的,但 Sphinx 1.2.2 似乎在生成的文档中包含属性文档字符串。 (2认同)
  • 请注意,PEP 258被拒绝.拒绝通知声明:"虽然这可能是现在独立的文档的一个有趣的设计文档,但它不再被列入标准库." (2认同)

ger*_*rit 11

您可以滥用属性来达到此效果.属性包含getter,setter,deleter 和docstring.天真地,这将变得非常冗长:

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
Run Code Online (Sandbox Code Playgroud)

然后你将有一个属于Cx的文档字符串:

In [24]: print(C.x.__doc__)
Docstring goes here.
Run Code Online (Sandbox Code Playgroud)

为许多属性执行此操作非常麻烦,但您可以设想一个辅助函数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!
Run Code Online (Sandbox Code Playgroud)

然后,调用Pythons交互help将给出:

Help on class C in module __main__:

class C
 |  Data descriptors defined here:
 |  
 |  a
 |      Hi, I'm A!
 |  
 |  b
 |      Hi, I'm B!
Run Code Online (Sandbox Code Playgroud)

编辑:我现在意识到我们可以避免需要传递第一个参数myprop,因为内部名称无关紧要.如果后续调用myprop可以某种方式相互通信,它可以自动决定一个长且不太可能的内部属性名称.我确信有办法实现这一点,但我不确定它们是否值得.