LeetArxiv is Leetcode for implementing Arxiv and other research papers. We code lots of research papers and these tend to be in C and Python*. Due to popular demand, we wrote this comprehensive guide to interfacing between these languages.
*Foundational research papers (✨from before the 2000's✨) tend to be in C while modern AI research papers are in Python.
This practical coding guide demonstrates how to : Call Python from C and embed Python scripts in your C codebase.
We’ll start from the complete basics.
Say you have a Python file, let’s call it PythonFunctions.py . Now you want to use these functions inside a C file, let’s call this Cmain.c .
We assume you’re working on Linux or a Mac*. We also assume you have Python 3 and GCC available on your system.
*Using C on Windows is pretty cumbersome lol
You need to include Python.h inside your C to file to proceed.
To locate Python.h, open a terminal and type :
python3-config --includesIn my terminal, I get this path to Python.h :
Now, we need to call the Python header file after we located Python.h.
I’m using Python 3.8, so I modify Cmain.c to this :
Sanity check : we need to compile our Cmain.c to ensure everything is working.
To achieve this, we add the path to our Python installation (we located Python.h in Section 2.1) then link Python 3.8 in GCC.
The final GCC command resembles this :
gcc Cmain.c -I/usr/include/python3.8 -lpython3.8 -o Cmain.oIf everything is working then this small program should run :
From here onwards, we are following the official Python documentation. PyObjects are structures used in the definition of object types for Python(Common Object Structures 2025). This simply means : everything is a PyObject.
First, we use Python’s os.setenv function to create a path to our PythonFunctions.py file.
We use ./ since it’s in the same directory as our Cmain.c .
After setting the path, we need to initialize Python using Py_Initialize.
Py_Initialize initializes the Python Interpreter.
Then we end the Python session using Py_Finalize.
Py_Finalize undoes all the initializations and destroys all sub-interpreters.
The code below runs :
char *pythonFileLocation = "./"; //Set environment setenv("PYTHONPATH", pythonFileLocation, 1); //Initialize Python Py_Initialize(); //End Python Session Py_Finalize();*The curious may see lots of Valgrind errors. Don’t worry. We’ll handle these later.
Now, we load the file containing our actual Python code, PythonFunctions.py . We free this memory using the Py_XDECREF function.
*In the docs, there is a Py_DECREF but Py_XDECREF is better to use the latter because it can handle null values.
char *pythonFileName = "PythonFunctions"; PyObject *loadModule = PyImport_ImportModule(pythonFileName); if(loadModule == NULL) { /*Need to set path to overcome this error*/ printf("Error importing module\n"); PyErr_Print(); exit(1); } //Cleanup Load Module Py_XDECREF(loadModule);Your entire main function should resemble this :
First, we’ll write three Python functions inside PythonFunctions.py .
Remember, everything is a PyObject. Here are the steps :
Load the Function name into a PyObject using PyObject_GetAttrString.
Call the Function by its name using PyObject_CallObject.
Free the two newly-create PyObjects using separate Py_XDECREF.
Let’s work with the PrintList function. The function takes 1 argument, a python List type. Here are the steps to call it :
Create a new list object using PyList_New.
Create an integer object using PyLong_FromLong.
Load the integer inside the list using PyList_SetItem.
Create a tuple to hold function arguments using PyTuple_Pack.
Cleanup the list and function arguments.*
*I might be mistaken but a really interesting observation is : Calling Py_XDECREF on pythonList is sufficient to free the integer within the list.
All the code you need to call PrintList should resemble this :
Try calling PrintSum on your own :)
If you’re lost then feel free to compare your code with mine.
This is the link to the C File and Python File.
In this article, you learnt how to call Python code within a C codebase.