key argument for max?

Alex Martelli aleaxit at yahoo.com
Mon Oct 25 06:50:23 EDT 2004


Raymond Hettinger <python at rcn.com> wrote:
   ...
> from itertools import imap, izip, tee
> 
> def newmax(*args, **kwds):
>     """Works just like max() but allows an optional key= argument.
> 
>     If several keys are equal and maximum, may return any one of them.
>     """
>     if 'key' not in kwds:
>         return max(*args)
>     func = kwds['key']

Errors should not pass silently, unless explicitly silenced.  I don't
like the idea of somebody mistakenly calling newmax(foo, kye=bar) and
having to find out "downstream" that the mistaken 'kye=bar' was just
silently ignored.  So, I would suggest rather using something like:

      func = kwds.pop('key', None)
      if kwds:
          raise TypeError, (
            'newmax() got an unexpected keyword argument %r' %
            (kwds.keys()[0],))
      if func is None:
          return max(*args)

>     if len(args) == 1:
>         v = args[0]
>     else:
>         v = args
>     v1, v2 = tee(v)
>     return max(izip(imap(func, v1), v2))[-1]

Unfortunately, this does not replicate an important functionality you
gave to the key= argument of .sort: that items themselves are NEVER
compared, even when they have equal keys.  So, for example, I can have
something like:

>>> c=[2j, 3+1j, 3, 2, 1+3j]
>>> sorted(c, key=abs)
[2j, 2, 3, (3+1j), (1+3j)]

but newmax(c, key=abs) would raise a TypeError.

It suffices, of course, to add 'count' to the names imported from
itertools, and change the function's last statement to:

      return max(izip(imap(func, v1), count(), v2))[-1]

As a side effect, I think this makes
      newmax(foo, key=bar) === sorted(foo, key=bar)[-1]
for any foo and bar.  Not sure whether this stronger spec is worth
committing to, as opposed to 'may return any one of them'.  But it sure
seems to me that the guarantee that comes with key=, that two items with
equal keys will never get compared to break the tie, _IS_ well worth
preserving for all uses of key=... -- it saves the day when items just
cannot be compared, and it avoids a performance trap when items _can_ be
compared but comparisons may be costly.


Alex



More information about the Python-list mailing list