Commit f4704655 authored by Guido van Rossum's avatar Guido van Rossum

It's a fact: for binary operators, *under certain circumstances*,

__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.
parent 7d6aea1d
# Test descriptor-related enhancements # Test enhancements related to descriptors and new-style classes
from test_support import verify, verbose, TestFailed, TESTFN from test_support import verify, verbose, TestFailed, TESTFN
from copy import deepcopy 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__"): def testunop(a, res, expr="len(a)", meth="__len__"):
if verbose: print "checking", expr if verbose: print "checking", expr
dict = {'a': a} dict = {'a': a}
...@@ -2133,6 +2137,36 @@ def copies(): ...@@ -2133,6 +2137,36 @@ def copies():
a.bar.append(4) a.bar.append(4)
verify(d.bar == [1,2,3]) 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(): def test_main():
lists() lists()
...@@ -2178,6 +2212,7 @@ def test_main(): ...@@ -2178,6 +2212,7 @@ def test_main():
setclass() setclass()
pickles() pickles()
copies() copies()
binopoverride()
if verbose: print "All OK" if verbose: print "All OK"
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -300,11 +300,14 @@ PyNumber_Check(PyObject *o) ...@@ -300,11 +300,14 @@ PyNumber_Check(PyObject *o)
v w Action 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) 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 new w.op(v,w), coerce(v,w), v.op(v,w)
old old 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: Legend:
------- -------
* new == new style number * new == new style number
...@@ -318,29 +321,35 @@ static PyObject * ...@@ -318,29 +321,35 @@ static PyObject *
binary_op1(PyObject *v, PyObject *w, const int op_slot) binary_op1(PyObject *v, PyObject *w, const int op_slot)
{ {
PyObject *x; PyObject *x;
binaryfunc *slot; binaryfunc slotv = NULL;
if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v)) { binaryfunc slotw = NULL;
slot = NB_BINOP(v->ob_type->tp_as_number, op_slot);
if (*slot) { if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v))
x = (*slot)(v, w); slotv = *NB_BINOP(v->ob_type->tp_as_number, op_slot);
if (x != Py_NotImplemented) { 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; return x;
}
Py_DECREF(x); /* can't do it */ Py_DECREF(x); /* can't do it */
slotw = NULL;
} }
if (v->ob_type == w->ob_type) { if (slotv) {
goto binop_error; x = slotv(v, w);
} if (x != Py_NotImplemented)
}
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; return x;
}
Py_DECREF(x); /* can't do it */ 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)) { if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w)) {
int err = PyNumber_CoerceEx(&v, &w); int err = PyNumber_CoerceEx(&v, &w);
...@@ -350,9 +359,10 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot) ...@@ -350,9 +359,10 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
if (err == 0) { if (err == 0) {
PyNumberMethods *mv = v->ob_type->tp_as_number; PyNumberMethods *mv = v->ob_type->tp_as_number;
if (mv) { if (mv) {
slot = NB_BINOP(mv, op_slot); binaryfunc slot;
if (*slot) { slot = *NB_BINOP(mv, op_slot);
PyObject *x = (*slot)(v, w); if (slot) {
PyObject *x = slot(v, w);
Py_DECREF(v); Py_DECREF(v);
Py_DECREF(w); Py_DECREF(w);
return x; return x;
...@@ -363,7 +373,6 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot) ...@@ -363,7 +373,6 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot)
Py_DECREF(w); Py_DECREF(w);
} }
} }
binop_error:
Py_INCREF(Py_NotImplemented); Py_INCREF(Py_NotImplemented);
return Py_NotImplemented; return Py_NotImplemented;
} }
...@@ -420,6 +429,18 @@ ternary_op(PyObject *v, ...@@ -420,6 +429,18 @@ ternary_op(PyObject *v,
register ternaryfunc *slot; register ternaryfunc *slot;
mv = v->ob_type->tp_as_number; 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)) { if (mv != NULL && NEW_STYLE_NUMBER(v)) {
/* try v.op(v,w,z) */ /* try v.op(v,w,z) */
slot = NB_TERNOP(mv, op_slot); slot = NB_TERNOP(mv, op_slot);
...@@ -435,7 +456,6 @@ ternary_op(PyObject *v, ...@@ -435,7 +456,6 @@ ternary_op(PyObject *v,
goto ternary_error; goto ternary_error;
} }
} }
mw = w->ob_type->tp_as_number;
if (mw != NULL && NEW_STYLE_NUMBER(w)) { if (mw != NULL && NEW_STYLE_NUMBER(w)) {
/* try w.op(v,w,z) */ /* try w.op(v,w,z) */
slot = NB_TERNOP(mw,op_slot); slot = NB_TERNOP(mw,op_slot);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment