]> granicus.if.org Git - python/commitdiff
Issue #4395: Better testing and documentation of binary operators.
authorRobert Collins <rbtcollins@hp.com>
Thu, 6 Aug 2015 22:22:54 +0000 (10:22 +1200)
committerRobert Collins <rbtcollins@hp.com>
Thu, 6 Aug 2015 22:22:54 +0000 (10:22 +1200)
Patch by Martin Panter.

Doc/reference/datamodel.rst
Lib/test/test_binop.py
Misc/NEWS

index dda18ba6ac31105427f5a70681079936c64e9aac..4acf13e97bcf8462d49197098c3138e87e69720f 100644 (file)
@@ -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)
 
index 9c4c18e63614ff159bc38e9a1f7664041fa2951e..963aa01b6ce535320b13499550b4dda5796061fb 100644 (file)
@@ -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()
index d5b5dc55c46aa6de65d75944e2207ab932db993f..795ace7452580175f6de1a8beaf3e66c637a4edf 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.