digitalmars.D - Polymorphism and wrapping Python
- Kirk McDonald (52/52) Jun 03 2006 I'm currently writing a family of classes that wrap the object layers of...
 - Daniel Keep (19/86) Jun 03 2006 If I remember correctly, there are a bunch of fields that may or may not
 - Hasan Aljudy (9/76) Jun 03 2006 I think one possibility is to use a d-to-d translator that inspects code...
 
I'm currently writing a family of classes that wrap the object layers of 
the Python/C API. The class hierarchy looks something like this:
DPyObject
   DPyNumber
     DPyInteger
     DPyFloat
     (etc)
   DPySequence
     DPyString
     DPyList
     (etc)
   DPyMapping
     DPyDict
   (etc)
All of the classes on the second level are declared as "abstract," and 
(appropriately) represent the Python/C API's abstract objects layer. 
DPyObject and the classes on the third level are not abstract and 
represent the concrete objects layer. (Though I think I may eventually 
make DPyObject abstract, too.)
Behind this is a factory function that takes a PyObject* (the Python/C 
API's usual way of representing a Python object) and returns a instance 
of the proper subclass of DPyObject.
I have a question, then, of polymorphism, and how closely I should try 
to emulate Python.
In Python, if you try to (for example) add two instances of a class that 
doesn't have the __add__ method (Python's equivalent of opAdd) defined, 
Python will raise a TypeError. If you try to do this in D, it is a 
compile-time error.
At the moment, DPyObject doesn't define opAdd. DPyNumber does, like this:
DPyNumber opAdd(DPyNumber rhs) {
     return make_dpyobject(PyNumber_Add(m_ptr, rhs.m_ptr));
}
(If you're unfamiliar with the Python/C API, PyNumber_Add takes two 
PyObject* arguments and returns a new PyObject* with the result. m_ptr 
is the only member of DPyObject, and is a PyObject*. make_dpyobject is 
my factory function that returns the proper subclass of DPyObject based 
on the type of the PyObject* passed to it.)
If we have:
DPyObject o = new DPyInteger(5);
DPyObject p = new DPyInteger(10);
DPyObject q = o + p; // Error! DPyObject doesn't define opAdd.
I'm trying to decide if this is a problem. I can easily define this for 
DPyObject:
DPyObject opAdd(DPyObject rhs) { assert(false); }
Then the above example would work, but I'm not totally sure that this is 
desirable behavior. In Python it certainly would be: that language is 
all about the duck typing. It would certainly simplify the API, in that 
all DPyObject-derived classes would have the same interface (now there's 
an idea, I should write an interface...). But I still have this 
irrational fear that it is a bad idea.
Anyone have more insight than I can currently muster?
-Kirk McDonald
 Jun 03 2006
Kirk McDonald wrote:
 I'm currently writing a family of classes that wrap the object layers of
 the Python/C API. The class hierarchy looks something like this:
 
 DPyObject
   DPyNumber
     DPyInteger
     DPyFloat
     (etc)
   DPySequence
     DPyString
     DPyList
     (etc)
   DPyMapping
     DPyDict
   (etc)
 
 All of the classes on the second level are declared as "abstract," and
 (appropriately) represent the Python/C API's abstract objects layer.
 DPyObject and the classes on the third level are not abstract and
 represent the concrete objects layer. (Though I think I may eventually
 make DPyObject abstract, too.)
 
 Behind this is a factory function that takes a PyObject* (the Python/C
 API's usual way of representing a Python object) and returns a instance
 of the proper subclass of DPyObject.
 
 I have a question, then, of polymorphism, and how closely I should try
 to emulate Python.
 
 In Python, if you try to (for example) add two instances of a class that
 doesn't have the __add__ method (Python's equivalent of opAdd) defined,
 Python will raise a TypeError. If you try to do this in D, it is a
 compile-time error.
 
 At the moment, DPyObject doesn't define opAdd. DPyNumber does, like this:
 
 DPyNumber opAdd(DPyNumber rhs) {
     return make_dpyobject(PyNumber_Add(m_ptr, rhs.m_ptr));
 }
 
 (If you're unfamiliar with the Python/C API, PyNumber_Add takes two
 PyObject* arguments and returns a new PyObject* with the result. m_ptr
 is the only member of DPyObject, and is a PyObject*. make_dpyobject is
 my factory function that returns the proper subclass of DPyObject based
 on the type of the PyObject* passed to it.)
 
 If we have:
 
 DPyObject o = new DPyInteger(5);
 DPyObject p = new DPyInteger(10);
 DPyObject q = o + p; // Error! DPyObject doesn't define opAdd.
 
 I'm trying to decide if this is a problem. I can easily define this for
 DPyObject:
 
 DPyObject opAdd(DPyObject rhs) { assert(false); }
 
 Then the above example would work, but I'm not totally sure that this is
 desirable behavior. In Python it certainly would be: that language is
 all about the duck typing. It would certainly simplify the API, in that
 all DPyObject-derived classes would have the same interface (now there's
 an idea, I should write an interface...). But I still have this
 irrational fear that it is a bad idea.
 
 Anyone have more insight than I can currently muster?
 
 -Kirk McDonald
If I remember correctly, there are a bunch of fields that may or may not
actually be implemented for a particular PyObject.  Things like __add__,
__str__, __repr__, etc.  I personally think that all of these should be
on DPyObject, just with a default "blarg, I dies!" implementation.
That way, the D binding operates in approximately the same fashion as
Python itself.
The only thing I think would be different is that if, say, __add__
fails, then Python tries __radd__.  If I understand rightly, D does the
same thing... except at compile time.  Perhaps DPyObject should define a
general-purpose opXXX that first tries the operand's __xxx__ method,
then it's __rxxx__ method.
In any case, good to know you've gotten so far :)
	-- Daniel
-- 
Unlike Knuth, I have neither proven or tried the above; it may not even
make sense.
v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D
i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
 Jun 03 2006
Kirk McDonald wrote:
 I'm currently writing a family of classes that wrap the object layers of 
 the Python/C API. The class hierarchy looks something like this:
 
 DPyObject
   DPyNumber
     DPyInteger
     DPyFloat
     (etc)
   DPySequence
     DPyString
     DPyList
     (etc)
   DPyMapping
     DPyDict
   (etc)
 
 All of the classes on the second level are declared as "abstract," and 
 (appropriately) represent the Python/C API's abstract objects layer. 
 DPyObject and the classes on the third level are not abstract and 
 represent the concrete objects layer. (Though I think I may eventually 
 make DPyObject abstract, too.)
 
 Behind this is a factory function that takes a PyObject* (the Python/C 
 API's usual way of representing a Python object) and returns a instance 
 of the proper subclass of DPyObject.
 
 I have a question, then, of polymorphism, and how closely I should try 
 to emulate Python.
 
 In Python, if you try to (for example) add two instances of a class that 
 doesn't have the __add__ method (Python's equivalent of opAdd) defined, 
 Python will raise a TypeError. If you try to do this in D, it is a 
 compile-time error.
 
 At the moment, DPyObject doesn't define opAdd. DPyNumber does, like this:
 
 DPyNumber opAdd(DPyNumber rhs) {
     return make_dpyobject(PyNumber_Add(m_ptr, rhs.m_ptr));
 }
 
 (If you're unfamiliar with the Python/C API, PyNumber_Add takes two 
 PyObject* arguments and returns a new PyObject* with the result. m_ptr 
 is the only member of DPyObject, and is a PyObject*. make_dpyobject is 
 my factory function that returns the proper subclass of DPyObject based 
 on the type of the PyObject* passed to it.)
 
 If we have:
 
 DPyObject o = new DPyInteger(5);
 DPyObject p = new DPyInteger(10);
 DPyObject q = o + p; // Error! DPyObject doesn't define opAdd.
 
 I'm trying to decide if this is a problem. I can easily define this for 
 DPyObject:
 
 DPyObject opAdd(DPyObject rhs) { assert(false); }
 
 Then the above example would work, but I'm not totally sure that this is 
 desirable behavior. In Python it certainly would be: that language is 
 all about the duck typing. It would certainly simplify the API, in that 
 all DPyObject-derived classes would have the same interface (now there's 
 an idea, I should write an interface...). But I still have this 
 irrational fear that it is a bad idea.
 
 Anyone have more insight than I can currently muster?
 
 -Kirk McDonald
I think one possibility is to use a d-to-d translator that inspects code 
and adds dummy functions to the DPyObject class as needed .. just like
DPyObject function_that_may_not_exist___who_knows( DPyObject[] params ... )
{
     assert(false);
}
Then this translator would pass the "modified" code to the actual dmd 
compiler.
 Jun 03 2006








 
 
 
 Daniel Keep <daniel.keep.lists gmail.com> 