From bb006cf26cc41aefcddc8f06722c524826aacefa Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 4 Apr 2010 21:45:01 +0000 Subject: [PATCH] Add tests for cmp_to_key. Adopt PEP 8 compliant function name. Factor-out existing uses cmp_to_key. Update documentation to use internal pointers instead of external resource. --- Doc/library/functions.rst | 5 ++--- Doc/library/functools.rst | 4 ++-- Doc/library/stdtypes.rst | 3 ++- Lib/functools.py | 2 +- Lib/pstats.py | 13 ++----------- Lib/test/test_functools.py | 7 ++++++- Lib/unittest/loader.py | 12 +----------- Misc/NEWS | 2 +- 8 files changed, 17 insertions(+), 31 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 0f4531912eb..cfb1945988f 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1154,9 +1154,8 @@ available. They are listed here in alphabetical order. In general, the *key* and *reverse* conversion processes are much faster than specifying an equivalent *cmp* function. This is because *cmp* is called multiple times for each list element while *key* and *reverse* touch - each element only once. To convert an old-style *cmp* function to a *key* - function, see the `CmpToKey recipe in the ASPN cookbook - `_\. + each element only once. Use :func:`functools.cmp_to_key` to convert an + old-style *cmp* function to a *key* function. For sorting examples and a brief sorting tutorial, see `Sorting HowTo `_\. diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index 3337ead1c54..15c4a95ab49 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -17,7 +17,7 @@ function for the purposes of this module. The :mod:`functools` module defines the following functions: -.. function:: CmpToKey(func) +.. function:: cmp_to_key(func) Transform an old-style comparison function to a key-function. Used with tools that accept key functions (such as :func:`sorted`, :func:`min`, @@ -35,7 +35,7 @@ The :mod:`functools` module defines the following functions: Example:: - sorted(iterable, key=CmpToKey(locale.strcoll)) # locale-aware sort order + sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order .. versionadded:: 2.7 diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 330768589d3..0a6178bf941 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1618,7 +1618,8 @@ Notes: In general, the *key* and *reverse* conversion processes are much faster than specifying an equivalent *cmp* function. This is because *cmp* is called multiple times for each list element while *key* and *reverse* touch each - element only once. + element only once. Use :func:`functools.cmp_to_key` to convert an + old-style *cmp* function to a *key* function. .. versionchanged:: 2.3 Support for ``None`` as an equivalent to omitting *cmp* was added. diff --git a/Lib/functools.py b/Lib/functools.py index d31b09042e5..ad1cccc77f0 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -76,7 +76,7 @@ def total_ordering(cls): setattr(cls, opname, opfunc) return cls -def CmpToKey(mycmp): +def cmp_to_key(mycmp): 'Convert a cmp= function into a key= function' class K(object): def __init__(self, obj, *args): diff --git a/Lib/pstats.py b/Lib/pstats.py index 0effa1c41c5..8b6081040a5 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -37,6 +37,7 @@ import os import time import marshal import re +from functools import cmp_to_key __all__ = ["Stats"] @@ -238,7 +239,7 @@ class Stats: stats_list.append((cc, nc, tt, ct) + func + (func_std_string(func), func)) - stats_list.sort(key=CmpToKey(TupleComp(sort_tuple).compare)) + stats_list.sort(key=cmp_to_key(TupleComp(sort_tuple).compare)) self.fcn_list = fcn_list = [] for tuple in stats_list: @@ -471,16 +472,6 @@ class TupleComp: return direction return 0 -def CmpToKey(mycmp): - """Convert a cmp= function into a key= function""" - class K(object): - def __init__(self, obj): - self.obj = obj - def __lt__(self, other): - return mycmp(self.obj, other.obj) == -1 - return K - - #************************************************************************** # func_name is a triple (file:string, line:int, name:string) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 2549e05c149..44992b8c5bb 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -338,7 +338,12 @@ class TestReduce(unittest.TestCase): self.assertEqual(reduce(42, "", "1"), "1") # func is never called with one item self.assertRaises(TypeError, reduce, 42, (42, 42)) - +class TestCmpToKey(unittest.TestCase): + def test_cmp_to_key(self): + def mycmp(x, y): + return y - x + self.assertEqual(sorted(range(5), key=functools.cmp_to_key(mycmp)), + [4, 3, 2, 1, 0]) def test_main(verbose=None): diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py index e0b8585209b..f0cc157415f 100644 --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -6,23 +6,13 @@ import sys import traceback import types +from functools import cmp_to_key as _CmpToKey from fnmatch import fnmatch from . import case, suite __unittest = True - -def _CmpToKey(mycmp): - 'Convert a cmp= function into a key= function' - class K(object): - def __init__(self, obj): - self.obj = obj - def __lt__(self, other): - return mycmp(self.obj, other.obj) == -1 - return K - - # what about .pyc or .pyo (etc) # we would need to avoid loading the same tests multiple times # from '.py', '.pyc' *and* '.pyo' diff --git a/Misc/NEWS b/Misc/NEWS index 0f1f225bc72..4cb76adc01b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -60,7 +60,7 @@ Library - 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 +- The functools module also adds cmp_to_key() as a tool to transition old-style comparison functions to new-style key-functions. - Issue #8294: The Fraction constructor now accepts Decimal and float