diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2015-01-01 13:23:12 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2015-01-01 13:23:12 (GMT) |
commit | 697a526fa1426b4d490cfa98bbf0b0c2889fa759 (patch) | |
tree | 9a82b27b69ea5a91d1773da65a3fd6c73b556ca2 /Lib | |
parent | a2d86228d52ba4d2d31c3dbb9f53670b1e5f4951 (diff) | |
download | cpython-697a526fa1426b4d490cfa98bbf0b0c2889fa759.zip cpython-697a526fa1426b4d490cfa98bbf0b0c2889fa759.tar.gz cpython-697a526fa1426b4d490cfa98bbf0b0c2889fa759.tar.bz2 |
Issue #23132: Improve performance and introspection support of comparison
methods created by functool.total_ordering.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/functools.py | 91 | ||||
-rw-r--r-- | Lib/test/test_functools.py | 18 |
2 files changed, 68 insertions, 41 deletions
diff --git a/Lib/functools.py b/Lib/functools.py index c1ca850..db8cc82 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -113,76 +113,85 @@ def wraps(wrapped, # underlying user provided method. Using this scheme, the __gt__ derived # from a user provided __lt__ becomes: # -# lambda self, other: _not_op_and_not_eq(self.__lt__, self, other)) - -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" - op_result = op(other) +# 'def __gt__(self, other):' + _not_op_and_not_eq % '__lt__' + +# "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 = self.%s(other) if op_result is NotImplemented: return NotImplemented 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" - op_result = op(other) +# "a > b or a == b" handles "a >= b" +# "a < b or a == b" handles "a <= b" +_op_or_eq = ''' + op_result = self.%s(other) if op_result is NotImplemented: return NotImplemented 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 and a != b" is equivalent - # "not (a > b or a == b)" handles "a < b" - # "not a > b and a != b" is equivalent - op_result = op(other) +''' + +# "not (a < b or a == b)" handles "a > b" +# "not a < b and a != b" is equivalent +# "not (a > b or a == b)" handles "a < b" +# "not a > b and a != b" is equivalent +_not_op_and_not_eq = ''' + op_result = self.%s(other) if op_result is NotImplemented: return NotImplemented 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" - op_result = op(other) +# "not a <= b or a == b" handles "a >= b" +# "not a >= b or a == b" handles "a <= b" +_not_op_or_eq = ''' + op_result = self.%s(other) if op_result is NotImplemented: return NotImplemented 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" - op_result = op(other) +# "a <= b and not a == b" handles "a < b" +# "a >= b and not a == b" handles "a > b" +_op_and_not_eq = ''' + op_result = self.%s(other) if op_result is NotImplemented: return NotImplemented return op_result and self != other +''' def total_ordering(cls): """Class decorator that fills in missing ordering methods""" convert = { - '__lt__': [('__gt__', lambda self, other: _not_op_and_not_eq(self.__lt__, self, other)), - ('__le__', lambda self, other: _op_or_eq(self.__lt__, self, other)), - ('__ge__', lambda self, other: _not_op(self.__lt__, other))], - '__le__': [('__ge__', lambda self, other: _not_op_or_eq(self.__le__, self, other)), - ('__lt__', lambda self, other: _op_and_not_eq(self.__le__, self, other)), - ('__gt__', lambda self, other: _not_op(self.__le__, other))], - '__gt__': [('__lt__', lambda self, other: _not_op_and_not_eq(self.__gt__, self, other)), - ('__ge__', lambda self, other: _op_or_eq(self.__gt__, self, other)), - ('__le__', lambda self, other: _not_op(self.__gt__, other))], - '__ge__': [('__le__', lambda self, other: _not_op_or_eq(self.__ge__, self, other)), - ('__gt__', lambda self, other: _op_and_not_eq(self.__ge__, self, other)), - ('__lt__', lambda self, other: _not_op(self.__ge__, other))] + '__lt__': {'__gt__': _not_op_and_not_eq, + '__le__': _op_or_eq, + '__ge__': _not_op}, + '__le__': {'__ge__': _not_op_or_eq, + '__lt__': _op_and_not_eq, + '__gt__': _not_op}, + '__gt__': {'__lt__': _not_op_and_not_eq, + '__ge__': _op_or_eq, + '__le__': _not_op}, + '__ge__': {'__le__': _not_op_or_eq, + '__gt__': _op_and_not_eq, + '__lt__': _not_op} } # 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)] if not roots: raise ValueError('must define at least one ordering operation: < > <= >=') 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: - 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__ setattr(cls, opname, opfunc) return cls diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 1012053..fbb43e4 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -880,6 +880,24 @@ class TestTotalOrdering(unittest.TestCase): with self.assertRaises(TypeError): 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): def test_lru(self): |