Extending & Embedding Python Using C - Pi
Written by Mike James   
Monday, 02 October 2023
Article Index
Extending & Embedding Python Using C - Pi
Using the module

Discover what goes into coding a Python extension module and see how fast C is when compared to Python by using it to compute Pi.

Extending & Embedding Python Using C

By Mike James

extendPython360

Buy from Amazon.

Contents

       Preface

  1. Extending And Embedding Python
  2. A First C Module Using Linux ***NEW!!!
  3. A First C Module Using Windows
  4. Module Basics
        Extract: A First Module
        Extract: 
    Pi 
  5. Arguments
  6. Returning Python Objects
  7. Objects And Attributes
  8. More Complex Objects – Tuples, Lists and Dicts
  9. Errors, Exceptions And Reference Counting
        Extract:
    Exceptions 
  10. Bytes And Strings
  11. Modules And Attributes
  12. New Types
  13. Advanced Types
  14. Threads And The GIL
  15. Embedding Python

<ASIN:B0CK3X93KF>

In chapter but not in this extract

  • The API and PyObject
  • The Initialization Function
  • Initializing the arith Module

Computing Pi – How Fast?

As a second simple example, we can use a very simple computation to demonstrate the potential speed advantages that a C extension module offers. You can compute pi using the very simple series:

pi = 4*(1-1/3+1/5-1/7 ... )

This is very easy to implement, we just need to generate the odd integers, but to get pi to a reasonable number of digits you have to compute a lot of terms. In other words, this series is very slow to converge. The simple-minded approach is to write something like:

import time
def myPi(m,n):
    pi=0
    for k in range(m,n+1):
        s= 1 if k%2 else -1 
        pi += s / (2 * k - 1)
    return 4*pi

This computes the sum from m to n.

A main program to make use of this function is:

if __name__ == '__main__':
    N=10000000
    t1=time.perf_counter()
    pi=myPi(1,N)
    t2=time.perf_counter()
    print((t2-t1)*1000)
    print(pi)

If you try this out you will find that it gives pi to about five digits, which is not good for so many terms, but it is a good example to convert to a C extension.

The details of initializing the module follow the usual steps and the function is a fairly obvious translation of the Python:

#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject * Pi(PyObject *self, PyObject *args)
{
    int m, n;
    double pi,s;
    if (!PyArg_ParseTuple(args, "ii", &m, &n))
        return NULL;
    pi=0;
    for(int k=m;k<n;k++){
        s=1;
        if(k%2==0)s=-1;
        pi=pi+s/(2*k-1);
    }    
    return PyFloat_FromDouble(4*pi);
}
static PyMethodDef AddMethods[] = {
    {"myPi", Pi, METH_VARARGS, "Compute Pi"},
    {NULL, NULL, 0, NULL} // sentinel
};
static struct PyModuleDef addmodule = {
  PyModuleDef_HEAD_INIT,
  "Pi",                              
  "C library to compute Pi",  
  -1,                                   
  AddMethods                          
};
PyMODINIT_FUNC PyInit_Pi(void) {    
     return PyModule_Create(&addmodule);
}

The only real changes are to the names used for the module and function. Task.json also needs to be updated. The args for Windows is:

"args": [
                "/Zi",
                "/EHsc",
                "/nologo",            
                "/IC:/Users/user/AppData/Local/Programs
                            /Python/Python311/include",               
                "${file}",
                "/link /dll /OUT:Pi.pyd        
                   /LIBPATH:C:/Users/user/AppData/Local/
                        Programs/Python/Python311/libs"

For Linux it is:

"args": [
"-fdiagnostics-color=always",
"-g",
"-shared",
"${file}",
"-o",
"Pi.so",
"-I/usr/local/include/python311"

With these changes the module should compile. If not go back to the instructions in chapters 2 and 3.

extendPython180



Last Updated ( Monday, 02 October 2023 )