mirror of
https://github.com/python/cpython.git
synced 2025-08-22 17:55:18 +00:00
Issue #23132: Improve performance and introspection support of comparison
methods created by functool.total_ordering.
This commit is contained in:
parent
a2d86228d5
commit
697a526fa1
3 changed files with 69 additions and 39 deletions
|
@ -113,76 +113,85 @@ def wraps(wrapped,
|
||||||
# underlying user provided method. Using this scheme, the __gt__ derived
|
# underlying user provided method. Using this scheme, the __gt__ derived
|
||||||
# from a user provided __lt__ becomes:
|
# from a user provided __lt__ becomes:
|
||||||
#
|
#
|
||||||
# lambda self, other: _not_op_and_not_eq(self.__lt__, self, other))
|
# 'def __gt__(self, other):' + _not_op_and_not_eq % '__lt__'
|
||||||
|
|
||||||
def _not_op(op, other):
|
# "not a < b" handles "a >= b"
|
||||||
# "not a < b" handles "a >= b"
|
# "not a <= b" handles "a > b"
|
||||||
# "not a <= b" handles "a > b"
|
# "not a >= b" handles "a < b"
|
||||||
# "not a >= b" handles "a < b"
|
# "not a > b" handles "a <= b"
|
||||||
# "not a > b" handles "a <= b"
|
_not_op = '''
|
||||||
op_result = op(other)
|
op_result = self.%s(other)
|
||||||
if op_result is NotImplemented:
|
if op_result is NotImplemented:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return not op_result
|
return not op_result
|
||||||
|
'''
|
||||||
|
|
||||||
def _op_or_eq(op, self, other):
|
# "a > b or a == b" handles "a >= b"
|
||||||
# "a < b or a == b" handles "a <= b"
|
# "a < b or a == b" handles "a <= b"
|
||||||
# "a > b or a == b" handles "a >= b"
|
_op_or_eq = '''
|
||||||
op_result = op(other)
|
op_result = self.%s(other)
|
||||||
if op_result is NotImplemented:
|
if op_result is NotImplemented:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return op_result or self == other
|
return op_result or self == other
|
||||||
|
'''
|
||||||
|
|
||||||
def _not_op_and_not_eq(op, self, other):
|
# "not (a < b or a == b)" handles "a > b"
|
||||||
# "not (a < b or a == b)" handles "a > b"
|
# "not a < b and a != b" is equivalent
|
||||||
# "not a < b and a != b" is equivalent
|
# "not (a > b or a == b)" handles "a < b"
|
||||||
# "not (a > b or a == b)" handles "a < b"
|
# "not a > b and a != b" is equivalent
|
||||||
# "not a > b and a != b" is equivalent
|
_not_op_and_not_eq = '''
|
||||||
op_result = op(other)
|
op_result = self.%s(other)
|
||||||
if op_result is NotImplemented:
|
if op_result is NotImplemented:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return not op_result and self != other
|
return not op_result and self != other
|
||||||
|
'''
|
||||||
|
|
||||||
def _not_op_or_eq(op, self, other):
|
# "not a <= b or a == b" handles "a >= b"
|
||||||
# "not a <= b or a == b" handles "a >= b"
|
# "not a >= b or a == b" handles "a <= b"
|
||||||
# "not a >= b or a == b" handles "a <= b"
|
_not_op_or_eq = '''
|
||||||
op_result = op(other)
|
op_result = self.%s(other)
|
||||||
if op_result is NotImplemented:
|
if op_result is NotImplemented:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return not op_result or self == other
|
return not op_result or self == other
|
||||||
|
'''
|
||||||
|
|
||||||
def _op_and_not_eq(op, self, other):
|
# "a <= b and not a == b" handles "a < b"
|
||||||
# "a <= b and not a == b" handles "a < b"
|
# "a >= b and not a == b" handles "a > b"
|
||||||
# "a >= b and not a == b" handles "a > b"
|
_op_and_not_eq = '''
|
||||||
op_result = op(other)
|
op_result = self.%s(other)
|
||||||
if op_result is NotImplemented:
|
if op_result is NotImplemented:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
return op_result and self != other
|
return op_result and self != other
|
||||||
|
'''
|
||||||
|
|
||||||
def total_ordering(cls):
|
def total_ordering(cls):
|
||||||
"""Class decorator that fills in missing ordering methods"""
|
"""Class decorator that fills in missing ordering methods"""
|
||||||
convert = {
|
convert = {
|
||||||
'__lt__': [('__gt__', lambda self, other: _not_op_and_not_eq(self.__lt__, self, other)),
|
'__lt__': {'__gt__': _not_op_and_not_eq,
|
||||||
('__le__', lambda self, other: _op_or_eq(self.__lt__, self, other)),
|
'__le__': _op_or_eq,
|
||||||
('__ge__', lambda self, other: _not_op(self.__lt__, other))],
|
'__ge__': _not_op},
|
||||||
'__le__': [('__ge__', lambda self, other: _not_op_or_eq(self.__le__, self, other)),
|
'__le__': {'__ge__': _not_op_or_eq,
|
||||||
('__lt__', lambda self, other: _op_and_not_eq(self.__le__, self, other)),
|
'__lt__': _op_and_not_eq,
|
||||||
('__gt__', lambda self, other: _not_op(self.__le__, other))],
|
'__gt__': _not_op},
|
||||||
'__gt__': [('__lt__', lambda self, other: _not_op_and_not_eq(self.__gt__, self, other)),
|
'__gt__': {'__lt__': _not_op_and_not_eq,
|
||||||
('__ge__', lambda self, other: _op_or_eq(self.__gt__, self, other)),
|
'__ge__': _op_or_eq,
|
||||||
('__le__', lambda self, other: _not_op(self.__gt__, other))],
|
'__le__': _not_op},
|
||||||
'__ge__': [('__le__', lambda self, other: _not_op_or_eq(self.__ge__, self, other)),
|
'__ge__': {'__le__': _not_op_or_eq,
|
||||||
('__gt__', lambda self, other: _op_and_not_eq(self.__ge__, self, other)),
|
'__gt__': _op_and_not_eq,
|
||||||
('__lt__', lambda self, other: _not_op(self.__ge__, other))]
|
'__lt__': _not_op}
|
||||||
}
|
}
|
||||||
# Find user-defined comparisons (not those inherited from object).
|
# Find user-defined comparisons (not those inherited from object).
|
||||||
roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)]
|
roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)]
|
||||||
if not roots:
|
if not roots:
|
||||||
raise ValueError('must define at least one ordering operation: < > <= >=')
|
raise ValueError('must define at least one ordering operation: < > <= >=')
|
||||||
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
|
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
|
||||||
for opname, opfunc in convert[root]:
|
for opname, opfunc in convert[root].items():
|
||||||
if opname not in roots:
|
if opname not in roots:
|
||||||
opfunc.__name__ = opname
|
namespace = {}
|
||||||
|
exec('def %s(self, other):%s' % (opname, opfunc % root), namespace)
|
||||||
|
opfunc = namespace[opname]
|
||||||
|
opfunc.__qualname__ = '%s.%s' % (cls.__qualname__, opname)
|
||||||
|
opfunc.__module__ = cls.__module__
|
||||||
opfunc.__doc__ = getattr(int, opname).__doc__
|
opfunc.__doc__ = getattr(int, opname).__doc__
|
||||||
setattr(cls, opname, opfunc)
|
setattr(cls, opname, opfunc)
|
||||||
return cls
|
return cls
|
||||||
|
|
|
@ -880,6 +880,24 @@ class TestTotalOrdering(unittest.TestCase):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
a <= b
|
a <= b
|
||||||
|
|
||||||
|
def test_pickle(self):
|
||||||
|
for proto in range(4, pickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
for name in '__lt__', '__gt__', '__le__', '__ge__':
|
||||||
|
with self.subTest(method=name, proto=proto):
|
||||||
|
method = getattr(Orderable_LT, name)
|
||||||
|
method_copy = pickle.loads(pickle.dumps(method, proto))
|
||||||
|
self.assertIs(method_copy, method)
|
||||||
|
|
||||||
|
@functools.total_ordering
|
||||||
|
class Orderable_LT:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.value < other.value
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.value == other.value
|
||||||
|
|
||||||
|
|
||||||
class TestLRU(unittest.TestCase):
|
class TestLRU(unittest.TestCase):
|
||||||
|
|
||||||
def test_lru(self):
|
def test_lru(self):
|
||||||
|
|
|
@ -196,6 +196,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #23132: Improve performance and introspection support of comparison
|
||||||
|
methods created by functool.total_ordering.
|
||||||
|
|
||||||
- Issue #19776: Add a expanduser() method on Path objects.
|
- Issue #19776: Add a expanduser() method on Path objects.
|
||||||
|
|
||||||
- Issue #23112: Fix SimpleHTTPServer to correctly carry the query string and
|
- Issue #23112: Fix SimpleHTTPServer to correctly carry the query string and
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue