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 McDonaldIf 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 McDonaldI 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