Making C and Python Talk to Each Other

May 29, 2025 - 20:45
 0  0
Making C and Python Talk to Each Other

1.0 Introduction

Summary of calling Python from C and C from Python

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.

Stop reading papers. Start coding them. Subscribe for weekly paper implementations, one semicolon at a time.

*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.

2.0 Calling Python inside C

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

2.1 Locating Python.h

You need to include Python.h inside your C to file to proceed.

To locate Python.h, open a terminal and type :

python3-config --includes 

In my terminal, I get this path to Python.h :

Python Installation Path

2.2 Including Python Header File in C

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 :

Adding Python.h to a C file

2.3 Compiling Our C file

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.o

If everything is working then this small program should run :

Sanity Check to Ensure Python Call Works

2.4 Working with PyObjects

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.

2.4.1 Setting Environment Variables, Initializing and Finalizing Python

  1. 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 .

  2. After setting the path, we need to initialize Python using Py_Initialize.

    • Py_Initialize initializes the Python Interpreter.

  3. 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.

2.4.2 Loading a Module and Freeing Module Memory

Now, we load the file containing our actual Python code, PythonFunctions.py . We free this memory using the Py_XDECREFfunction.

*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 :

Main file

2.5 Calling Python Functions

First, we’ll write three Python functions inside PythonFunctions.py .

Modified Python File

2.5.1 Calling Functions Without Arguments

Remember, everything is a PyObject. Here are the steps :

  1. Load the Function name into a PyObject using PyObject_GetAttrString.

  2. Call the Function by its name using PyObject_CallObject.

  3. Free the two newly-create PyObjects using separate Py_XDECREF.


	PyObject *currentDirectoryFunction = PyObject_GetAttrString(loadModule, (char *)"PrintCurrentDirectory");
	if(currentDirectoryFunction == NULL)
	{
		printf("Error loading currentDirectoryFunction\n");
		PyErr_Print();
		exit(1);
	}
	
	//Argument is Null because function takes zero arguments
	PyObject *directoryFunctionCall = PyObject_CallObject(currentDirectoryFunction, NULL);
	if(directoryFunctionCall == NULL)
	{
		printf("Error calling directoryFunctionCall\n");
		PyErr_Print();
		exit(1);
	}
	
	//Cleanup Call to Directory Function
	Py_XDECREF(directoryFunctionCall);
	//Cleanup Current Directory Function
	Py_XDECREF(currentDirectoryFunction);

2.5.2 Calling Functions With Arguments

Let’s work with the PrintList function. The function takes 1 argument, a python List type. Here are the steps to call it :

  1. Create a new list object using PyList_New.

  2. Create an integer object using PyLong_FromLong.

  3. Load the integer inside the list using PyList_SetItem.

  4. Create a tuple to hold function arguments using PyTuple_Pack.

  5. 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.
        int numberOfArguments = 1;
	int listLength = 1;
	//Create a new list
	PyObject *pythonList = PyList_New(listLength);
	if(pythonList == NULL)
	{
		printf("Error creating Python List\n");
		PyErr_Print();
		exit(1);
	}
	//Create a new integer
	PyObject *pythonInteger = PyLong_FromLong(55);
	if(pythonInteger == NULL)
	{
		printf("Error creating Python Int\n");
		PyErr_Print();
		exit(1);
	}
	//Set integer at list index 0
	PyList_SetItem(pythonList, 0, pythonInteger);
	PyObject *functionArguments = PyTuple_Pack(numberOfArguments, pythonList);
	


	
	//Cleanup functionArguments
	Py_XDECREF(functionArguments);
	//Cleanup pythonList
	Py_XDECREF(pythonList);
	

All the code you need to call PrintList should resemble this :

Calling a function with arguments

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.

3.0 Conclusion

In this article, you learnt how to call Python code within a C codebase.

Subscribe if you made it this far.

References

Python Software Foundation. (2025). Common Object Structures. Link

Python Software Foundation. (2025). Py_Initialize. Link

Python Software Foundation. (2025). Py_Finalize. Link

Python Software Foundation. (2025). Py_XDECREF. Link

What's Your Reaction?

Like Like 0
Dislike Dislike 0
Love Love 0
Funny Funny 0
Angry Angry 0
Sad Sad 0
Wow Wow 0