From a551f31d482e9feb61f80fe8cf1d1e615e4e8d50 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 4 Apr 2010 18:34:45 +0000 Subject: Add functools.CmpToKey() --- Doc/library/functools.rst | 22 ++++++++++++++++++++++ Lib/functools.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 6 ++++++ 3 files changed, 73 insertions(+) diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 6dc9f0a..3337ead 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -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 diff --git a/Lib/functools.py b/Lib/functools.py index a54f030..d31b090 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -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 diff --git a/Misc/NEWS b/Misc/NEWS index af1dd20..0f1f225 100644 --- 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. -- cgit v0.12