Recently, a friend and I met with various Python C ++ wrappers, trying to find one that meets the needs of both professional and hobby. We both honed PyCxx as a good balance between a lightweight and lightweight interface by hiding some of the ugliest bits of the Python C api. However, PyCxx is not very reliable when it comes to type exposing (i.e.: it instructs you to create type factories, rather than creating constructors), and we are working on filling in the gaps to expose our types in a more functional way. To fill in these gaps move on to C api.
This left us with some questions, however, that the api documentation does not seem to cover a lot of depth (and when this happens, the answers are sometimes contradictory). The main comprehensive question is: what should be defined for a Python type to use as a base type? We found that for the PyCxx class, which will function as a type, we must explicitly define tp_new and tp_dealloc and set the type as a module attribute, and that we need to set Py_TPFLAGS_BASETYPE to [our type] → tp_flags, but then we are still feeling for the dark .
Here is our code:
class kitty : public Py::PythonExtension<kitty> {
public:
kitty() : Py::PythonExtension<kitty>() {}
virtual ~kitty() {}
static void init_type() {
behaviors().name("kitty");
add_varargs_method("speak", &kitty::speak);
}
static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) {
return static_cast<PyObject*>(new kitty());
}
static void tp_dealloc(PyObject *obj) {
kitty* k = static_cast<kitty*>(obj);
delete k;
}
private:
Py::Object speak(const Py::Tuple &args) {
cout << "Meow!" << endl;
return Py::None();
}
};
class cat_module : public Py::ExtensionModule<cat_module> {
public:
cat_module() : Py::ExtensionModule<cat_module>("cat") {
kitty::init_type();
PyTypeObject* kittyType = kitty::type_object();
kittyType->tp_new = &kitty::tp_new;
kittyType->tp_dealloc = &kitty::tp_dealloc;
kittyType->tp_flags |= Py_TPFLAGS_BASETYPE;
module().setAttr("kitty", Py::Object((PyObject*)kittyType));
initialize();
}
virtual ~cat_module() {}
};
extern "C" void initcat() {
static cat_module* cat = new cat_module();
}
And our Python test code looks like this:
import cat
class meanKitty(cat.kitty):
def scratch(self):
print "hiss! *scratch*"
myKitty = cat.kitty()
myKitty.speak()
meanKitty = meanKitty()
meanKitty.speak()
meanKitty.scratch()
The curious bit is that if you comment out all the meanKitty bits, the script runs, and the cat meows just fine, but if you uncomment the meanKitty class all of a sudden, Python gives us this:
AttributeError: 'kitty' object has no attribute 'speak'
. ! - , , ! !
EDIT: , , . -
virtual Py::Object getattr( const char *name ) {
return getattr_methods( name );
}
Python! , :
Traceback (most recent call last):
File "d:\Development\Junk Projects\PythonCxx\Toji.py", line 12, in <module>
meanKitty.scratch()
AttributeError: scratch
- ! !