Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting key function that uses custom comparison [duplicate]

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?

like image 819
bignose Avatar asked Oct 15 '16 00:10

bignose


1 Answers

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.

like image 72
bignose Avatar answered Oct 03 '22 19:10

bignose