[Python-ideas] Docstrings for namedtuple

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


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/a3346a54/attachment.html>


More information about the Python-ideas mailing list