[Python-checkins] cpython: Issue #23132: Improve performance and introspection support of comparison

serhiy.storchaka python-checkins at python.org
Thu Jan 1 14:28:35 CET 2015


https://hg.python.org/cpython/rev/4e85df8b3ea6
changeset: 94003:4e85df8b3ea6
user: Serhiy Storchaka <storchaka at gmail.com>
date: Thu Jan 01 15:23:12 2015 +0200
summary:
 Issue #23132: Improve performance and introspection support of comparison
methods created by functool.total_ordering.
files:
 Lib/functools.py | 87 ++++++++++++++-----------
 Lib/test/test_functools.py | 18 +++++
 Misc/NEWS | 3 +
 3 files changed, 69 insertions(+), 39 deletions(-)
diff --git a/Lib/functools.py b/Lib/functools.py
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -113,76 +113,85 @@
 # 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 __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"
- op_result = 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_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
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -880,6 +880,24 @@
 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)
+
+ at 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):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -196,6 +196,9 @@
 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 #23112: Fix SimpleHTTPServer to correctly carry the query string and
-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /