[Python-Dev] Py2.6 ideas
Raymond Hettinger
python at rcn.com
Tue Feb 20 07:47:34 CET 2007
Thanks for the comments.
Will experiment with them and get back to you..
The alternate naming is fine. At work, we call it ntuple().
The constructor signature has been experimented with
several time and had best results in its current form
which allows the *args for casting a record set returned
by SQL or by the CSV module as in Point(*fetchall(s)),
and it allows for direct construction with Point(2,3) without the
slower and weirder form: Point((2,3)). Also, the current signature
works better with keyword arguments: Point(x=2, y=3) or
Point(2, y=3) which wouldn't be common but would be
consistent with the relationship between keyword arguments
and positional arguments in other parts of the language.
The string form for the named tuple factory was arrived at
because it was easier to write, read, and alter than its original
form with a list of strings:
Contract = namedtuple('Contract stock strike volatility expiration rate
iscall')
vs.
Contract = namedtuple('Contract', 'stock', 'strike', 'volatility',
'expiration', 'rate', 'iscall')
That former is easier to edit and to re-arrange. Either form is trivial to
convert
programmatically to the other and the definition step only occurs once while the
use of the new type can appear many times throughout the code.
Having experimented with both forms, I've found the string form to
be best thought it seems a bit odd. Yet, the decision isn't central to
the proposal and is still an open question.
The __module__ idea is nice. Will add it. Likewise with pickling.
The 'show' part of __repr__ was done for speed (localized access
versus accessing the enclosing scope. Will rename it to _show
to make it clear that it is not part of the API.
Thanks for the accolades and the suggestions.
Raymond
----- Original Message -----
From: "Michele Simionato" <michele.simionato at gmail.com>
To: <python-dev at python.org>
Sent: Monday, February 19, 2007 9:25 PM
Subject: Re: [Python-Dev] Py2.6 ideas
> Raymond Hettinger <raymond.hettinger <at> verizon.net> writes:
>> * Add a pure python named_tuple class to the collections module. I've been
>> using the class for about a year and found that it greatly improves the
>> usability of tuples as records.
>> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/500261
>
> The implementation of this recipe is really clean and I like it a lot
> (I even think of including it in our codebase), but there are a few issues
> that I would like to point out.
>
> 1. I find the camelcase confusing and I think that NamedTuple should be
> spelled namedtuple, since is a function, not a class. The fact that it
> returns classes does not count ;)
>
> 2. I agree with Giovanni Bajo, the constructor signature should be consistent
> with regular tuples. For instance I want to be able to map a named tuple
> over a record set as returned by fetchall.
>
> 3. I would like to pass the list of the fields as a sequence, not as
> a string. It would be more consistent and it would make easier
> the programmatic creation of NamedTuple classes at runtime.
>
> 4. I want help(MyNamedTuple) to work well; in particular it should
> display the right module name. That means
> that in the m dictionary you should add a __module__ attribute:
>
> __module__ = sys._getframe(1).f_globals['__name__']
>
> 5. The major issue is that pickle does work with named tuples since the
> __module__ attribute is wrong. The suggestion in #4 would solve even
> this issue for free.
>
> 6. The ability to pass a show function to the __repr__ feems over-engineering
> to me.
>
> In short, here is how I would change the recipe:
>
> import sys
> from operator import itemgetter
>
> def namedtuple(f):
> """Returns a new subclass of tuple with named fields.
>
> >>> Point = namedtuple('Point x y'.split())
> >>> Point.__doc__ # docstring for the new class
> 'Point(x, y)'
> >>> p = Point((11,), y=22) # instantiate with positional args or keywords
> >>> p[0] + p[1] # works just like the tuple (11, 22)
> 33
> >>> x, y = p # unpacks just like a tuple
> >>> x, y
> (11, 22)
> >>> p.x + p.y # fields also accessable by name
> 33
> >>> p # readable __repr__ with name=value style
> Point(x=11, y=22)
>
> """
> typename, field_names = f[0], f[1:]
> nargs = len(field_names)
>
> def __new__(cls, args=(), **kwds):
> if kwds:
> try:
> args += tuple(kwds[name] for name in field_names[len(args):])
> except KeyError, name:
> raise TypeError(
> '%s missing required argument: %s' % (typename, name))
> if len(args) != nargs:
> raise TypeError(
> '%s takes exactly %d arguments (%d given)' %
> (typename, nargs, len(args)))
> return tuple.__new__(cls, args)
>
> template = '%s(%s)' % (
> typename, ', '.join('%s=%%r' % name for name in field_names))
>
> def __repr__(self):
> return template % self
>
> m = dict(vars(tuple)) # pre-lookup superclass methods (for faster lookup)
> m.update(__doc__= '%s(%s)' % (typename, ', '.join(field_names)),
> __slots__ = (), # no per-instance dict
> __new__ = __new__,
> __repr__ = __repr__,
> __module__ = sys._getframe(1).f_globals['__name__'],
> )
> m.update((name, property(itemgetter(index)))
> for index, name in enumerate(field_names))
>
> return type(typename, (tuple,), m)
>
>
> if __name__ == '__main__':
> import doctest
> TestResults = namedtuple(['TestResults', 'failed', 'attempted'])
> print TestResults(doctest.testmod())
>
>
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe:
> http://mail.python.org/mailman/options/python-dev/python%40rcn.com
More information about the Python-Dev
mailing list