[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