5

Python 3 removes the cmp parameter to sorting functions:

builtin.sorted() and list.sort() no longer accept the cmp argument providing a comparison function. Use the key argument instead.

That's fine for orderings that can be determined just by inspecting a single item in the sequence (e.g. key=str.lower). But what about custom orderings that must have two items for inspection to determine their ordering?

$ python2
Python 2.7.12+ (default, Sep  1 2016, 20:27:38)
[…]

>>> digits = ['3', '30', '34', '32', '9', '5']
>>> sorted(
...         digits,
...         cmp=(lambda a, b: cmp(a+b, b+a)),
...         reverse=True)
['9', '5', '34', '3', '32', '30']

That two-parameter cmp function can't be replaced with a one-parameter key function, can it?

bignose
  • 27,414
  • 13
  • 72
  • 104
  • That is a really bad target @TigerhawkT3, maybe this http://stackoverflow.com/questions/20202418/why-is-the-cmp-parameter-removed-from-sort-sorted-in-python3-0 is better? – Dimitris Fasarakis Hilliard Oct 15 '16 at 00:30
  • 1
    @JimFasarakis-Hilliard - Why is it bad? The top answer explains how to implement this type of comparison sorting with a modern `key` argument, and another answer suggests `functools.cmp_to_key`, so there really is nothing unique about this new question. – TigerhawkT3 Oct 15 '16 at 00:31
  • 1
    There are many additional possible duplicate questions, like [this one](http://stackoverflow.com/questions/30043067/python3-style-sorting-old-cmp-method-functionality-in-new-key-mechanism) and [this one](http://stackoverflow.com/questions/20202418/why-is-the-cmp-parameter-removed-from-sort-sorted-in-python3-0), and probably several more if I had done more than 30 seconds of searching. – TigerhawkT3 Oct 15 '16 at 00:34
  • @TigerhawkT3 Fair enough, just thought the alternative dup *might* be better :-). It's linked now so I'm fine with that. – Dimitris Fasarakis Hilliard Oct 15 '16 at 00:35

1 Answers1

7

Use the functools.cmp_to_key helper.

>>> import functools
>>> digits = ['3', '30', '34', '32', '9', '5']
>>> sorted(
...         digits,
...         key=functools.cmp_to_key(lambda a, b: cmp(a+b, b+a)),
...         reverse=True)
['9', '5', '34', '3', '32', '30']

The Python 3 sorting functions take a ‘key’ parameter:

key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly).

The functools.cmp_to_key helper is designed to help you transition to that style:

functools.cmp_to_key(func)

Transform an old-style comparison function to a key function. […] This function is primarily used as a transition tool for programs being converted from Python 2 which supported the use of comparison functions.

This works in the latest Python 2 and Python 3.

The trick is done by creating a key function that takes an item for comparison, and returns a custom object, which knows how to compare itself as specified by your comparison function.

>>> key_func = functools.cmp_to_key(lambda a, b: cmp(a+b, b+a))
>>> key_func("32")
<functools.K object at 0x7f6781ce0980>
>>> key_func("32") < key_func("5")
True

See the Sorting HOWTO for this and other tricks.

bignose
  • 27,414
  • 13
  • 72
  • 104