Como documentar atributos de classe em Python? [fechadas]

115

Estou escrevendo uma classe leve cujos atributos devem ser publicamente acessíveis e apenas às vezes substituídos em instanciações específicas. Não há provisão na linguagem Python para a criação de docstrings para atributos de classe, ou qualquer tipo de atributo, para esse assunto. Qual é a forma esperada e suportada, caso haja uma, para documentar esses atributos? Atualmente estou fazendo este tipo de coisa:

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)

Isso resultará em docstring da classe contendo a seção docstring padrão inicial, bem como as linhas adicionadas para cada atributo por meio de atribuição aumentada para __doc__.

Embora esse estilo não pareça ser expressamente proibido nas diretrizes de estilo do docstring , ele também não é mencionado como uma opção. A vantagem aqui é que ele fornece uma maneira de documentar os atributos junto com suas definições, enquanto ainda cria uma docstring de classe apresentável e evita a necessidade de escrever comentários que reiteram as informações da docstring. Ainda estou meio chateado por ter de escrever os atributos duas vezes; Estou pensando em usar as representações de string dos valores no docstring para, pelo menos, evitar a duplicação dos valores padrão.

Esta é uma violação hedionda das convenções da comunidade ad hoc? Está tudo bem? Existe uma maneira melhor? Por exemplo, é possível criar um dicionário contendo valores e docstrings para os atributos e depois adicionar o conteúdo à classe __dict__e docstring no final da declaração da classe; isso aliviaria a necessidade de digitar os nomes e valores dos atributos duas vezes. editar : esta última ideia, eu acho, não é realmente possível, pelo menos não sem construir dinamicamente a classe inteira a partir de dados, o que parece uma ideia realmente ruim, a menos que haja algum outro motivo para fazer isso.

Eu sou muito novo em Python e ainda estou trabalhando nos detalhes do estilo de codificação, então críticas não relacionadas também são bem-vindas.

intuído
fonte
Se você está procurando uma maneira de documentar os atributos do modelo Django, isso pode ser útil: djangosnippets.org/snippets/2533
Michael Scheper
3
Duplicado de como documentar campos e propriedades em Python? que contém uma solução diferente.
bufh
1
Eu não entendo por que isso é baseado em opinião. Python documenta especificamente suas convenções aceitáveis ​​em PEPs. Existem diferentes ferramentas de origem Python que extraem documentação devidamente formatada. Na verdade, o Python tem um attribute doc stringmencionado no PEP 257 que não é muito conhecido e parece difícil de encontrar, que pode responder à pergunta dos OPs e é suportado por algumas ferramentas de origem. Isso não é opinião. É um fato, parte da linguagem e praticamente exatamente o que o OP deseja.
NeilG

Respostas:

83

Para evitar confusão: o termo propriedade tem um significado específico em python. Você está falando sobre o que chamamos de atributos de classe . Uma vez que eles sempre recebem ações por meio de sua classe, acho que faz sentido documentá-los dentro da string doc da classe. Algo assim:

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"

Acho que é muito mais fácil para os olhos do que a abordagem em seu exemplo. Se eu realmente quisesse que uma cópia dos valores dos atributos aparecesse na string doc, eu os colocaria ao lado ou abaixo da descrição de cada atributo.

Lembre-se de que, em Python, as strings de doc são membros reais dos objetos que documentam, não apenas anotações de código-fonte. Uma vez que as variáveis ​​de atributo de classe não são objetos em si, mas referências a objetos, elas não têm como manter sequências de documentos próprias. Eu acho que você poderia fazer um caso para sequências de documentos em referências, talvez para descrever "o que deve estar aqui" em vez de "o que está realmente aqui", mas acho fácil fazer isso na sequência de documentos de classe que contém.

