ExtensionClass and __radd__()?
Hi all -- looks like ExtensionClass doesn't recognize/implement the '__radd__()' protocol. Speculation below; first, a demonstration. Normal case: a regular class Number that knows how to add itself to other number-like things: class Number: def __init__ (self, n): self.n = n def __str__ (self): return str(self.n) def __add__ (self, other): return self.n + other __radd__ = __add__ n = Number(37) print n, n + 5, 5 + n The output of this is exactly what you'd expect: 37 42 42 (I have convinced myself that '__radd__()' is called in the "5 + n" case by writing a separate '__radd__()' with a print statement. Everything is sane!) Now the same thing as an ExtensionClass: from ExtensionClass import Base class ECNumber (Base): def __init__ (self, n): self.n = n def __str__ (self): return str(self.n) def __add__ (self, other): return self.n + other __radd__ = __add__ ecn = ECNumber(37) print ecn, ecn + 5, 5 + ecn The output of this is puzzling, to say the least: 37 42 134883149 IOW, "5 + ecn" evaluates to 134883149 -- that's a plain int, I checked with 'type()' on it. If I put this code: print id(ecn), id(5), id(ECNumber), id(Base), id(ExtensionClass) immediately after the last print, I get 135530536 135220848 135568720 1075195712 1075195488 ... which just tells me that the result of "5 + ecn" looks a lot like an id(), but isn't the id() of anything obvious. (Or 5 + any obvious id().) Happens the same with the ExtensionClass code from Zope 2.1.5 or 2.2beta1, and with Python 1.5.2 and the latest CVS Python. Speculation time: I'm guessing that this is similar to the problem with 'isinstance()' and ExtensionClass that I found several months ago, which was heroically debugged by Barry. To recap, it's a mutual finger-pointing bug: Python (Guido) can claim that it's up to ExtensionClass (Jim) to emulate the full semantics of Python classes/instances, but ExtensionClass can claim that Python should be more relaxed in what it accepts as a "class object" or "instance object". I think the relevant code in Python is in Objects/abstract.c, specifically 'PyNumber_Add()' and the BINOP macro: #define BINOP(v, w, opname, ropname, thisfunc) \ if (PyInstance_Check(v) || PyInstance_Check(w)) \ return PyInstance_DoBinOp(v, w, opname, ropname, thisfunc) [...] PyNumber_Add(v, w) PyObject *v, *w; { PySequenceMethods *m; BINOP(v, w, "__add__", "__radd__", PyNumber_Add); [...] My guess is that PyInstance_Check() returns false for ExtensionClass instances. Two possible fixes: loosen up PyInstance_Check(), or loosen up BINOP. Well, it's a nice theory. It doesn't explain why '__add__()' works for ExtensionClass while '__radd__()' does not; perhaps ExtensionClass implements that much of Python's class semantics, but doesn't go as far as '__radd__()'. Other opinions? Greg
On Wed, 5 Jul 2000, Greg Ward wrote:
Well, it's a nice theory. It doesn't explain why '__add__()' works for ExtensionClass while '__radd__()' does not; perhaps ExtensionClass implements that much of Python's class semantics, but doesn't go as far as '__radd__()'.
A quick note which you probably already know: grep add ExtensionClass.c gives: static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__, INIT_PY_NAME(__add__); BINOP(add,Add) FILLENTRY(nm->nb, add, add, METH_VARARGS, "Add to another"); FILLENTRY(sm->sq, concat, add, METH_VARARGS, SET_SPECIAL(add,add); subclass_add(PyObject *self, PyObject *v) UNLESS(m=subclass_getspecial(self,py__add__)) return NULL; && AsCMethod(m)->meth==(PyCFunction)add_by_name ASSIGN(m,AsCMethod(m)->type->tp_as_number->nb_add(self,v)); (binaryfunc)subclass_add, /*nb_add*/ (binaryfunc)subclass_add, /*sq_concat*/ return; /* we added a reference; don't delete now */ whereas grep radd ExtensionClass.c returns nothing Pavlos
On 06 July 2000, Pavlos Christoforou said:
A quick note which you probably already know:
grep add ExtensionClass.c gives: [...lots...] whereas grep radd ExtensionClass.c returns nothing
Yep, did the same myself shortly after posting. It's not really clear what 'py__add__' is and how it works, though, so it's not obvious if 'py__radd__' is the right thing to add, and if so how to add it. Greg If it's just a matter of adding radd (and rsub, rmul, and rdiv) in all those places -- Greg Ward - software developer gward@mems-exchange.org MEMS Exchange / CNRI voice: +1-703-262-5376 Reston, Virginia, USA fax: +1-703-262-5367
Greg Ward wrote:
Hi all --
looks like ExtensionClass doesn't recognize/implement the '__radd__()' protocol. Speculation below; first, a demonstration. Normal case: a regular class Number that knows how to add itself to other number-like things:
(demonstration snipped)
Speculation time: I'm guessing that this is similar to the problem with 'isinstance()' and ExtensionClass that I found several months ago, which was heroically debugged by Barry. To recap, it's a mutual finger-pointing bug: Python (Guido) can claim that it's up to ExtensionClass (Jim) to emulate the full semantics of Python classes/instances, but ExtensionClass can claim that Python should be more relaxed in what it accepts as a "class object" or "instance object".
I think the relevant code in Python is in Objects/abstract.c, specifically 'PyNumber_Add()' and the BINOP macro:
#define BINOP(v, w, opname, ropname, thisfunc) \ if (PyInstance_Check(v) || PyInstance_Check(w)) \ return PyInstance_DoBinOp(v, w, opname, ropname, thisfunc)
[...] PyNumber_Add(v, w) PyObject *v, *w; { PySequenceMethods *m;
BINOP(v, w, "__add__", "__radd__", PyNumber_Add); [...]
My guess is that PyInstance_Check() returns false for ExtensionClass instances. Two possible fixes: loosen up PyInstance_Check(), or loosen up BINOP.
Well, it's a nice theory. It doesn't explain why '__add__()' works for ExtensionClass while '__radd__()' does not; perhaps ExtensionClass implements that much of Python's class semantics, but doesn't go as far as '__radd__()'.
I'd love to see __radd__ added. ;) I don't remember why it's not there. Maybe I was just lazy. It may be fairly hard to add. I haven't looked in quite a while. As anyone whos looked at ExtensionClass sources may be able to tell, ExtensionClass has to play quite a few tricks to: - Try to sanely bridge the quite different semantics of Python "types" and "classes" (e.g. there's no radd for "types"). - Try to overcome the fact that the interpreter special-cases InstanceType and ClassType pretty liberally. To (try to and mostly succeed to) provide instance semantics I have to do alot of weird indirection. This is especially hard for numeric things. Your analysis of the code demonstrates this issue. ExtensionClass instances are not of type InstanceType. In fact, each ExtensionClass is a separate type and instances of different ExtensionClasses have different types. Note that I just got around to responding to your earlier post "Comparison inconsistency with ExtensionClass". This has a similar root cause: the special-case treatment of InstanceType. I think that the *real* solution to this problem is to get rid of ExtensionClass. ;) To do this, I need to get the features I have in ExtensionClass into Python. I guess there's some hope for that in Python 3K (love that name :). In the mean time, I don't have time to fix the radd problem myself, but would be willing to advise someone who wanted to try to take it on, especially if we could work out some phone or face-to-face sessions. Jim -- Jim Fulton mailto:jim@digicool.com Python Powered! Technical Director (888) 344-4332 http://www.python.org Digital Creations http://www.digicool.com http://www.zope.org Under US Code Title 47, Sec.227(b)(1)(C), Sec.227(a)(2)(B) This email address may not be added to any commercial mail list with out my permission. Violation of my privacy with advertising or SPAM will result in a suit for a MINIMUM of $500 damages/incident, $1500 for repeats.
participants (3)
-
Greg Ward -
Jim Fulton -
Pavlos Christoforou