I want to add my own structure to the structure created by Python's C extension module!

Thing you want to do

Well, as the title says, this time I would like to talk about "I want to put my own structure in the structure created by Python's C extension module". It's crazy, and it's very difficult to understand in terms of words, so I'll write an explanation code.

I want something that can be used like this!

testcord.py



import myModule

test = myModule.Custom1()
test.a = 200
logging.debug(test.a)
test.t2.a = 400
logging.debug(test.t2.a)

As a requirement, I want to make a C extension module of python, and in it, I want to make an integer type "a" and an integer type "a" in some structure "t2". .. Looking at the variable definition inside in C ++, it looks like the following.

test.cpp




namespace test2 {
	class CustomObject{
		int a;

	}
}


namespace test1 {
	class CustomObject{
		int a;
		test2::CustomObject t2;
	}
}

In the test1:: CustomObject structure, a of type int and t2 of the test1:: CustomObject structure exist. (* As a bonus, there is an int type a in testobject2)

Prerequisite knowledge before making.

The explanation on the above site describes how to make a C extension module in an easy-to-understand manner.

This time, the contents up to "2.2. Add data and methods to the basic sample" will solve the problem.

Actually made program

main.cpp corresponds to the function to be registered as an extension module in python and the contents of the test1:: CustomObject structure written in "I want something that can be used like this!".

main.cpp


#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"

#include <iostream>
#include <string>

#include "test1.hpp"

namespace test1 {
	typedef struct {
		PyObject_HEAD
		int a = 10;
		PyObject *t2;
	} CustomObject;

	static PyObject *
	Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
	{
		CustomObject *self;
		self = (CustomObject *) type->tp_alloc(type, 0);
		if (self != NULL) {
			self->a = 0;

			auto obj = (PyObject *) PyObject_Malloc(sizeof(test2::CustomObject));
			if (obj == NULL) {
				return PyErr_NoMemory();
			}
			PyObject_Init(obj, &test2::CustomType);

			self->t2 = obj;
		}
		return (PyObject *) self;
	}

	static int
	Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
	{
		static char *kwlist[] = {"a", "t2", NULL};
		//PyObject *t2 = NULL;
		if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist,
										&self->a,&self->t2))
			return -1;
		return 0;
	}

	static void
	Custom_dealloc(CustomObject *self)
	{
		Py_XDECREF(self->t2);
		Py_TYPE(self)->tp_free((PyObject *) self);
	}

	static PyObject* c_helloworld(CustomObject *self)
	{
		self->a = 300;

		return Py_None;
	}

	// myModule definition(python's name)
	static PyMethodDef myMethods[] = {
		{ "helloworld", (PyCFunction) c_helloworld, METH_NOARGS, "Prints Hello World" },
		{ NULL }
	};

	static PyMemberDef Custom_members[] = {
		{"a", T_INT, offsetof(CustomObject, a), 0,
		"custom a"},
		{"t2", T_OBJECT_EX, offsetof(CustomObject, t2), 0,
		"first t2"},
		{NULL}  /* Sentinel */
	};


	static PyTypeObject CustomType = {
		PyVarObject_HEAD_INIT(NULL, 0)
		.tp_name = "custom.Custom1",
		.tp_basicsize = sizeof(CustomObject),
		.tp_itemsize = 0,
		.tp_dealloc = (destructor) Custom_dealloc,
		.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
		.tp_doc = "Custom1 objects",
		.tp_methods = myMethods,
		.tp_members = Custom_members,
		.tp_init = (initproc) Custom_init,
		.tp_new = Custom_new,
	};
}

// myModule definition struct
static struct PyModuleDef myModule = {
	PyModuleDef_HEAD_INIT,
	"myModule",
	"Python3 C API Module(Sample 1)",
	-1,
};

// Initializes myModule
PyMODINIT_FUNC PyInit_myModule(void)
{
	PyObject *m;
	if (PyType_Ready(&test1::CustomType) < 0 || PyType_Ready(&test2::CustomType) < 0)
		return NULL;

	m = PyModule_Create(&myModule);
	if (m == NULL)
		return NULL;

	Py_INCREF(&test1::CustomType);
	Py_INCREF(&test2::CustomType);
	PyModule_AddObject(m, "Custom1", (PyObject *) &test1::CustomType);
	PyModule_AddObject(m, "Custom2", (PyObject *) &test2::CustomType);

	return m;
}

test1.hpp corresponds to the contents of the test2 :: CustomObject structure written in "I want something that can be used like this!".

test1.hpp


#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"

#include <iostream>
#include <string>

namespace test2{
	struct CustomObject {
		PyObject_HEAD
		int a = 10;

		CustomObject(){
			this->a = 100;
		}
		~CustomObject(){}
	} ;
	
	static PyObject *
	Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
	{
		CustomObject *self;
		self = (CustomObject *) type->tp_alloc(type, 0);
		if (self != NULL) {
			self->a = 0;
			//self->t2 = 
		}
		return (PyObject *) self;
	}

