Как документировать атрибуты класса в Python?
Я пишу легкий класс, чьи атрибуты предназначены для публичного доступа и только иногда переопределяются в конкретных экземплярах. На языке Python нет никаких условий для создания docstrings для атрибутов класса или любых атрибутов. Каков приемлемый способ, должен ли он быть, документировать эти атрибуты? В настоящее время я делаю такие вещи:
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. Меня все еще раздражает, что я должен на самом деле написать атрибуты дважды; Я рассматриваю возможность использования строковых представлений значений в docstring, чтобы избежать дублирования значений по умолчанию.
Является ли это отвратительным нарушением специальных конвенций сообщества? Это нормально? Есть ли способ лучше? Например, можно создать словарь, содержащий значения и docstrings для атрибутов, а затем добавить содержимое в класс __dict__
и docstring в конец объявления класса; это облегчит необходимость ввода имен атрибутов и значений дважды. edit: эта последняя идея, по-моему, на самом деле не возможна, по крайней мере, не без динамического построения всего класса из данных, что кажется действительно плохим идеей, если нет других причин для этого.
Я новичок в python и все еще разрабатываю детали стиля кодирования, поэтому также приветствуются не связанные с критикой критические замечания.
Ответы
Ответ 1
Чтобы избежать путаницы: термин свойство имеет особое значение в 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"
Я думаю, что это намного проще для глаз, чем подход в вашем примере. Если бы я действительно хотел, чтобы копия значений атрибута появлялась в строке документа, я бы поместил их рядом с описанием каждого атрибута или под ним.
Помните, что в Python строки документов являются фактическими членами объектов, которые они документируют, а не просто аннотациями исходного кода. Поскольку переменные атрибутов класса не являются самими объектами, а являются ссылками на объекты, они не могут содержать собственные строки документов. Я полагаю, вы могли бы привести аргументы в пользу строк документации на ссылках, возможно, описать "что должно быть здесь" вместо "что на самом деле здесь", но я считаю, что это довольно легко сделать в содержащей строке класса документа.
Ответ 2
Вы цитируете PEP257: Условные обозначения, в разделе Что такое указанная строка
Строковые литералы, встречающиеся в других местах кода Python, также могут выступать в качестве документации. Они не распознаются компилятором байт-кода Python и недоступны в качестве атрибутов объекта среды выполнения (т.е. не назначаются для __doc__), но программные инструменты могут извлекать два типа дополнительных строк документации:
Строковые литералы, встречающиеся сразу после простого присваивания на верхнем уровне модуля, класса или метода __init__, называются "строки документации атрибута".
И это более подробно объясняется в PEP 258: Строки документации атрибутов. Как объясняется выше ʇsәɹoɈ. атрибут не является объектом, которому может принадлежать __doc__, поэтому он не будет отображаться в help()
или pydoc. Эти строки документации могут использоваться только для сгенерированной документации.
Они используются в Sphinx с директивой autoattribute
Sphinx может использовать комментарии в строке перед назначением или специальный комментарий после назначения или строку документации после определения, которое будет автоматически документировано.
Ответ 3
Вы можете злоупотреблять свойствами с этой целью. Свойства содержат получатель, установщик, удалитель и строку документации. Наивно это получилось бы очень многословно:
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!
Затем, вызов интерактивной help
Pythons даст:
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
могут каким-либо образом связываться друг с другом, он может автоматически выбрать длинное и маловероятное внутреннее имя атрибута. Я уверен, что есть способы реализовать это, но я не уверен, что они того стоят.