]> granicus.if.org Git - python/commitdiff
Add functools.CmpToKey()
authorRaymond Hettinger <python@rcn.com>
Sun, 4 Apr 2010 18:34:45 +0000 (18:34 +0000)
committerRaymond Hettinger <python@rcn.com>
Sun, 4 Apr 2010 18:34:45 +0000 (18:34 +0000)
Doc/library/functools.rst
Lib/functools.py
Misc/NEWS

index 6dc9f0abed8568b99d1bf11040fc348af51d532b..3337ead1c54222802cd4d8860380f57d74fa5ced 100644 (file)
@@ -17,6 +17,28 @@ function for the purposes of this module.
 
 The :mod:`functools` module defines the following functions:
 
+..  function:: CmpToKey(func)
+
+    Transform an old-style comparison function to a key-function.  Used with
+    tools that accept key functions (such as :func:`sorted`, :func:`min`,
+    :func:`max`, :func:`heapq.nlargest`, :func:`heapq.nsmallest`,
+    :func:`itertools.groupby`).
+    This function is primarily used as a transition tool for programs
+    being converted to Py3.x where comparison functions are no longer
+    supported.
+
+    A compare function is any callable that accept two arguments, compares
+    them, and returns a negative number for less-than, zero for equality,
+    or a positive number for greater-than.  A key function is a callable
+    that accepts one argument and returns another value that indicates
+    the position in the desired collation sequence.
+
+    Example::
+
+        sorted(iterable, key=CmpToKey(locale.strcoll))  # locale-aware sort order
+
+   .. versionadded:: 2.7
+
 .. function:: total_ordering(cls)
 
    Given a class defining one or more rich comparison ordering methods, this
index a54f03083231379c642f7d6e0bc65e391f07bbd8..d31b09042e5a1ade3a4bf17d3da4e2e27fb70189 100644 (file)
@@ -49,3 +49,48 @@ def wraps(wrapped,
     """
     return partial(update_wrapper, wrapped=wrapped,
                    assigned=assigned, updated=updated)
+
+def total_ordering(cls):
+    'Class decorator that fills-in missing ordering methods'
+    convert = {
+        '__lt__': [('__gt__', lambda self, other: other < self),
+                   ('__le__', lambda self, other: not other < self),
+                   ('__ge__', lambda self, other: not self < other)],
+        '__le__': [('__ge__', lambda self, other: other <= self),
+                   ('__lt__', lambda self, other: not other <= self),
+                   ('__gt__', lambda self, other: not self <= other)],
+        '__gt__': [('__lt__', lambda self, other: other > self),
+                   ('__ge__', lambda self, other: not other > self),
+                   ('__le__', lambda self, other: not self > other)],
+        '__ge__': [('__le__', lambda self, other: other >= self),
+                   ('__gt__', lambda self, other: not other >= self),
+                   ('__lt__', lambda self, other: not self >= other)]
+    }
+    roots = set(dir(cls)) & set(convert)
+    assert roots, 'must define at least one ordering operation: < > <= >='
+    root = max(roots)       # prefer __lt __ to __le__ to __gt__ to __ge__
+    for opname, opfunc in convert[root]:
+        if opname not in roots:
+            opfunc.__name__ = opname
+            opfunc.__doc__ = getattr(int, opname).__doc__
+            setattr(cls, opname, opfunc)
+    return cls
+
+def CmpToKey(mycmp):
+    'Convert a cmp= function into a key= function'
+    class K(object):
+        def __init__(self, obj, *args):
+            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
+        def __ne__(self, other):
+            return mycmp(self.obj, other.obj) != 0
+    return K
index af1dd20d24998c8c93a17a38b7d72d2545bf7363..0f1f225bc72c6f379ea01d531292fe0a2288fb5e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -57,6 +57,12 @@ Library
 
 - collections.Counter() now supports a subtract() method.
 
+- the functools module now has a total_ordering() class decorator
+  to simplify the specifying rich comparisons.
+
+- The functools module also adds CmpToKey() as a tool to transition
+  old-style comparison functions to new-style key-functions.
+
 - Issue #8294: The Fraction constructor now accepts Decimal and float
   instances directly.