Thêm docstrings vào nametuples?


85

Có thể thêm một chuỗi tài liệu vào một tệp tin có tên một cách dễ dàng không?

Tôi đã thử

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

nhưng điều đó không cắt nó. Có thể làm theo một số cách khác không?

Câu trả lời:


53

Bạn có thể đạt được điều này bằng cách tạo một lớp bao bọc trống, đơn giản xung quanh giá trị được trả về từ đó namedtuple. Nội dung của tệp tôi đã tạo ( nt.py):

from collections import namedtuple

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

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

Sau đó, trong Python REPL:

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

Hoặc bạn có thể làm:

>>> help(nt.Point)  # which outputs...
Trợ giúp về lớp Điểm trong mô-đun nt:

điểm lớp (Điểm)
 | Một điểm trong không gian 2d
 |  
 | Thứ tự giải quyết phương pháp:
 | Điểm
 | Điểm
 | __builtin __. tuple
 | __builtin __. đối tượng
 ...

Nếu bạn không thích làm điều đó bằng tay mọi lúc, thì việc viết một loại hàm factory để thực hiện việc này là rất đơn giản:

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__

kết quả đầu ra:

A point in 3d space

2
Sẽ không chuyển đổi lớp con namedtuplethành một "đối tượng" chính thức? Do đó làm mất một số lợi ích về hiệu suất từ ​​các bộ giá trị được đặt tên?
exhuma

5
Nếu bạn thêm __slots__ = ()vào lớp con có nguồn gốc bạn có thể giữ lại những lợi thế bộ nhớ và hiệu suất sử dụngnamedtuple
ali_m

Nó vẫn thêm một cấp độ khác cho MRO, điều này không phù hợp với một chuỗi tài liệu. Tuy nhiên, người ta có thể chỉ cần gán __doc__và lưu một chuỗi tài liệu tùy chỉnh trong đối tượng gốc.
Bachsau

70

Trong Python 3, không cần trình bao bọc, vì các __doc__thuộc tính của kiểu có thể ghi được.

from collections import namedtuple

Point = namedtuple('Point', 'x y')
Point.__doc__ = '''\
A 2-dimensional coordinate

x - the abscissa
y - the ordinate'''

Điều này gần tương ứng với định nghĩa lớp tiêu chuẩn, trong đó docstring theo sau tiêu đề.

class Point():
    '''A 2-dimensional coordinate

    x - the abscissa
    y - the ordinate'''
    <class code>

Điều này không hoạt động trong Python 2.

AttributeError: attribute '__doc__' of 'type' objects is not writable.


64

Bắt gặp câu hỏi cũ này qua Google trong khi tự hỏi điều tương tự.

Chỉ muốn chỉ ra rằng bạn có thể thu gọn nó hơn nữa bằng cách gọi têntuple () ngay từ khai báo lớp:

from collections import namedtuple

class Point(namedtuple('Point', 'x y')):
    """Here is the docstring."""

8
Quan trọng là bạn bao gồm __slots__ = ()trong lớp. Nếu không, bạn tạo một __dict__tệp đính kèm của mình, làm mất đi tính chất gọn nhẹ của nametuple.
BoltzmannBrain

34

Có thể thêm một chuỗi tài liệu vào một tệp tin có tên một cách dễ dàng không?

Có, theo một số cách.

Nhập lớp con.NamedTuple - Python 3.6+

Kể từ Python 3.6, chúng ta có thể sử dụng một classđịnh nghĩa typing.NamedTupletrực tiếp, với một chuỗi doc (và các chú thích!):

from typing import NamedTuple

class Card(NamedTuple):
    """This is a card type."""
    suit: str
    rank: str

So với Python 2, việc khai báo rỗng __slots__là không cần thiết. Trong Python 3.8, nó không cần thiết ngay cả đối với các lớp con.

Lưu ý rằng khai báo __slots__không được để trống!

Trong Python 3, bạn cũng có thể dễ dàng thay đổi tài liệu trên một tệp tin có tên:

NT = collections.namedtuple('NT', 'foo bar')

NT.__doc__ = """:param str foo: foo name
:param list bar: List of bars to bar"""

Điều này cho phép chúng tôi xem mục đích dành cho họ khi chúng tôi kêu gọi họ trợ giúp:

Help on class NT in module __main__:

class NT(builtins.tuple)
 |  :param str foo: foo name
 |  :param list bar: List of bars to bar
...

Điều này thực sự dễ hiểu so với những khó khăn mà chúng tôi đã gặp phải khi hoàn thành điều tương tự trong Python 2.

Python 2

Trong Python 2, bạn sẽ cần

  • phân lớp con được đặt tên, và
  • khai báo __slots__ == ()

Khai báo __slots__một phần quan trọng mà các câu trả lời khác ở đây bỏ lỡ .

Nếu bạn không khai báo __slots__- bạn có thể thêm các thuộc tính đặc biệt có thể thay đổi vào các phiên bản, đưa ra các lỗi.

class Foo(namedtuple('Foo', 'bar')):
    """no __slots__ = ()!!!"""

Và bây giờ:

