[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