6

I get that as in the docs, I can use cmp_to_key to convert the old style comparison from python2 to a key function such as in python3, but I don't get what exactly is being returned. For instance:

def my_cmp(a, b):
    if(a == b):
        return 0
    elif(a < b):
        return -1
    else:
        return 1

my_key_fn = functools.cmp_to_key(my_cmp)

And then whenever I call my_key_fn on any number, the result is the address of a new function, with a new address for every invocation. For instance:

In [49]: my_key_fn(1)
Out[49]: <functools.KeyWrapper at address...>

I thought that the return value would give me some kind of a value that could be used as a sort key. Why am I getting a function Wrapper?

guptashark
  • 119
  • 7
  • 3
    [This](https://docs.python.org/3/howto/sorting.html#the-old-way-using-the-cmp-parameter) page provides a pure-Python implementation of `cmp_to_key`. – vaultah Dec 25 '17 at 18:55
  • @vaultah I don't see it there? – wim Dec 13 '22 at 07:06
  • @wim: It got removed in the 3.11 version of the docs. You can still see it in the [3.10 docs](https://docs.python.org/3.10/howto/sorting.html#the-old-way-using-the-cmp-parameter). – user2357112 Dec 13 '22 at 07:13

1 Answers1

0

There is a pure-Python implementation available in functools.py, reproduced below:

def cmp_to_key(mycmp):
    """Convert a cmp= function into a key= function"""
    class K(object):
        __slots__ = ['obj']
        def __init__(self, obj):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        __hash__ = None
    return K

The functools.KeyWrapper instances you've seen returned are not actually from this Python code, but a C implementation which you can find in _functoolsmodule.c. The technique is similar: it needn't return some kind of scalar value such as an integer, it only has to return things which can be compared with eachother. It achieves that just by defining the necessary comparison operators, which use the cmp function provided by the caller.

I thought that the return value would give me some kind of a value that could be used as a sort key. Why am I getting a function Wrapper?

It's not a value that can be used as a sort key, it is the sort key.

Inverting the normal ordering of integers or strings, for example:

def cmp_to_key(mycmp):
    class K:
        def __init__(self, obj):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
    return K

def cmp(x, y):
    return 1 if x < y else 0 if x == y else -1

key = cmp_to_key(cmp)

Demo:

>>> "foo" < "bar"
False
>>> key("foo") < key("bar")
True
>>> sorted([1,2,3], key=key)
[3, 2, 1]
wim
  • 338,267
  • 99
  • 616
  • 750