>>> f = Foo('bar')
>>> f.bar
'bar'
>>> f.baz = 'what?'
>>> f.__dict__
{'baz': 'what?'}

Mỗi trường hợp sẽ tạo ra một riêng biệt __dict__khi __dict__được truy cập (thiếu __slots__sẽ không khác cản trở các chức năng, nhưng lightweightness của tuple, không thay đổi, và tuyên bố thuộc tính này là tất cả các tính năng quan trọng của namedtuples).

Bạn cũng sẽ muốn một __repr__, nếu bạn muốn những gì được lặp lại trên dòng lệnh cung cấp cho bạn một đối tượng tương đương:

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__ = ()

cần một cái __repr__như thế này nếu bạn tạo base nametuple với một tên khác (giống như chúng ta đã làm ở trên với đối số chuỗi tên, 'NTBase'):

    def __repr__(self):
        return 'NT(foo={0}, bar={1})'.format(
                repr(self.foo), repr(self.bar))

Để kiểm tra repr, hãy khởi tạo, sau đó kiểm tra xem có bằng nhau về lượt vượt qua eval(repr(instance))

nt = NT('foo', 'bar')
assert eval(repr(nt)) == nt

Ví dụ từ tài liệu

Tài liệu cũng đưa ra một ví dụ như vậy, liên quan đến __slots__- Tôi đang thêm chuỗi tài liệu của riêng mình vào đó:

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)

...

Lớp con được hiển thị ở trên đặt __slots__thành một bộ trống. Điều này giúp giữ cho yêu cầu bộ nhớ thấp bằng cách ngăn chặn việc tạo các từ điển phiên bản.

Điều này thể hiện cách sử dụng tại chỗ (giống như một câu trả lời khác ở đây gợi ý), nhưng lưu ý rằng việc sử dụng tại chỗ có thể trở nên khó hiểu khi bạn nhìn vào thứ tự giải quyết phương pháp, nếu bạn đang gỡ lỗi, đó là lý do tại sao ban đầu tôi đề xuất sử dụng Baselàm hậu tố cho base có têntuple:

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

Để ngăn việc tạo __dict__lớp con khi từ một lớp sử dụng nó, bạn cũng phải khai báo nó trong lớp con. Xem thêm câu trả lời này để biết thêm những lưu ý khi sử dụng__slots__ .


3
Mặc dù không ngắn gọn và rõ ràng như các câu trả lời khác, nhưng đây nên là câu trả lời được chấp nhận vì nó làm nổi bật tầm quan trọng của __slots__. Nếu không có nó, bạn đang đánh mất giá trị nhẹ của một trang có tên.
BoltzmannBrain

7

Kể từ Python 3.5, docstrings cho namedtuplecác đối tượng có thể được cập nhật.

Từ whatsnew :

Point = namedtuple('Point', ['x', 'y'])
Point.__doc__ += ': Cartesian coodinate'
Point.x.__doc__ = 'abscissa'
Point.y.__doc__ = 'ordinate'


3

Không cần sử dụng một lớp trình bao bọc như được đề xuất bởi câu trả lời được chấp nhận. Chỉ cần thêm một chuỗi doc theo nghĩa đen :

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
Point.__doc__="A point in 2D space"

Điều này dẫn đến: (sử dụng ví dụ ipython3):

In [1]: Point?
Type:       type
String Form:<class '__main__.Point'>
Docstring:  A point in 2D space

In [2]: 

Voilà!


1
Lưu ý: Đây là chỉ có giá trị cho Python 3. Trong Python 2: AttributeError: attribute '__doc__' of 'type' objects is not writable.
Taylor Edmiston

1

Bạn có thể pha chế phiên bản của riêng bạn của hàm nhà máy có tên là Raymond Hettinger và thêm một docstringđối số tùy chọn . Tuy nhiên, sẽ dễ dàng hơn - và được cho là tốt hơn - chỉ cần xác định chức năng nhà máy của riêng bạn bằng cách sử dụng kỹ thuật cơ bản giống như trong công thức. Dù bằng cách nào, bạn sẽ kết thúc với một cái gì đó có thể tái sử dụng.

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

Tôi đã tạo hàm này để nhanh chóng tạo một tuple được đặt tên và ghi lại tuple cùng với từng tham số của nó:

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

Sau đó, bạn có thể tạo một tuple mới có tên:

MyTuple = named_tuple(
    "MyTuple",
    "My named tuple for x,y coordinates",
    x="The x value",
    y="The y value"
)

Sau đó, khởi tạo bộ tuple có tên được mô tả với dữ liệu của riêng bạn, tức là.

t = MyTuple(4, 8)
print(t) # prints: MyTuple(x=4, y=8)

Khi thực hiện help(MyTuple)thông qua dòng lệnh python3, điều sau được hiển thị:

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.

Ngoài ra, bạn cũng có thể chỉ định kiểu của tham số qua:

MyTuple = named_tuple(
    "MyTuple",
    "My named tuple for x,y coordinates",
    x=("int", "The x value"),
    y=("int", "The y value")
)

-2

Không, bạn chỉ có thể thêm chuỗi doc vào mô-đun, lớp và hàm (bao gồm cả các phương thức)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.