Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
c50846aa
Commit
c50846aa
authored
Apr 05, 2010
by
Raymond Hettinger
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Forward port total_ordering() and cmp_to_key().
parent
5daab451
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
187 additions
and
26 deletions
+187
-26
Doc/library/functions.rst
Doc/library/functions.rst
+2
-3
Doc/library/functools.rst
Doc/library/functools.rst
+45
-0
Doc/library/stdtypes.rst
Doc/library/stdtypes.rst
+3
-0
Doc/reference/datamodel.rst
Doc/reference/datamodel.rst
+1
-2
Lib/functools.py
Lib/functools.py
+47
-0
Lib/pstats.py
Lib/pstats.py
+2
-10
Lib/test/test_functools.py
Lib/test/test_functools.py
+83
-1
Lib/unittest/loader.py
Lib/unittest/loader.py
+2
-1
Lib/unittest/util.py
Lib/unittest/util.py
+0
-9
Misc/NEWS
Misc/NEWS
+2
-0
No files found.
Doc/library/functions.rst
View file @
c50846aa
...
@@ -1014,9 +1014,8 @@ are always available. They are listed here in alphabetical order.
...
@@ -1014,9 +1014,8 @@ are always available. They are listed here in alphabetical order.
*reverse* is a boolean value. If set to ``True``, then the list elements are
*reverse* is a boolean value. If set to ``True``, then the list elements are
sorted as if each comparison were reversed.
sorted as if each comparison were reversed.
To convert an old-style *cmp* function to a *key* function, see the
Use :func:`functools.cmp_to_key` to convert an
`CmpToKey recipe in the ASPN cookbook
old-style *cmp* function to a *key* function.
<http://code.activestate.com/recipes/576653/>`_\.
For sorting examples and a brief sorting tutorial, see `Sorting HowTo
For sorting examples and a brief sorting tutorial, see `Sorting HowTo
<http://wiki.python.org/moin/HowTo/Sorting/>`_\.
<http://wiki.python.org/moin/HowTo/Sorting/>`_\.
...
...
Doc/library/functools.rst
View file @
c50846aa
...
@@ -15,6 +15,51 @@ function for the purposes of this module.
...
@@ -15,6 +15,51 @@ function for the purposes of this module.
The :mod:`functools` module defines the following functions:
The :mod:`functools` module defines the following functions:
.. 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`,
:func:`max`, :func:`heapq.nlargest`, :func:`heapq.nsmallest`,
:func:`itertools.groupby`).
This function is primarily used as a transition tool for programs
being converted from Py2.x which supported the use of comparison
functions.
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=cmp_to_key(locale.strcoll)) # locale-aware sort order
.. versionadded:: 3.2
.. function:: total_ordering(cls)
Given a class defining one or more rich comparison ordering methods, this
class decorator supplies the rest. This simplies the effort involved
in specifying all of the possible rich comparison operations:
The class must define one of :meth:`__lt__`, :meth:`__le__`,
:meth:`__gt__`, or :meth:`__ge__`.
In addition, the class should supply an :meth:`__eq__` method.
For example::
@total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
.. versionadded:: 3.2
.. function:: partial(func, *args, **keywords)
.. function:: partial(func, *args, **keywords)
Return a new :class:`partial` object which when called will behave like *func*
Return a new :class:`partial` object which when called will behave like *func*
...
...
Doc/library/stdtypes.rst
View file @
c50846aa
...
@@ -1567,6 +1567,9 @@ Notes:
...
@@ -1567,6 +1567,9 @@ Notes:
*key* specifies a function of one argument that is used to extract a comparison
*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``.
key from each list element: ``key=str.lower``. The default value is ``None``.
Use :func:`functools.cmp_to_key` to convert an
old-style *cmp* function to a *key* function.
*reverse* is a boolean value. If set to ``True``, then the list elements are
*reverse* is a boolean value. If set to ``True``, then the list elements are
sorted as if each comparison were reversed.
sorted as if each comparison were reversed.
...
...
Doc/reference/datamodel.rst
View file @
c50846aa
...
@@ -1209,8 +1209,7 @@ Basic customization
...
@@ -1209,8 +1209,7 @@ Basic customization
Arguments to rich comparison methods are never coerced.
Arguments to rich comparison methods are never coerced.
To automatically generate ordering operations from a single root operation,
To automatically generate ordering operations from a single root operation,
see the `Total Ordering recipe in the ASPN cookbook
see :func:`functools.total_ordering`.
<http://code.activestate.com/recipes/576529/>`_\.
.. method:: object.__hash__(self)
.. method:: object.__hash__(self)
...
...
Lib/functools.py
View file @
c50846aa
...
@@ -49,3 +49,50 @@ def wraps(wrapped,
...
@@ -49,3 +49,50 @@ def wraps(wrapped,
"""
"""
return
partial
(
update_wrapper
,
wrapped
=
wrapped
,
return
partial
(
update_wrapper
,
wrapped
=
wrapped
,
assigned
=
assigned
,
updated
=
updated
)
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
cmp_to_key
(
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
def
__hash__
(
self
):
raise
TypeError
(
'hash not implemented'
)
return
K
Lib/pstats.py
View file @
c50846aa
...
@@ -37,6 +37,7 @@ import os
...
@@ -37,6 +37,7 @@ import os
import
time
import
time
import
marshal
import
marshal
import
re
import
re
from
functools
import
cmp_to_key
__all__
=
[
"Stats"
]
__all__
=
[
"Stats"
]
...
@@ -226,7 +227,7 @@ class Stats:
...
@@ -226,7 +227,7 @@ class Stats:
stats_list
.
append
((
cc
,
nc
,
tt
,
ct
)
+
func
+
stats_list
.
append
((
cc
,
nc
,
tt
,
ct
)
+
func
+
(
func_std_string
(
func
),
func
))
(
func_std_string
(
func
),
func
))
stats_list
.
sort
(
key
=
CmpToK
ey
(
TupleComp
(
sort_tuple
).
compare
))
stats_list
.
sort
(
key
=
cmp_to_k
ey
(
TupleComp
(
sort_tuple
).
compare
))
self
.
fcn_list
=
fcn_list
=
[]
self
.
fcn_list
=
fcn_list
=
[]
for
tuple
in
stats_list
:
for
tuple
in
stats_list
:
...
@@ -458,15 +459,6 @@ class TupleComp:
...
@@ -458,15 +459,6 @@ class TupleComp:
return
direction
return
direction
return
0
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)
# func_name is a triple (file:string, line:int, name:string)
...
...
Lib/test/test_functools.py
View file @
c50846aa
...
@@ -364,7 +364,89 @@ class TestReduce(unittest.TestCase):
...
@@ -364,7 +364,89 @@ class TestReduce(unittest.TestCase):
d
=
{
"one"
:
1
,
"two"
:
2
,
"three"
:
3
}
d
=
{
"one"
:
1
,
"two"
:
2
,
"three"
:
3
}
self
.
assertEqual
(
self
.
func
(
add
,
d
),
""
.
join
(
d
.
keys
()))
self
.
assertEqual
(
self
.
func
(
add
,
d
),
""
.
join
(
d
.
keys
()))
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_hash
(
self
):
def
mycmp
(
x
,
y
):
return
y
-
x
key
=
functools
.
cmp_to_key
(
mycmp
)
k
=
key
(
10
)
self
.
assertRaises
(
TypeError
,
hash
(
k
))
class
TestTotalOrdering
(
unittest
.
TestCase
):
def
test_total_ordering_lt
(
self
):
@
functools
.
total_ordering
class
A
:
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__lt__
(
self
,
other
):
return
self
.
value
<
other
.
value
self
.
assert_
(
A
(
1
)
<
A
(
2
))
self
.
assert_
(
A
(
2
)
>
A
(
1
))
self
.
assert_
(
A
(
1
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
1
))
self
.
assert_
(
A
(
2
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
2
))
def
test_total_ordering_le
(
self
):
@
functools
.
total_ordering
class
A
:
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__le__
(
self
,
other
):
return
self
.
value
<=
other
.
value
self
.
assert_
(
A
(
1
)
<
A
(
2
))
self
.
assert_
(
A
(
2
)
>
A
(
1
))
self
.
assert_
(
A
(
1
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
1
))
self
.
assert_
(
A
(
2
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
2
))
def
test_total_ordering_gt
(
self
):
@
functools
.
total_ordering
class
A
:
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__gt__
(
self
,
other
):
return
self
.
value
>
other
.
value
self
.
assert_
(
A
(
1
)
<
A
(
2
))
self
.
assert_
(
A
(
2
)
>
A
(
1
))
self
.
assert_
(
A
(
1
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
1
))
self
.
assert_
(
A
(
2
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
2
))
def
test_total_ordering_ge
(
self
):
@
functools
.
total_ordering
class
A
:
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__ge__
(
self
,
other
):
return
self
.
value
>=
other
.
value
self
.
assert_
(
A
(
1
)
<
A
(
2
))
self
.
assert_
(
A
(
2
)
>
A
(
1
))
self
.
assert_
(
A
(
1
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
1
))
self
.
assert_
(
A
(
2
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
2
))
def
test_total_ordering_no_overwrite
(
self
):
# new methods should not overwrite existing
@
functools
.
total_ordering
class
A
(
int
):
raise
Exception
()
self
.
assert_
(
A
(
1
)
<
A
(
2
))
self
.
assert_
(
A
(
2
)
>
A
(
1
))
self
.
assert_
(
A
(
1
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
1
))
self
.
assert_
(
A
(
2
)
<=
A
(
2
))
self
.
assert_
(
A
(
2
)
>=
A
(
2
))
def
test_main
(
verbose
=
None
):
def
test_main
(
verbose
=
None
):
...
...
Lib/unittest/loader.py
View file @
c50846aa
...
@@ -5,6 +5,7 @@ import re
...
@@ -5,6 +5,7 @@ import re
import
sys
import
sys
import
traceback
import
traceback
import
types
import
types
import
functools
from
fnmatch
import
fnmatch
from
fnmatch
import
fnmatch
...
@@ -141,7 +142,7 @@ class TestLoader(object):
...
@@ -141,7 +142,7 @@ class TestLoader(object):
testFnNames
=
testFnNames
=
list
(
filter
(
isTestMethod
,
testFnNames
=
testFnNames
=
list
(
filter
(
isTestMethod
,
dir
(
testCaseClass
)))
dir
(
testCaseClass
)))
if
self
.
sortTestMethodsUsing
:
if
self
.
sortTestMethodsUsing
:
testFnNames
.
sort
(
key
=
util
.
CmpToK
ey
(
self
.
sortTestMethodsUsing
))
testFnNames
.
sort
(
key
=
functools
.
cmp_to_k
ey
(
self
.
sortTestMethodsUsing
))
return
testFnNames
return
testFnNames
def
discover
(
self
,
start_dir
,
pattern
=
'test*.py'
,
top_level_dir
=
None
):
def
discover
(
self
,
start_dir
,
pattern
=
'test*.py'
,
top_level_dir
=
None
):
...
...
Lib/unittest/util.py
View file @
c50846aa
...
@@ -70,15 +70,6 @@ def unorderable_list_difference(expected, actual):
...
@@ -70,15 +70,6 @@ def unorderable_list_difference(expected, actual):
# anything left in actual is unexpected
# anything left in actual is unexpected
return
missing
,
actual
return
missing
,
actual
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
)
==
-
1
return
K
def
three_way_cmp
(
x
,
y
):
def
three_way_cmp
(
x
,
y
):
"""Return -1 if x < y, 0 if x == y and 1 if x > y"""
"""Return -1 if x < y, 0 if x == y and 1 if x > y"""
return
(
x
>
y
)
-
(
x
<
y
)
return
(
x
>
y
)
-
(
x
<
y
)
Misc/NEWS
View file @
c50846aa
...
@@ -303,6 +303,8 @@ C-API
...
@@ -303,6 +303,8 @@ C-API
Library
Library
-------
-------
- Add functools.total_ordering() and functools.cmp_to_key().
- Issue #8257: The Decimal construct now accepts a float instance
- Issue #8257: The Decimal construct now accepts a float instance
directly, converting that float to a Decimal of equal value:
directly, converting that float to a Decimal of equal value:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment