명명 된 튜플에 독 스트링을 추가 하시겠습니까?


85

쉽게 명명 된 튜플에 문서 문자열을 추가 할 수 있습니까?

나는 시도했다

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

그러나 그것은 그것을 자르지 않습니다. 다른 방법으로 할 수 있습니까?

답변:


53

에서 반환 된 값 주위에 간단한 빈 래퍼 클래스를 만들어이를 달성 할 수 있습니다 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...
nt 모듈의 Point 클래스에 대한 도움말 :

클래스 Point (Point)
 | 2D 공간의 점
 |  
 | 방법 해결 순서 :
 | 포인트
 | 포인트
 | __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

2
서브 클래 싱 namedtuple은를 본격적인 "객체" 로 변환하지 않습니까? 이로 인해 명명 된 튜플의 성능 향상이 일부 손실됩니까?
exhuma

5
__slots__ = ()파생 된 하위 클래스에 추가 하면 사용의 메모리 및 성능 이점을 유지할 수 있습니다namedtuple
ali_m

여전히 MRO에 또 다른 수준을 추가하며 독 스트링에 적합하지 않습니다. 그러나 __doc__원래 객체에 사용자 정의 된 독 스트링 을 간단히 할당하고 저장할 수 있습니다.
Bachsau

70

Python 3에서는 __doc__유형 의 속성이 쓰기 가능 하므로 래퍼가 필요하지 않습니다 .

from collections import namedtuple

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

x - the abscissa
y - the ordinate'''

이것은 docstring이 헤더 뒤에 오는 표준 클래스 정의와 거의 일치합니다.

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.


64

같은 것을 궁금해하면서 Google을 통해이 오래된 질문을 보았습니다.

클래스 선언에서 바로 namedtuple ()을 호출하여 더 깔끔하게 정리할 수 있다는 점을 지적하고 싶었습니다.

from collections import namedtuple

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

8
__slots__ = ()수업에 포함시키는 것이 중요합니다 . 그렇지 않으면 __dict__속성에 대한 을 생성하여 namedtuple의 경량 특성을 잃게됩니다.
BoltzmannBrain

34

쉽게 명명 된 튜플에 문서 문자열을 추가 할 수 있습니까?

예, 여러 가지 방법이 있습니다.

하위 클래스 유형 지정 .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에서는 namedtuple의 문서를 쉽게 변경할 수도 있습니다.

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

이것은 우리가 파이썬 2에서 똑같은 일을 수행하는 어려움에 비해 정말 간단합니다.

파이썬 2

Python 2에서는 다음을 수행해야합니다.

  • namedtuple을 하위 클래스로 만들고
  • 알리다 __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__기능을 방해하지는 않지만 튜플의 경량 성, 불변성 및 선언 된 속성은 모두 namedtuple의 중요한 기능입니다).

__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__- 내가 내 자신의 문서화 문자열을 추가 해요를 :

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접미사로 사용하도록 제안 했습니다. 기본 namedtuple의 경우 :

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

__dict__사용하는 클래스에서 when 서브 클래 싱이 생성되는 것을 방지하려면 서브 클래스에서도 선언해야합니다. 사용에 대한 자세한 경고__slots__this answer을 참조하십시오 .


3
다른 답변만큼 간결하고 명확하지는 않지만의 중요성을 강조하기 때문에 허용되는 답변이어야합니다 __slots__. 그것 없이는 명명 된 튜플의 경량 가치를 잃게됩니다.
BoltzmannBrain

7

Python 3.5부터 namedtuple 객체에 업데이트 할 수 있습니다.

로부터 각각 whatsnew :

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


3

수락 된 답변에서 제안한 래퍼 클래스를 사용할 필요가 없습니다. 말 그대로 독 스트링을 추가 하기 만하면됩니다 .

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

Voilà!


1
참고 : 이것은 Python 3에서만 유효합니다. Python 2 : AttributeError: attribute '__doc__' of 'type' objects is not writable.
테일러 Edmiston

1

Raymond Hettinger 의 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

0

명명 된 튜플을 빠르게 만들고 각 매개 변수와 함께 튜플을 문서화하기 위해이 함수를 만들었습니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.