ʇsәɹoɈ
fonte
Eu acho que na maioria dos casos isso é bom, uma vez que os atributos - obrigado pela correção de terminologia - são sucintamente declarados que podem ser agrupados apenas no início da declaração de classe, sem tornar impraticável alternar entre {leia ambos a documentação e o valor padrão} ou {atualizar ambas as instâncias da documentação e / ou valor padrão}.
intuído em
1
Observe também que meu exemplo fará com que a documentação dos atributos apareça na docstring da classe. Na verdade, eu preferiria colocar a documentação em docstrings dos próprios atributos, mas isso não funciona para a maioria dos tipos internos.
intuído em
Sim, minha ideia inicial era apenas declarar, por exemplo flight_speed = 691; flight_speed.__doc__ = "blah blah". Acho que é isso que você está mencionando em sua edição . Infelizmente, isso não funciona para instanciações de (a maioria?) Tipos internos (como intnaquele exemplo). Ele funciona para instanciações de tipos definidos pelo usuário. =========== Na verdade, havia um PEP (desculpe, esqueça o número) que propôs adicionar docstrings para atributos de classe / módulo, mas foi recusado porque eles não conseguiram descobrir uma maneira de deixar isso claro se os docstrings eram para os atributos anteriores ou seguintes.
intuído em
2
e daí se eles forem atributos de instância? ainda documenta na classe docstring ou o quê?
n611x007
1
@intuited Foi este PEP? legacy.python.org/dev/peps/pep-0224
taz
30

Você cita o PEP257: Convenções de Docstring, na seção O que é uma docstring está declarado:

Literais de string que ocorrem em outro lugar no código Python também podem atuar como documentação. Eles não são reconhecidos pelo compilador de bytecode Python e não são acessíveis como atributos de objeto de tempo de execução (ou seja, não atribuídos a __doc__), mas dois tipos de docstrings extras podem ser extraídos por ferramentas de software:

Literais de string que ocorrem imediatamente após uma atribuição simples no nível superior de um módulo, classe ou método __init__ são chamados de "docstrings de atributo".

E isso é explicado com mais detalhes em PEP 258: docstrings de atributos. Conforme explicado acima ʇsәɹoɈ. um atributo não é um objeto que pode possuir um __doc__, então eles não aparecerão emhelp() ou pydoc. Essas docstrings só podem ser usadas para documentação gerada.

Eles são usados ​​no Sphinx com a diretiva autoattribute

O Sphinx pode usar comentários em uma linha antes de uma atribuição ou um comentário especial após uma atribuição ou uma docstring após a definição que será autodocumentada.

Marcz
fonte
1
O plugin jedi-vim também reconhece docstrings de atributos.
Long Vu
1
Não sei quando isso foi introduzido, mas o Sphinx 1.2.2 parece incluir docstrings de atributos na documentação gerada.
jochen
1
Obrigado @jochen, atualizo minha resposta.
marcz 01 de
3
Observe que o PEP 258 foi rejeitado. O aviso de rejeição afirma: "Embora isso possa servir como um documento de design interessante para os docutils agora independentes, não está mais programado para inclusão na biblioteca padrão."
Michał Łazowik
13

Você pode abusar das propriedades para esse efeito. As propriedades contêm um getter, um setter, um deleter e um docstring . Ingenuamente, isso ficaria muito prolixo:

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

Então você terá uma docstring pertencente a Cx:

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

Fazer isso para muitos atributos é complicado, mas você poderia imaginar uma função auxiliar 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!

Então, chamar o Pythons interativo helpdará:

Help on class C in module __main__:

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

que eu acho que deve ser o que você está procurando.

Edit : Percebo agora que talvez possamos evitar a necessidade de passar o primeiro argumento para myprop, porque o nome interno não importa. Se as chamadas subsequentes de myproppuderem se comunicar de alguma forma, ele poderá decidir automaticamente sobre um nome de atributo interno longo e improvável. Tenho certeza de que existem maneiras de implementar isso, mas não tenho certeza se valem a pena.

gerrit
fonte