Можно ли легко добавить строку документации в именованный набор?
Я старался
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
"""
A point in 2D space
"""
# Yet another test
"""
A(nother) point in 2D space
"""
Point2 = namedtuple("Point2", ["x", "y"])
print Point.__doc__ # -> "Point(x, y)"
print Point2.__doc__ # -> "Point2(x, y)"
но это не режет. А можно как-то иначе?
python
docstring
namedtuple
Рикард
источник
источник
namedtuple
в полноценный «объект»? Из-за этого теряется прирост производительности от именованных кортежей?__slots__ = ()
к производному подклассу, вы сможете сохранить преимущества использования памяти и производительностиnamedtuple
__doc__
и сохранить настроенную строку документации в исходном объекте.В Python 3 оболочка не требуется, поскольку
__doc__
атрибуты типов доступны для записи.from collections import namedtuple Point = namedtuple('Point', 'x y') Point.__doc__ = '''\ A 2-dimensional coordinate x - the abscissa y - the ordinate'''
Это близко соответствует определению стандартного класса, где строка документации следует за заголовком.
class Point(): '''A 2-dimensional coordinate x - the abscissa y - the ordinate''' <class code>
Это не работает в Python 2.
AttributeError: attribute '__doc__' of 'type' objects is not writable
.источник
Наткнулся на этот старый вопрос через Google, задаваясь вопросом о том же.
Просто хотел указать, что вы можете привести его в порядок, вызвав namedtuple () прямо из объявления класса:
from collections import namedtuple class Point(namedtuple('Point', 'x y')): """Here is the docstring."""
источник
__slots__ = ()
в класс. В противном случае вы создадите объект__dict__
для своих атрибутов, потеряв легковесный характер namedtuple.Да, несколькими способами.
Типизация подкласса.NamedTuple - Python 3.6+
Начиная с Python 3.6, мы можем использовать
class
определениеtyping.NamedTuple
напрямую, со строкой документации (и аннотациями!):from typing import NamedTuple class Card(NamedTuple): """This is a card type.""" suit: str rank: str
По сравнению с Python 2 объявление пустым
__slots__
не требуется. В Python 3.8 это не требуется даже для подклассов.Обратите внимание, что объявление
__slots__
не может быть непустым!В Python 3 вы также можете легко изменить документ для именованного кортежа:
NT = collections.namedtuple('NT', 'foo bar') NT.__doc__ = """:param str foo: foo name :param list bar: List of bars to bar"""
Это позволяет нам видеть их намерения, когда мы обращаемся к ним за помощью:
Help on class NT in module __main__: class NT(builtins.tuple) | :param str foo: foo name | :param list bar: List of bars to bar ...
Это действительно просто по сравнению с трудностями, которые мы испытываем при выполнении того же самого в Python 2.
Python 2
В Python 2 вам потребуется
__slots__ == ()
Объявление
__slots__
- важная часть, которую здесь упускают другие ответы .Если вы не объявляете
__slots__
- вы можете добавить изменяемые специальные атрибуты к экземплярам, внося ошибки.class Foo(namedtuple('Foo', 'bar')): """no __slots__ = ()!!!"""
И сейчас:
>>> f = Foo('bar') >>> f.bar 'bar' >>> f.baz = 'what?' >>> f.__dict__ {'baz': 'what?'}
Каждый экземпляр будет создавать отдельный
__dict__
при__dict__
доступе (отсутствие в__slots__
противном случае не будет препятствовать функциональности, но легкость кортежа, неизменяемость и объявленные атрибуты являются важными особенностями namedtuples).Вам также понадобится a
__repr__
, если вы хотите, чтобы то, что отображается в командной строке, давало вам эквивалентный объект:NTBase = collections.namedtuple('NTBase', 'foo bar') class NT(NTBase): """ Individual foo bar, a namedtuple :param str foo: foo name :param list bar: List of bars to bar """ __slots__ = ()
__repr__
, как это необходимо , если вы создаете базу namedtuple с другим именем (как мы делали выше с именем строки аргумента'NTBase'
):def __repr__(self): return 'NT(foo={0}, bar={1})'.format( repr(self.foo), repr(self.bar))
Чтобы проверить repr, создайте экземпляр, а затем проверьте равенство перехода к
eval(repr(instance))
nt = NT('foo', 'bar') assert eval(repr(nt)) == nt
Пример из документации
В документации также приводится такой пример, касающийся
__slots__
- я добавляю к нему свою собственную строку документации:Это демонстрирует использование на месте (как предлагает другой ответ здесь), но обратите внимание, что использование на месте может сбивать с толку, когда вы смотрите на порядок разрешения метода, если вы отлаживаете, поэтому я первоначально предложил использовать
Base
в качестве суффикса для базы с именем кортеж:>>> Point.mro() [<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>] # ^^^^^---------------------^^^^^-- same names!
Чтобы предотвратить создание класса
__dict__
при создании подкласса от класса, который его использует, вы также должны объявить его в подклассе. См. Также этот ответ для получения дополнительных предупреждений об использовании__slots__
.источник
__slots__
. Без него вы теряете легковесное значение namedtuple.Начиная с Python 3.5, строки документации для
namedtuple
объектов можно обновлять.Из чего нового :
источник
В Python 3.6+ вы можете использовать:
class Point(NamedTuple): """ A point in 2D space """ x: float y: float
источник
Нет необходимости использовать класс-оболочку, как это предлагается в принятом ответе. Просто добавьте строку документации:
from collections import namedtuple Point = namedtuple("Point", ["x", "y"]) Point.__doc__="A point in 2D space"
Это приводит к: (пример использования
ipython3
):In [1]: Point? Type: type String Form:<class '__main__.Point'> Docstring: A point in 2D space In [2]:
Вуаля!
источник
AttributeError: attribute '__doc__' of 'type' objects is not writable
.Вы можете придумать свою собственную версию фабричной функции namedtuple Раймонда Хеттингера и добавить необязательный
docstring
аргумент. Однако было бы проще - и, возможно, лучше - просто определить собственную фабричную функцию, используя ту же базовую технику, что и в рецепте. В любом случае вы получите что-то многоразовое.from collections import namedtuple def my_namedtuple(typename, field_names, verbose=False, rename=False, docstring=''): '''Returns a new subclass of namedtuple with the supplied docstring appended to the default one. >>> Point = my_namedtuple('Point', 'x, y', docstring='A point in 2D space') >>> print Point.__doc__ Point(x, y): A point in 2D space ''' # create a base class and concatenate its docstring and the one passed _base = namedtuple(typename, field_names, verbose, rename) _docstring = ''.join([_base.__doc__, ': ', docstring]) # fill in template to create a no-op subclass with the combined docstring template = '''class subclass(_base): %(_docstring)r pass\n''' % locals() # execute code string in a temporary namespace namespace = dict(_base=_base, _docstring=_docstring) try: exec template in namespace except SyntaxError, e: raise SyntaxError(e.message + ':\n' + template) return namespace['subclass'] # subclass object created
источник
Я создал эту функцию для быстрого создания именованного кортежа и документирования кортежа вместе с каждым из его параметров:
from collections import namedtuple def named_tuple(name, description='', **kwargs): """ A named tuple with docstring documentation of each of its parameters :param str name: The named tuple's name :param str description: The named tuple's description :param kwargs: This named tuple's parameters' data with two different ways to describe said parameters. Format: <pre>{ str: ( # The parameter's name str, # The parameter's type str # The parameter's description ), str: str, # The parameter's name: the parameter's description ... # Any other parameters }</pre> :return: collections.namedtuple """ parameter_names = list(kwargs.keys()) result = namedtuple(name, ' '.join(parameter_names)) # If there are any parameters provided (such that this is not an empty named tuple) if len(parameter_names): # Add line spacing before describing this named tuple's parameters if description is not '': description += "\n" # Go through each parameter provided and add it to the named tuple's docstring description for parameter_name in parameter_names: parameter_data = kwargs[parameter_name] # Determine whether parameter type is included along with the description or # if only a description was provided parameter_type = '' if isinstance(parameter_data, str): parameter_description = parameter_data else: parameter_type, parameter_description = parameter_data description += "\n:param {type}{name}: {description}".format( type=parameter_type + ' ' if parameter_type else '', name=parameter_name, description=parameter_description ) # Change the docstring specific to this parameter getattr(result, parameter_name).__doc__ = parameter_description # Set the docstring description for the resulting named tuple result.__doc__ = description return result
Затем вы можете создать новый именованный кортеж:
MyTuple = named_tuple( "MyTuple", "My named tuple for x,y coordinates", x="The x value", y="The y value" )
Затем создайте экземпляр описанного именованного кортежа с вашими собственными данными, т.е.
t = MyTuple(4, 8) print(t) # prints: MyTuple(x=4, y=8)
При выполнении
help(MyTuple)
через командную строку python3 отображается следующее:Help on class MyTuple: class MyTuple(builtins.tuple) | MyTuple(x, y) | | My named tuple for x,y coordinates | | :param x: The x value | :param y: The y value | | Method resolution order: | MyTuple | builtins.tuple | builtins.object | | Methods defined here: | | __getnewargs__(self) | Return self as a plain tuple. Used by copy and pickle. | | __repr__(self) | Return a nicely formatted representation string | | _asdict(self) | Return a new OrderedDict which maps field names to their values. | | _replace(_self, **kwds) | Return a new MyTuple object replacing specified fields with new values | | ---------------------------------------------------------------------- | Class methods defined here: | | _make(iterable) from builtins.type | Make a new MyTuple object from a sequence or iterable | | ---------------------------------------------------------------------- | Static methods defined here: | | __new__(_cls, x, y) | Create new instance of MyTuple(x, y) | | ---------------------------------------------------------------------- | Data descriptors defined here: | | x | The x value | | y | The y value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | _fields = ('x', 'y') | | _fields_defaults = {} | | ---------------------------------------------------------------------- | Methods inherited from builtins.tuple: | | __add__(self, value, /) | Return self+value. | | __contains__(self, key, /) | Return key in self. | | __eq__(self, value, /) | Return self==value. | | __ge__(self, value, /) | Return self>=value. | | __getattribute__(self, name, /) | Return getattr(self, name). | | __getitem__(self, key, /) | Return self[key]. | | __gt__(self, value, /) | Return self>value. | | __hash__(self, /) | Return hash(self). | | __iter__(self, /) | Implement iter(self). | | __le__(self, value, /) | Return self<=value. | | __len__(self, /) | Return len(self). | | __lt__(self, value, /) | Return self<value. | | __mul__(self, value, /) | Return self*value. | | __ne__(self, value, /) | Return self!=value. | | __rmul__(self, value, /) | Return value*self. | | count(self, value, /) | Return number of occurrences of value. | | index(self, value, start=0, stop=9223372036854775807, /) | Return first index of value. | | Raises ValueError if the value is not present.
Кроме того, вы также можете указать тип параметра с помощью:
MyTuple = named_tuple( "MyTuple", "My named tuple for x,y coordinates", x=("int", "The x value"), y=("int", "The y value") )
источник
Нет, вы можете добавлять только строки документации к модулям, классам и функциям (включая методы)
источник