[Zope-dev] ExtensionClass and __radd__()?
Greg Ward
gward@mems-exchange.org
Wed, 5 Jul 2000 18:30:46 -0400
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