]> granicus.if.org Git - python/commitdiff
It's a fact: for binary operators, *under certain circumstances*,
authorGuido van Rossum <guido@python.org>
Fri, 28 Sep 2001 23:49:48 +0000 (23:49 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 28 Sep 2001 23:49:48 +0000 (23:49 +0000)
__rop__ now takes precendence over __op__.  Those circumstances are:

  - Both arguments are new-style classes
  - Both arguments are new-style numbers
  - Their implementation slots for tp_op differ
  - Their types differ
  - The right argument's type is a subtype of the left argument's type

Also did this for the ternary operator (pow) -- only the binary case
is dealt with properly though, since __rpow__ is not supported anyway.

Lib/test/test_descr.py
Objects/abstract.c

index 2c5e7a4008c2f8fac60efd86972813a3421c432e..22ae09a983da8316b716c5e9daa69c6096e1a9c9 100644 (file)
@@ -1,8 +1,12 @@
-# Test descriptor-related enhancements
+# Test enhancements related to descriptors and new-style classes
 
 from test_support import verify, verbose, TestFailed, TESTFN
 from copy import deepcopy
 
+def vereq(a, b):
+    if a != b:
+        raise TestFailed, "%r != %r" % (a, b)
+
 def testunop(a, res, expr="len(a)", meth="__len__"):
     if verbose: print "checking", expr
     dict = {'a': a}
@@ -2133,6 +2137,36 @@ def copies():
     a.bar.append(4)
     verify(d.bar == [1,2,3])
 
+def binopoverride():
+    if verbose: print "Testing overrides of binary operations..."
+    class I(int):
+        def __repr__(self):
+            return "I(%r)" % int(self)
+        def __add__(self, other):
+            return I(int(self) + int(other))
+        __radd__ = __add__
+        def __pow__(self, other, mod=None):
+            if mod is None:
+                return I(pow(int(self), int(other)))
+            else:
+                return I(pow(int(self), int(other), int(mod)))
+        def __rpow__(self, other, mod=None):
+            if mod is None:
+                return I(pow(int(other), int(self), mod))
+            else:
+                return I(pow(int(other), int(self), int(mod)))
+            
+    vereq(`I(1) + I(2)`, "I(3)")
+    vereq(`I(1) + 2`, "I(3)")
+    vereq(`1 + I(2)`, "I(3)")
+    vereq(`I(2) ** I(3)`, "I(8)")
+    vereq(`2 ** I(3)`, "I(8)")
+    vereq(`I(2) ** 3`, "I(8)")
+    vereq(`pow(I(2), I(3), I(5))`, "I(3)")
+    class S(str):
+        def __eq__(self, other):
+            return self.lower() == other.lower()
+
 
 def test_main():
     lists()
@@ -2178,6 +2212,7 @@ def test_main():
     setclass()
     pickles()
     copies()
+    binopoverride()
     if verbose: print "All OK"
 
 if __name__ == "__main__":
index fdf1a4483fc9ca17631d7c4ecc3f66682d737ec6..c353159e985f6fd32758ede46d6095de2d0bd5e9 100644 (file)
@@ -300,11 +300,14 @@ PyNumber_Check(PyObject *o)
 
   v    w       Action
   -------------------------------------------------------------------
-  new  new     v.op(v,w), w.op(v,w)
+  new  new     w.op(v,w)[*], v.op(v,w), w.op(v,w)
   new  old     v.op(v,w), coerce(v,w), v.op(v,w)
   old  new     w.op(v,w), coerce(v,w), v.op(v,w)
   old  old     coerce(v,w), v.op(v,w)
 
+  [*] only when v->ob_type != w->ob_type && w->ob_type is a subclass of
+      v->ob_type
+
   Legend:
   -------
   * new == new style number
@@ -318,29 +321,35 @@ static PyObject *
 binary_op1(PyObject *v, PyObject *w, const int op_slot)
 {
        PyObject *x;
-       binaryfunc *slot;
-       if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v)) {
-               slot = NB_BINOP(v->ob_type->tp_as_number, op_slot);
-               if (*slot) {
-                       x = (*slot)(v, w);
-                       if (x != Py_NotImplemented) {
-                               return x;
-                       }
-                       Py_DECREF(x); /* can't do it */
-               }
-               if (v->ob_type == w->ob_type) {
-                       goto binop_error;
-               }
+       binaryfunc slotv = NULL;
+       binaryfunc slotw = NULL;
+
+       if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v))
+               slotv = *NB_BINOP(v->ob_type->tp_as_number, op_slot);
+       if (w->ob_type != v->ob_type &&
+           w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
+               slotw = *NB_BINOP(w->ob_type->tp_as_number, op_slot);
+               if (slotw == slotv)
+                       slotw = NULL;
+       }
+       if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) {
+               x = slotw(v, w);
+               if (x != Py_NotImplemented)
+                       return x;
+               Py_DECREF(x); /* can't do it */
+               slotw = NULL;
        }
-       if (w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) {
-               slot = NB_BINOP(w->ob_type->tp_as_number, op_slot);
-               if (*slot) {
-                       x = (*slot)(v, w);
-                       if (x != Py_NotImplemented) {
-                               return x;
-                       }
-                       Py_DECREF(x); /* can't do it */
-               }
+       if (slotv) {
+               x = slotv(v, w);
+               if (x != Py_NotImplemented)
+                       return x;
+               Py_DECREF(x); /* can't do it */
+       }
+       if (slotw) {
+               x = slotw(v, w);
+               if (x != Py_NotImplemented)
+                       return x;
+               Py_DECREF(x); /* can't do it */
        }
        if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w)) {
                int err = PyNumber_CoerceEx(&v, &w);
@@ -350,9 +359,10 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
                if (err == 0) {
                        PyNumberMethods *mv = v->ob_type->tp_as_number;
                        if (mv) {
-                               slot = NB_BINOP(mv, op_slot);
-                               if (*slot) {
-                                       PyObject *x = (*slot)(v, w);
+                               binaryfunc slot;
+                               slot = *NB_BINOP(mv, op_slot);
+                               if (slot) {
+                                       PyObject *x = slot(v, w);
                                        Py_DECREF(v);
                                        Py_DECREF(w);
                                        return x;
@@ -363,7 +373,6 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
                        Py_DECREF(w);
                }
        }
-binop_error:
        Py_INCREF(Py_NotImplemented);
        return Py_NotImplemented;
 }
@@ -420,6 +429,18 @@ ternary_op(PyObject *v,
        register ternaryfunc *slot;
        
        mv = v->ob_type->tp_as_number;
+       mw = w->ob_type->tp_as_number;
+       if (v->ob_type != w->ob_type && mw && NEW_STYLE_NUMBER(w)) {
+               slot = NB_TERNOP(mw, op_slot);
+               if (*slot && *slot != *NB_TERNOP(mv, op_slot) &&
+                   PyType_IsSubtype(w->ob_type, v->ob_type)) {
+                       x = (*slot)(v, w, z);
+                       if (x != Py_NotImplemented)
+                               return x;
+                       /* Can't do it... fall through */
+                       Py_DECREF(x);
+               }
+       }
        if (mv != NULL && NEW_STYLE_NUMBER(v)) {
                /* try v.op(v,w,z) */
                slot = NB_TERNOP(mv, op_slot);
@@ -435,7 +456,6 @@ ternary_op(PyObject *v,
                        goto ternary_error;
                }
        }
-       mw = w->ob_type->tp_as_number;
        if (mw != NULL && NEW_STYLE_NUMBER(w)) {
                /* try w.op(v,w,z) */
                slot = NB_TERNOP(mw,op_slot);