Thread overview | |||||
---|---|---|---|---|---|
|
June 03, 2006 Polymorphism and wrapping Python | ||||
---|---|---|---|---|
| ||||
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 |
June 03, 2006 Re: Polymorphism and wrapping Python | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kirk McDonald | 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/ |
June 04, 2006 Re: Polymorphism and wrapping Python | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kirk McDonald | 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. |
Copyright © 1999-2021 by the D Language Foundation