[Python-ideas] Docstrings for namedtuple

Tim Delaney timothy.c.delaney at gmail.com
Sun Dec 16 22:09:21 CET 2012


And ignore that extra debugging print in there ;)

class NamedTuple(metaclass=NamedTupleMetaClass):
    def __new__(cls, *p, **kw):
        if not p:
            p = cls.fields.values()

        try:
            verbose = kw['verbose']
        except KeyError:
            verbose = cls.verbose

        return collections.namedtuple(cls.__name__, list(cls.fields),
rename=cls.rename, verbose=verbose)(*p)

Tim Delaney


On 17 December 2012 08:08, Tim Delaney <timothy.c.delaney at gmail.com> wrote:

> It can be made a bit more intelligent. I haven't done anything with
> docstrings here, but it wouldn't be hard to add. This automatically handles
> defaults (you can call the namedtuple with either zero parameters or the
> exact number). You can specify __rename__ = True, which will then only
> exclude __dunder_names__ (otherwise all names starting with an underscore
> are excluded). You can also pass verbose=[True|False] to the subclass
> constructor.
>
> import collections
>
> class NamedTupleMetaClass(type):
>     # The prepare function
>     @classmethod
>     def __prepare__(metacls, name, bases): # No keywords in this case
>         return collections.OrderedDict()
>
>     # The metaclass invocation
>     def __new__(cls, name, bases, classdict):
>         fields = collections.OrderedDict()
>         rename = False
>         verbose = False
>
>         for f in classdict:
>             if f == '__rename__':
>                 rename = classdict[f]
>             elif f == '__verbose__':
>                 verbose = classdict[f]
>
>         for f in classdict:
>             if f.startswith('_'):
>                 if not rename:
>                     continue
>
>                 if f.startswith('__') and f.endswith('__'):
>                     continue
>
>             fields[f] = classdict[f]
>
>         result = type.__new__(cls, name, bases, classdict)
>         result.fields = fields
>         result.rename = rename
>         result.verbose = verbose
>         return result
>
> class NamedTuple(metaclass=NamedTupleMetaClass):
>     def __new__(cls, *p, **kw):
>         print(p)
>         if not p:
>             p = cls.fields.values()
>
>         try:
>             verbose = kw['verbose']
>         except KeyError:
>             verbose = cls.verbose
>
>         return collections.namedtuple(cls.__name__, list(cls.fields),
> rename=cls.rename, verbose=verbose)(*p)
>
> Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64
> bit (AMD64)] on win32
> Type "help", "copyright", "credits" or "license" for more information.
> >>> import namedtuple_baseclass
> >>> class Point(namedtuple_baseclass.NamedTuple):
> ...     x = 0
> ...     y = 0
> ...
> >>> print(Point())
> Point(x=0, y=0)
> >>> print(Point(1, 2))
> Point(x=1, y=2)
> >>> print(Point(1))
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File ".\namedtuple_baseclass.py", line 38, in __new__
>     return collections.namedtuple(cls.__name__, list(cls.fields),
> rename=cls.rename, verbose=cls.verbose)(*p)
> TypeError: __new__() missing 1 required positional argument: 'y'
> >>> print(Point(1, 2, 3))
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File ".\namedtuple_baseclass.py", line 38, in __new__
>     return collections.namedtuple(cls.__name__, list(cls.fields),
> rename=cls.rename, verbose=cls.verbose)(*p)
> TypeError: __new__() takes 3 positional arguments but 4 were given
> >>>
>
> Tim Delaney
>
>
> On 17 December 2012 07:05, Terry Reedy <tjreedy at udel.edu> wrote:
>
>> On 12/16/2012 8:22 AM, Eli Bendersky wrote:
>>
>>  This may be a good time to say that personally I always disliked
>>> namedtuple's creation syntax. It is unpleasant in two respects:
>>>
>>> 1. You have to repeat the name
>>> 2. You have to specify the fields in a space-separated string
>>>
>>> I wish there was an alternative of something like:
>>>
>>> @namedtuple
>>> class Point:
>>>    x = 0
>>>    y = 0
>>>
>>
>> Pretty easy, once one figures out metaclass basics.
>>
>> import collections as co
>>
>> class ntmeta():
>>     def __prepare__(name, bases, **kwds):
>>         return co.OrderedDict()
>>     def __new__(cls, name, bases, namespace):
>>         print(namespace) # shows why filter is needed
>>         return co.namedtuple(name,
>>                 filter(lambda s: s[0] != '_', namespace))
>>
>> class Point(metaclass=ntmeta):
>>
>>     x = 0
>>     y = 0
>>
>> p = Point(1,2)
>> print(p)
>> #
>> OrderedDict([('__module__', '__main__'), ('__qualname__', 'Point'), ('x',
>> 0), ('y', 0)])
>> Point(x=1, y=2)
>>
>> To use the filtered namespace values as defaults (Antoine's suggestion),
>> first replace namedtuple() with its body.
>> Then modify the header of generated name.__new__. For Point, change
>>
>> def __new__(_cls, x, y):
>> #to
>> def __new__(_cls, x=0, y=0):
>>
>> Also change the newclass docstring. For Point, change
>>     'Point(x, y)'
>> to
>>     'Point(x=0, y=0)'
>>
>> --
>> Terry Jan Reedy
>>
>>
>> ______________________________**_________________
>> Python-ideas mailing list
>> Python-ideas at python.org
>> http://mail.python.org/**mailman/listinfo/python-ideas<http://mail.python.org/mailman/listinfo/python-ideas>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20121217/de611d23/attachment.html>


More information about the Python-ideas mailing list