[Python-ideas] Docstrings for namedtuple

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


An improvement would be to cache the namedtuple types so that each only
gets created once.

Tim Delaney


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

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


More information about the Python-ideas mailing list