diff options
-rw-r--r-- | Doc/reference/datamodel.rst | 22 | ||||
-rw-r--r-- | Lib/test/test_binop.py | 23 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
3 files changed, 38 insertions, 10 deletions
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index dda18ba..4acf13e 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1266,10 +1266,14 @@ Basic customization context (e.g., in the condition of an ``if`` statement), Python will call :func:`bool` on the value to determine if the result is true or false. - There are no implied relationships among the comparison operators. The truth - of ``x==y`` does not imply that ``x!=y`` is false. Accordingly, when - defining :meth:`__eq__`, one should also define :meth:`__ne__` so that the - operators will behave as expected. See the paragraph on :meth:`__hash__` for + By default, :meth:`__ne__` delegates to :meth:`__eq__` and + inverts the result unless it is ``NotImplemented``. There are no other + implied relationships among the comparison operators, for example, + the truth of ``(x<y or x==y)`` does not imply ``x<=y``. + To automatically generate ordering operations from a single root operation, + see :func:`functools.total_ordering`. + + See the paragraph on :meth:`__hash__` for some important notes on creating :term:`hashable` objects which support custom comparison operations and are usable as dictionary keys. @@ -1278,11 +1282,11 @@ Basic customization rather, :meth:`__lt__` and :meth:`__gt__` are each other's reflection, :meth:`__le__` and :meth:`__ge__` are each other's reflection, and :meth:`__eq__` and :meth:`__ne__` are their own reflection. - - Arguments to rich comparison methods are never coerced. - - To automatically generate ordering operations from a single root operation, - see :func:`functools.total_ordering`. + If the operands are of different types, and right operand's type is + a direct or indirect subclass of the left operand's type, + the reflected method of the right operand has priority, otherwise + the left operand's method has priority. Virtual subclassing is + not considered. .. method:: object.__hash__(self) diff --git a/Lib/test/test_binop.py b/Lib/test/test_binop.py index 9c4c18e..963aa01 100644 --- a/Lib/test/test_binop.py +++ b/Lib/test/test_binop.py @@ -3,6 +3,7 @@ import unittest from test import support from operator import eq, ne, lt, gt, le, ge +from abc import ABCMeta def gcd(a, b): """Greatest common divisor using Euclid's algorithm.""" @@ -332,7 +333,7 @@ class A(OperationLogger): self.log_operation('A.__ge__') return NotImplemented -class B(OperationLogger): +class B(OperationLogger, metaclass=ABCMeta): def __eq__(self, other): self.log_operation('B.__eq__') return NotImplemented @@ -354,6 +355,20 @@ class C(B): self.log_operation('C.__ge__') return NotImplemented +class V(OperationLogger): + """Virtual subclass of B""" + def __eq__(self, other): + self.log_operation('V.__eq__') + return NotImplemented + def __le__(self, other): + self.log_operation('V.__le__') + return NotImplemented + def __ge__(self, other): + self.log_operation('V.__ge__') + return NotImplemented +B.register(V) + + class OperationOrderTests(unittest.TestCase): def test_comparison_orders(self): self.assertEqual(op_sequence(eq, A, A), ['A.__eq__', 'A.__eq__']) @@ -369,8 +384,14 @@ class OperationOrderTests(unittest.TestCase): self.assertEqual(op_sequence(le, B, C), ['C.__ge__', 'B.__le__']) self.assertEqual(op_sequence(le, C, B), ['C.__le__', 'B.__ge__']) + self.assertTrue(issubclass(V, B)) + self.assertEqual(op_sequence(eq, B, V), ['B.__eq__', 'V.__eq__']) + self.assertEqual(op_sequence(le, B, V), ['B.__le__', 'V.__ge__']) + + def test_main(): support.run_unittest(RatTestCase, OperationOrderTests) + if __name__ == "__main__": test_main() @@ -10,6 +10,9 @@ Release date: tba Core and Builtins ----------------- +- Issue #4395: Better testing and documentation of binary operators. + Patch by Martin Panter. + - Issue #24467: Fixed possible buffer over-read in bytearray. The bytearray object now always allocates place for trailing null byte and it's buffer now is always null-terminated. |