Adlandırılmış çiftlere dokümanlar mı ekliyorsunuz?


87

Adlandırılmış bir gruba kolay bir şekilde dokümantasyon dizisi eklemek mümkün müdür?

denedim

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

ama bu onu kesmez. Başka bir şekilde yapmak mümkün mü?

Yanıtlar:


53

Bunu, döndürülen değerin etrafında basit, boş bir sarmalayıcı sınıfı oluşturarak elde edebilirsiniz namedtuple. Oluşturduğum bir dosyanın içeriği ( nt.py):

from collections import namedtuple

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

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

Sonra Python REPL'de:

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

Ya da yapabilirsin:

>>> help(nt.Point)  # which outputs...
Nt modülündeki sınıf noktası ile ilgili yardım:

sınıf Puan (Nokta)
 | 2 boyutlu uzayda bir nokta
 |  
 | Yöntem çözümleme sırası:
 | Nokta
 | Nokta
 | __builtin __. tuple
 | __builtin __. nesne
 ...

Bunu her seferinde elle yapmaktan hoşlanmıyorsanız, bunu yapmak için bir tür fabrika işlevi yazmak önemsizdir:

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__

hangi çıktılar:

A point in 3d space

2
Alt sınıflandırma, namedtupleonu tam teşekküllü bir "nesneye" dönüştürmez mi? Böylece adlandırılmış dizilerden elde edilen bazı performans kazançlarını mı kaybediyorsunuz?
exhuma

5
Eğer eklerseniz __slots__ = ()türetilmiş alt sınıfa Kullandığınız bellek ve performans avantajlarını muhafaza edebilirnamedtuple
ali_m

Yine de MRO'ya bir docstring için haklı olmayan başka bir seviye ekler. Ancak, __doc__orijinal nesneye özel bir doküman dizgisi atanabilir ve kaydedilebilir.
Bachsau

71

Python 3'te __doc__türlerin öznitelikleri yazılabilir olduğundan sarmalayıcıya gerek yoktur .

from collections import namedtuple

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

x - the abscissa
y - the ordinate'''

Bu, docstring'in başlığı takip ettiği standart bir sınıf tanımına yakından karşılık gelir.

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

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

Bu Python 2'de çalışmaz.

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


64

Aynı şeyi merak ederken Google üzerinden bu eski soruyla karşılaştım.

Sınıf bildiriminden doğrudan namedtuple () 'ı çağırarak bunu daha da düzeltebileceğinizi belirtmek istedim:

from collections import namedtuple

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

8
__slots__ = ()Sınıfa dahil etmeniz önemlidir. Aksi takdirde __dict__, attr'larınız için bir oluşturursunuz, adlandırılmış tuple'ın hafif doğasını kaybedersiniz.
BoltzmannBrain

34

Adlandırılmış bir gruba kolay bir şekilde dokümantasyon dizisi eklemek mümkün müdür?

Evet, birkaç şekilde.

Alt sınıf yazma.NamedTuple - Python 3.6+

Python 3.6'dan itibaren, doğrudan, bir docstring (ve ek açıklamalar!) İle bir classtanım kullanabiliriz typing.NamedTuple:

from typing import NamedTuple

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

Python 2 ile karşılaştırıldığında boş bildirmek __slots__gerekli değildir. Python 3.8'de alt sınıflar için bile gerekli değildir.

Bildirimin __slots__boş olamayacağını unutmayın !

Python 3'te, ayrıca adlandırılmış bir çift üzerindeki dokümanı kolayca değiştirebilirsiniz:

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

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

Bu, yardım çağırdığımızda onlar için niyeti görmemizi sağlar:

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'de aynı şeyi başarmakta karşılaştığımız zorluklarla karşılaştırıldığında bu gerçekten basittir.

Python 2

Python 2'de yapmanız gerekenler

  • adlandırılmış grubu alt sınıflara ayırın ve
  • bildirmek __slots__ == ()

Bildirmek __slots__, buradaki diğer cevapların kaçırdığı önemli bir kısımdır .

Bildirmezseniz __slots__, örneklere değişken geçici öznitelikler ekleyebilir ve böceklere yol açabilirsiniz.

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

Ve şimdi:

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

Her örnek ayrı yaratacaktır erişilir (eksikliğini aksi işlevselliği engel olmaz, ancak kayıt düzeni değişmezlik lightweightness ve nitelikleri namedtuples tüm önemli özellikleridir ilan).__dict____dict____slots__

Ayrıca __repr__, komut satırında yankılananın size eşdeğer bir nesne vermesini istiyorsanız:

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__tuple temelini farklı bir adla oluşturursanız buna benzer bir gereklidir (yukarıda ad dizesi bağımsız değişkeniyle yaptığımız gibi 'NTBase'):

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

Repr'yi test etmek için, somutlaştırın, ardından bir geçişin eşitliğini test edin eval(repr(instance))

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

Belgelerden örnek

Docs ayrıca , ilgili böyle bir örnek vermek __slots__- Ben buna kendi docstring'ini ekliyorum:

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)

...

Yukarıda gösterilen alt sınıf __slots__, boş bir demete ayarlanır . Bu, örnek sözlüklerinin oluşturulmasını engelleyerek bellek gereksinimlerini düşük tutmaya yardımcı olur.

Bu, yerinde kullanımı gösterir (buradaki başka bir yanıtın önerdiği gibi), ancak yerinde kullanımın, yöntem çözümleme sırasına baktığınızda kafa karıştırıcı hale gelebileceğini unutmayın, hata ayıklama yapıyorsanız, bu yüzden başlangıçta Basebir sonek olarak kullanmayı önerdim çift ​​adlı taban için:

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

Onu __dict__kullanan bir sınıftan alt sınıflandırma yaparken bir alt sınıf oluşturmayı önlemek için , alt sınıfta da bildirmelisiniz. Kullanımla ilgili daha fazla uyarı için bu yanıta__slots__ da bakın .


3
Diğer cevaplar kadar kısa ve net olmasa da, kabul edilen cevap bu olmalıdır çünkü önemini vurgulamaktadır __slots__. Onsuz, isimli bir çiftin hafiflik değerini kaybediyorsunuz.
BoltzmannBrain

7

Python 3.5'ten beri, namedtuplenesneler için docstrings güncellenebilir.

Gönderen whatsnew :

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


3

Kabul edilen yanıtın önerdiği gibi bir sarmalayıcı sınıf kullanmaya gerek yoktur. Kelimenin tam anlamıyla bir docstring ekleyin :

from collections import namedtuple

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

Bunun sonucu: (örnek kullanarak ipython3):

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

In [2]: 

Voilà!


1
Not: Bu Python 2'de Python 3 için geçerlidir: AttributeError: attribute '__doc__' of 'type' objects is not writable.
Taylor Edmiston

1

Raymond Hettinger tarafından yazılan adlandırılmış çift fabrika işlevinin kendi versiyonunu oluşturabilir ve isteğe bağlı bir docstringargüman ekleyebilirsiniz . Bununla birlikte, tarifte olduğu gibi aynı temel tekniği kullanarak kendi fabrika işlevinizi tanımlamak daha kolay ve muhtemelen daha iyi olacaktır. Her iki durumda da, yeniden kullanılabilir bir şey elde edeceksiniz.

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

Bu işlevi, hızlı bir şekilde adlandırılmış bir demet oluşturmak ve demeti parametresiyle birlikte belgelemek için oluşturdum:

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

Daha sonra yeni bir adlandırılmış demet oluşturabilirsiniz:

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

Daha sonra tarif edilen adlandırılmış tuple'ı kendi verilerinizle somutlaştırın, örn.

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

help(MyTuple)Python3 komut satırı aracılığıyla çalıştırılırken aşağıdakiler gösterilir:

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.

Alternatif olarak, parametrenin türünü şu yolla da belirtebilirsiniz:

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

-2

Hayır, yalnızca modüllere, sınıflara ve işleve (yöntemler dahil) belge dizeleri ekleyebilirsiniz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.