	static int
	Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
	{
		static char *kwlist[] = {"a", NULL};

		if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
										&self->a))
			return -1;

		return 0;
	}

	static void
	Custom_dealloc(CustomObject *self)
	{
		Py_TYPE(self)->tp_free((PyObject *) self);
	}

	static PyObject* c_helloworld(CustomObject *self)
	{
		self->a = 300;

		return Py_None;
	}

	// myModule definition(python's name)
	static PyMethodDef myMethods[] = {
		{ "helloworld", (PyCFunction) c_helloworld, METH_NOARGS, "Prints Hello World" },
		{ NULL }
	};

	static PyMemberDef Custom_members[] = {
		{"a", T_INT, offsetof(CustomObject, a), 0,
		"custom a"},
		{NULL} 
	};


	static PyTypeObject CustomType = {
		PyVarObject_HEAD_INIT(NULL, 0)
		.tp_name = "custom.Custom2",
		.tp_basicsize = sizeof(CustomObject),
		.tp_itemsize = 0,
		.tp_dealloc = (destructor) Custom_dealloc,
		.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
		.tp_doc = "Custom2 objects",
		.tp_methods = myMethods,
		.tp_members = Custom_members,
		.tp_init = (initproc) Custom_init,
		.tp_new = Custom_new,
	};

}

Description

test1 :: CustomObject structure

main.cpp



typedef struct {
	PyObject_HEAD
	int a = 10;
	PyObject *t2;
} CustomObject;

Add the PyObject_HEAD macro to the normally created structure. And, t2 is originally planned to be a test2 :: CustomObject structure, but in order to handle it as a C extension module of python, it needs to be PyObject, so it is described as PyObject.

test1 :: Allocate memory for CustomObject


static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	CustomObject *self;
	self = (CustomObject *) type->tp_alloc(type, 0);
	if (self != NULL) {
		self->a = 0;

		auto obj = (PyObject *) PyObject_Malloc(sizeof(test2::CustomObject));
		if (obj == NULL) {
			return PyErr_NoMemory();
		}
		PyObject_Init(obj, &test2::CustomType);

		self->t2 = obj;
	}
	return (PyObject *) self;
}

test1: It is necessary to allocate memory for CustomObject. The int type can be just initialized, but the original class (test2 :: CustomObjec t2) does not.

Allocate only for the original class with PyObject_Malloc. In PyObject_Init, let us know to use your own class as part of the C extension module. Finally,

self->t2 = obj;

Then, memory allocation is completed as PyObject *.

Other code statements

Others can be written according to the contents of "2. Definition of extension type: Tutorial --Python 3.6.5 documentation". test2 :: CustomObjec can be written in the same way as a normal C extension module.

Summary!

It was a difficult time because I couldn't understand all this. I couldn't understand it even if I looked at various sites, and after all, when I read the source code sentence of "cpython / Objects / unicodeobject.c" of "python / cpython github", it was solved immediately. The sentence "PyUnicode_FromString (" ");" in the commentary says that it only allocates memory ... It took a long time to know this much ...

** Conclusion: If you don't understand from the official reference, go to the official source code **

Oware.

Digression: Why is this in the first place?

I'm using python with mackerel, but it took more than 3 seconds to process, which was a dangerous bottleneck, so I decided to make a C extension module to solve it. The people who make numpy are amazing. I can't sleep with my legs turned. I say python god, but I think it's better to say python god after knowing the person who wrote C behind the scenes with the intention of dying.

Recommended Posts

I want to add my own structure to the structure created by Python's C extension module!
[Ansible] I want to call my own function from the template module (macro)
I want to save the photos sent by LINE to S3
I want to terminate python's multiprocessing Pool with ctrl + c (KeyboardInterrupt)
To add a C module to MicroPython ...
I want to check the position of my face with OpenCV!
I want to change the color by clicking the scatter point in matplotlib
[C language] I want to generate random numbers in the specified range
I tried how to improve the accuracy of my own Neural Network
I want to use the Django Debug Toolbar in my Ajax application
I want to express my feelings with the lyrics of Mr. Children
I want to pin Spyder to the taskbar
I want to output to the console coolly
I want to handle the rhyme part1
I want to handle the rhyme part3
I want to display the progress bar
What I always add to my ~ / .bashrc
I want to handle the rhyme part2
I want to handle the rhyme part5
I want to handle the rhyme part4
I tried to publish my own module so that I can pip install it
I want to see a list of WebDAV files in the Requests module
I made a module in C language to filter images loaded by Python
I want to have recursion come to my mind
I want to handle the rhyme part7 (BOW)
[I tried using Pythonista3] Importing my own module
I want to make fits from my head
I want to manage systemd by time zone! !!
I tried using the Datetime module by Python
I want to sell Mercari by scraping python
I want to make C ++ code from Python code!
I want to customize the appearance of zabbix
I want to use the activation function Mish
I want to display the progress in Python!
I just wanted to understand Python's Pickle module
[First data science ⑤] I tried to help my friend find the first property by data analysis.
How to avoid the cut-off label of the graph created by the plot module using matplotlib
I want to get started with the Linux kernel, what is the list head structure?
I want to see the file name from DataLoader
I want to grep the execution result of strace
I want to inherit to the back with python dataclass
I want to fully understand the basics of Bokeh
I felt that I ported the Python code to C ++ 98.
I want to handle the rhyme part6 (organize once)
I want to automate ssh using the expect command!
I want to publish the product at the lowest cost
I want to use the R dataset in python
I want to handle the rhyme part8 (finished once)
I want to increase the security of ssh connections
[Selenium] I want to display the browser by hitting the driver on the host OS from WSL