2010-06-16 24 views
72

Estoy escribiendo una clase ligera cuyos atributos están destinados a ser de acceso público, y solo a veces se anula en instancias específicas. No hay ninguna disposición en el lenguaje Python para crear cadenas de documentos para los atributos de clase, o cualquier tipo de atributos, para el caso. ¿Cuál es la forma aceptada, debería haber una, para documentar estos atributos? Actualmente estoy haciendo este tipo de cosas:¿Cómo documentar los atributos de clase en 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) 

Esto resultará en cadena de documentación de la clase que contiene la sección inicial estándar cadena de documentación, así como las líneas añadidas para cada atributo a través de la asignación aumentada para __doc__.

Aunque este estilo no parece estar expresamente prohibido en el docstring style guidelines, tampoco se menciona como una opción. La ventaja aquí es que proporciona una forma de documentar los atributos junto con sus definiciones, al mismo tiempo que crea una docstring de clase presentable, y evita tener que escribir comentarios que reiteran la información de la docstring. Todavía estoy un poco molesto de tener que escribir los atributos dos veces; Estoy considerando usar las representaciones de cadena de los valores en la docstring para evitar al menos la duplicación de los valores predeterminados.

¿Es esto una abyecta infracción de las convenciones comunitarias ad hoc? ¿Está bien? ¿Hay una mejor manera? Por ejemplo, es posible crear un diccionario que contenga valores y docstrings para los atributos y luego agregar el contenido a la clase __dict__ y docstring hacia el final de la declaración de clase; esto aliviaría la necesidad de escribir los nombres y valores de los atributos dos veces. edit: esta última idea, creo, no es posible, al menos no sin construir dinámicamente toda la clase a partir de datos, lo que parece una idea realmente mala a menos que haya alguna otra razón para hacerlo.

Soy bastante nuevo en Python y todavía estoy resolviendo los detalles del estilo de codificación, por lo que las críticas no relacionadas también son bienvenidas.

+0

Si está buscando una manera de documentar los atributos del modelo Django, esto podría ser útil: https://djangosnippets.org/snippets/2533/ –

+0

Duplicado de [¿Cómo documentar campos y propiedades en Python?] (Http : //stackoverflow.com/questions/6060813/how-to-document-fields-and-properties-in-python) que contienen una solución diferente. – bufh

Respuesta

50

Para evitar confusión: el término propiedad tiene specific meaning en python. De lo que estás hablando es a lo que llamamos class attributes. Como siempre se actúa sobre ellos a través de su clase, creo que tiene sentido documentarlos dentro de la cadena de documentación de la clase. Algo como esto:

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" 

creo que es mucho más fácil en los ojos que el enfoque en su ejemplo. Si realmente quisiera que apareciera una copia de los valores de los atributos en la cadena de documentos, los pondría al lado o debajo de la descripción de cada atributo.

Editar:

Tenga en cuenta que en Python, cadenas de documentación son usuarios reales de los objetos que documentan, no simples anotaciones de código fuente. Como las variables de atributo de clase no son objetos en sí mismas, sino referencias a objetos, no tienen forma de mantener sus propias cadenas de documentos. Supongo que podría defender las cadenas de doc en las referencias, tal vez para describir "lo que debería ir aquí" en lugar de "lo que realmente está aquí", pero me parece fácil hacerlo en la cadena de documentación de la clase contenedora.

+0

Supongo que en la mayoría de los casos esto está bien, ya que los atributos -gracias a la corrección de la terminología- están suficientemente declarados como para que se puedan agrupar al principio de la declaración de clase sin que sea impráctico pasar de uno a otro {leer tanto la documentación como el valor predeterminado} o {actualizar ambas instancias de la documentación y/o el valor predeterminado}. – intuited

+1

También tenga en cuenta que mi ejemplo hará que la documentación de los atributos aparezca en la carpeta de documentos de la clase. En realidad, preferiría poner la documentación en docstrings de los atributos en sí, pero esto no funciona para la mayoría de los tipos incorporados. – intuited

+0

Sí, mi idea inicial era simplemente declarar, p. 'flight_speed = 691; flight_speed .__ doc__ = "blah blah" '. Creo que esto es lo que estás mencionando en tu ** edición **. Desafortunadamente, esto no funciona para instancias de (la mayoría) tipos incorporados (como 'int' en ese ejemplo).Funciona para instancias de tipos definidos por el usuario. =========== No era en realidad una PEP (lo siento, olvide el número), que propuso la adición de cadenas de documentación para los atributos de la clase/módulo, pero fue rechazada porque no podían encontrar una manera para que quede claro si las docstrings fueron para los atributos anteriores o siguientes. – intuited

18

Se citan la PEP257: Convenios cadena de documentación, en la sección What is a docstring se afirma:

literales de cadena que se producen en otras partes de código Python también pueden actuar como documentación. No son reconocidos por el compilador de códigos de bytes de Python y no son accesibles como atributos de objetos de tiempo de ejecución (es decirno asignado a __doc__), pero las herramientas de software pueden extraer dos tipos de docstrings adicionales:

Los literales de cadena que se producen inmediatamente después de una asignación simple en el nivel superior de un módulo, clase o método __init__ se llaman "docstrings de atributos" .

Y esto se explica con más detalle en PEP 258: Cadenas de atributos. Como se explica anteriormente ʇsәɹoɈ. un atributo no es un objeto que puede poseer un __doc__ por lo que no aparecerá en help() o pydoc. Estas cadenas de documentos solo se pueden usar para la documentación generada.

Pero en la actualidad pocas herramientas los utilizan.

El anterior Epydoc do use them y Sphinx lo introdujeron en v0.6 y lo extendieron en v1.1. Sphinx puede usar docstring en una línea antes de una tarea o en un comentario especial después de una tarea.

Ver el directive autoattribute in the Sphinx Manual y los ejemplos de docstrings de atributos allí.

+0

El complemento jedi-vim también reconoce cadenas de documentos de atributos. –

+1

No sé cuándo se introdujo esto, pero Sphinx 1.2.2 parece incluir docstrings de atributos en la documentación generada. – jochen

+1

Gracias @jochen, actualizo mi respuesta. – marcz

7

Puede abusar de las propiedades en este sentido. Las propiedades contienen un getter, un setter, un eliminador, y una docstring. Ingenuamente, esto podría ser muy prolijo:

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 

, entonces tendrá una cadena de documentación perteneciente a Cx:

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

Para hacer esto para muchos atributos es engorroso, pero se podía imaginar un myprop función auxiliar:

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! 

Entonces, llamando pitones interactivos help dará:

Help on class C in module __main__: 

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

which I think should be pretty much what you're after. 

Editar: Ahora me doy cuenta de que quizás podamos evitar tener que pasar el primer argumento al myprop, porque el nombre interno no importa. Si las llamadas posteriores de myprop pueden comunicarse de alguna manera entre ellas, podría decidir automáticamente un nombre de atributo interno largo e improbable. Estoy seguro de que hay formas de implementar esto, pero no estoy seguro si valen la pena.

Cuestiones relacionadas