programacion - ¿Cómo documentar los atributos de clase en Python?
poo python 3 (3)
Cita el PEP257: Convenciones de Docstring, en la sección Qué es un docstring se indica:
Los literales de cadena que aparecen en otras partes del código de Python también pueden actuar como documentación. No son reconocidos por el compilador de códigos de bytes Python y no son accesibles como atributos de objetos en tiempo de ejecución (es decir, no están asignados a __doc__), pero las herramientas de software pueden extraer dos tipos de documentos adicionales:
Los literales de cadena que aparecen inmediatamente después de una asignación simple en el nivel superior de un módulo, clase o método __init__ se llaman "cadenas de documentos de atributo".
Y esto se explica con más detalle en PEP 258: Cadenas de atributos. Como se explica arriba ʇ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 actualmente pocas herramientas los usan.
Los antiguos Epydoc sí los usan y Sphinx lo introdujo en v0.6 y lo extendió 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.
Consulte la autoatribución de la directiva en el Manual de Sphinx y los ejemplos de docstrings de atributos allí.
Estoy escribiendo una clase ligera cuyos atributos están destinados a ser de acceso público, y solo algunas 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:
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 dará como resultado la docstring de la clase que contiene la sección de docstring estándar inicial, así como las líneas agregadas para cada atributo a través de una asignación aumentada a __doc__
.
Aunque este estilo no parece estar expresamente prohibido en las pautas de estilo de docstring , 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 violación atroz 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. editar : esta última idea, creo, no es realmente 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 para Python y aún estoy trabajando en los detalles del estilo de codificación, por lo que las críticas no relacionadas también son bienvenidas.
Para evitar confusiones: el término propiedad tiene un significado específico en python. De lo que estás hablando es de lo que llamamos atributos de clase . 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 para los ojos que el enfoque en tu 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, las cadenas de documentos son miembros reales de los objetos que documentan, no solo las 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.
Podría abusar de las propiedades a este efecto. Las propiedades contienen un getter, un setter, un eliminador y una docstring . Ingenuamente, esto sería muy detallado:
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ás una docstring que pertenece a Cx:
In [24]: print(C.x.__doc__)
Docstring goes here.
Hacer esto para muchos atributos es engorroso, pero podrías imaginar una función 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!
Luego, al llamar a la help
interactiva de Pythons se obtendrá:
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 a myprop
, porque el nombre interno no importa. Si las llamadas posteriores de myprop
pueden comunicarse de alguna manera entre sí, podría decidir automáticamente sobre 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.