Добавление строк документов в именованные кортежи? - PullRequest
72 голосов
/ 22 октября 2009

Можно ли легко добавить строку документации в именованный кортеж?

Я пытался

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)"

но это не сокращает это. Можно ли сделать как-то иначе?

Ответы [ 10 ]

60 голосов
/ 27 марта 2013

Наткнулся на этот старый вопрос через Google, удивляясь тому же.

Просто хотел отметить, что вы можете привести в порядок еще больше, вызвав namedtuple () прямо из объявления класса:

from collections import namedtuple

class Point(namedtuple('Point', 'x y')):
    """Here is the docstring."""
57 голосов
/ 05 декабря 2013

В 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.

46 голосов
/ 22 октября 2009

Этого можно добиться, создав простой пустой класс-оболочку для возвращаемого значения из namedtuple. Содержимое файла, который я создал (nt.py):

from collections import namedtuple

Point_ = namedtuple("Point", ["x", "y"])

class Point(Point_):
    """ A point in 2d space """
    pass

Тогда в Python REPL:

>>> print nt.Point.__doc__
 A point in 2d space 

Или вы можете сделать:

>>> help(nt.Point)  # which outputs...
Help on class Point in module nt:

class Point(Point)
 |  A point in 2d space
 |  
 |  Method resolution order:
 |      Point
 |      Point
 |      __builtin__.tuple
 |      __builtin__.object
 ...

Если вам не нравится делать это вручную каждый раз, написать обычную фабричную функцию для этого просто:

def NamedTupleWithDocstring(docstring, *ntargs):
    nt = namedtuple(*ntargs)
    class NT(nt):
        __doc__ = docstring
    return NT

Point3D = NamedTupleWithDocstring("A point in 3d space", "Point3d", ["x", "y", "z"])

p3 = Point3D(1,2,3)

print p3.__doc__

который выводит:

A point in 3d space
26 голосов
/ 17 февраля 2015

Можно ли легко добавить строку документации в именованный кортеж?

Python 3

В 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).

Вам также понадобится __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__ = ()

a __repr__, как это необходимо, если вы создаете базовый именованный кортеж с другим именем (как мы делали выше с аргументом строки имени, '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__ - я добавляю к нему свою собственную строку документации:

class Point(namedtuple('Point', 'x y')):
    """Docstring added here, not in original"""
    __slots__ = ()
    @property
    def hypot(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5
    def __str__(self):
        return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

...

Показанный выше подкласс устанавливает __slots__ в пустой кортеж. Это помогает поддерживать требования к памяти на низком уровне, предотвращая создание экземпляра словари.

Это демонстрирует использование на месте (как предлагает другой ответ здесь), но обратите внимание, что использование на месте может сбивать с толку, когда вы смотрите порядок разрешения методов, если вы отлаживаете, именно поэтому я изначально предложил использовать Base в качестве суффикса для базового именованного кортежа:

>>> Point.mro()
[<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>]
                # ^^^^^---------------------^^^^^-- same names!        

Чтобы предотвратить создание __dict__ при создании подкласса из класса, который его использует, вы также должны объявить его в подклассе. См. Также этот ответ для получения дополнительной информации об использовании __slots__.

6 голосов
/ 04 сентября 2016

Начиная с Python 3.5, строки документов для namedtuple объектов могут быть обновлены.

Из whatsnew :

Point = namedtuple('Point', ['x', 'y'])
Point.__doc__ += ': Cartesian coodinate'
Point.x.__doc__ = 'abscissa'
Point.y.__doc__ = 'ordinate'
4 голосов
/ 07 марта 2018

В Python 3.6+ вы можете использовать:

class Point(NamedTuple):
    """
    A point in 2D space
    """
    x: float
    y: float
3 голосов
/ 24 сентября 2014

Нет необходимости использовать класс-оболочку, как предполагает принятый ответ. Просто буквально добавить строку документации:

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]: 

Вуаля!

1 голос
/ 28 июля 2010

Вы можете придумать свою собственную версию фабричной функции именованных кортежей от Raymond Hettinger и добавить необязательный аргумент 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
0 голосов
/ 07 января 2019

Я создал эту функцию для быстрого создания именованного кортежа и документирования кортежа вместе с каждым из его параметров:

<code>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
        }
: return: collection.namedtuple «»» имя_параметра = список (kwargs.keys ()) результат = namedtuple (name, '' .join (имя_параметра)) # Если есть какие-либо параметры (например, это не пустой именованный кортеж) если len (имя_параметра): # Добавить межстрочный интервал перед описанием параметров этого именованного кортежа если описание не '': описание + = "\ n" # Просмотрите каждый предоставленный параметр и добавьте его в описание строки документа именованного кортежа. для имени_параметра в именах параметров: параметр_данные = kwargs [имя_параметра] # Определите, включен ли тип параметра вместе с описанием или # если было предоставлено только описание параметр_тип = '' if isinstance (параметр_данные, строка): Параметр_данных = Параметр_данных еще: тип_параметра, параметр_добавление = параметр_данные описание + = "\ n: параметр {тип} {имя}: {описание}". формат ( type = parameter_type + '' if parameter_type else '', имя = parameter_name, Описание = parameter_description ) # Изменить строку документации, специфичную для этого параметра getattr (результат, имя_параметра) .__ doc__ = описание_параметра # Установить описание строки документа для результирующего именованного кортежа результат .__ doc__ = описание вернуть результат

Затем вы можете создать новый кортеж с именем:

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")
)
0 голосов
/ 22 октября 2009

Нет, вы можете добавлять только строки документа в модули, классы и функции (включая методы)

...