[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