如何在Python中记录类属性?[关闭]


115

我正在编写一个轻量级的类,其属性旨在可公开访问,并且有时仅在特定的实例中被覆盖。就此而言,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)

这将导致该类的docstring包含初始的标准docstring部分,以及通过对的扩展分配为每个属性添加的行__doc__

尽管docstring样式指南中似乎并未明确禁止使用这种样式,但也没有提到它是一种选择。这样做的好处是,它提供了一种在定义时连同属性一起记录属性的方法,同时仍然创建了一个可显示的类docstring,并且避免了编写注释以重申该docstring中的信息。我仍然对必须两次写入属性感到恼火。我正在考虑使用文档字符串中值的字符串表示形式来至少避免重复默认值。

这是对特设社区惯例的严重违反吗?可以吗 有没有更好的办法?例如,可以创建一个包含属性值和文档字符串的字典,然后__dict__在类声明的末尾将内容添加到该类和文档字符串中。这样可以减少两次键入属性名称和值的需要。 编辑:我认为,最后一个想法实际上是不可能的,至少没有没有根据数据动态构建整个类的想法,除非有其他原因,否则这似乎是一个糟糕的主意。

我是python的新手,仍然在研究编码风格的细节,因此也欢迎无关的批评。


如果您正在寻找一种记录Django模型属性的方法,这可能会有所帮助:djangosnippets.org/snippets/2533
Michael Scheper

3
如何在Python中记录字段和属性的副本拥有不同的解决方案。
bufh

1
我不明白为什么这是基于意见的。Python专门记录了PEP中可接受的约定。有多种Python源工具可以提取格式正确的文档。实际上,Python实际上attribute doc stringPEP 257中有一个鲜为人知的提及,并且似乎很难找到它可以回答OP的问题,并且受到某些源工具的支持。这不是意见。这是事实,也是语言的一部分,几乎完全是OP想要的。
NeilG

Answers:


82

为避免混淆:术语property在python中具有特定含义。您所说的是所谓的类属性。由于始终在类中对它们进行操作,因此我发现将它们记录在类的文档字符串中是有意义的。像这样:

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字符串中这样做很容易。


我猜在大多数情况下还可以,因为属性(感谢术语更正)已经足够简洁地声明为可以在类声明的开始处进行分组,而又不会使来回切换到任何一个都不切实际。文档和默认值}或{同时更新文档和/或默认值的两个实例}。
直觉

1
还要注意,我的示例将导致属性的文档出现在类的文档字符串中。我实际上更希望将文档放在属性本身的文档字符串中,但这不适用于大多数内置类型。
直觉

是的,我最初的想法是声明例如flight_speed = 691; flight_speed.__doc__ = "blah blah"。我认为这就是您在编辑中提到的内容。不幸的是,这对于(大多数?)内置类型的实例化无效(如int该示例中所示)。它确实适用于实例化用户定义的类型。============实际上有一个PEP(对不起,忘记了数字)建议为类/模块属性添加文档字符串,但被拒绝了,因为它们无法找出一种清晰的方法该文档字符串是用于先前还是之后的属性。
直觉

2
那么如果它们是实例属性呢?还在类文档字符串中还是文档?
n611x007 2015年

1
@intuited这是PEP吗?legacy.python.org/dev/peps/pep-0224
taz

30

您在“ 什么是文档字符串 ”部分中引用了PEP257:文档字符串约定

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

在模块,类或__init__方法的顶级进行简单赋值后立即出现的字符串文字称为“属性文档字符串”。

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

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

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


1
jedi-vim插件还可以识别属性文档字符串。
Long Vu 2013年

1
我不知道什么时候引入的,但是Sphinx 1.2.2似乎在生成的文档中包括属性docstrings。
jochen 2014年

1
谢谢@jochen,我更新了我的答案。
marcz 2014年

3
请注意,PEP 258被拒绝。拒绝通知指出:“尽管这可能是现在独立的文档的有趣的设计文档,但不再打算将其包含在标准库中。”
米沙乌·Łazowik,

13

您可以滥用此属性。属性包含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

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

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

要对许多属性执行此操作比较麻烦,但是您可以设想一个辅助函数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可以通过某种方式彼此通信,则它可以自动确定一个较长且不太可能的内部属性名称。我敢肯定有实现此目的的方法,但是我不确定他们是否值得。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.