extending python, what is the best option for me?
DESCRIPTION
by Francisco Fernández Castaño - Python is a great language, but there are occasions where we need access to low level operations or connect with some database driver written in C. With the FFI(Foreign function interface) we can connect Python with other languages like C, C++ and even the new Rust. There are some alternatives to achieve this goal, Native Extensions, Ctypes and CFFI. I'll compare this three ways of extending Python.TRANSCRIPT
![Page 1: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/1.jpg)
Extending Python
Francisco Fernandez Castano
Rushmore.fm
[email protected] @fcofdezc
November 29, 2014
http://kcy.me/1dykg
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 1 / 52
![Page 2: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/2.jpg)
Overview
1 Motivations
2 Guidelines
3 Native extensions
4 CTypes
5 CFFI
6 Conclusions
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 2 / 52
![Page 3: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/3.jpg)
Caution
Huge topic
Toy examples
Unix CPython centric
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 3 / 52
![Page 4: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/4.jpg)
Motivation
Why write in C?
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 4 / 52
![Page 5: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/5.jpg)
Motivation
Speed
Using legacy code
Integration
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 5 / 52
![Page 6: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/6.jpg)
MotivationWhy is Python slow?
Interpretation overhead
Boxed arithmetic and automatic overflow handling
Dynamic dispatch of operations
Dynamic lookup of methods and attributes
Everything can change on runtime
Extreme introspective and reflective capabilities
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 6 / 52
![Page 7: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/7.jpg)
Motivation
Speed
Using legacy code
Integration
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 7 / 52
![Page 8: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/8.jpg)
Motivation
Speed
Using legacy code
Integration
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 8 / 52
![Page 9: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/9.jpg)
Guidelines
Static libraries
Shared libraries
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 9 / 52
![Page 10: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/10.jpg)
Static libraries
$ ar tv libdemo.a
rw -r--r-- 501/20 40 Jul 19 22:34 2014 __.SYMDEF
rw -r--r-- 501/20 2352 Jul 19 22:33 2014 a.o
rw -r--r-- 501/20 2352 Jul 19 22:33 2014 b.o
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 10 / 52
![Page 11: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/11.jpg)
Shared libraries
Single copy in memory
Runtime load
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 11 / 52
![Page 12: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/12.jpg)
Native extensions
C API
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 12 / 52
![Page 13: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/13.jpg)
Native extensions
Hello world, Newton method
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 13 / 52
![Page 14: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/14.jpg)
Native extensions
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 14 / 52
![Page 15: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/15.jpg)
Native extensions
#include "Python.h"
static PyObject *
newton(PyObject *self , PyObject *args)
{
float guess;
float x;
if (! PyArg_ParseTuple(args , "ff", &guess , &x))
return NULL;
while (fabs(powf(guess , 2) - x) > 0.01)
{
guess = ((x / guess) + guess) / 2;
}
return Py_BuildValue("f", guess );
}
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 15 / 52
![Page 16: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/16.jpg)
Native extensions
static PyMethodDef
module_functions [] = {
{"newton", newton , METH_VARARGS , "Newton method."},
{NULL}
};
PyMODINIT_FUNC
initnewton(void)
{
Py_InitModule3("newton", module_functions , "Newton");
}
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 16 / 52
![Page 17: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/17.jpg)
Native extensions
from distutils.core import setup , Extension
setup(name=’codemotion ’,
version =1.0,
ext_modules =[
Extension(’newton ’, [’newton.c’])])
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 17 / 52
![Page 18: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/18.jpg)
Native extensions
Python 2.7.5 (default , Nov 3 2014, 14:26:24)
[GCC 4.8.3 20140911 (Red Hat 4.8.3 -7)] on linux2
>>> import newton
>>> newton.newton(1, 2)
1.4166667461395264
>>>
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 18 / 52
![Page 19: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/19.jpg)
How?
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 19 / 52
![Page 20: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/20.jpg)
Native extensions
NAME
dlopen --load and link a dynamic library or bundle
SYNOPSIS
#include <dlfcn.h>
void*
dlopen(const char* path , int mode);
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 20 / 52
![Page 21: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/21.jpg)
Native extensions
$ nm -g newton.so
00000000002010 a0 B __bss_start
w __cxa_finalize@@GLIBC_2 .2.5
00000000002010 a0 D _edata
00000000002010 a8 B _end
0000000000000944 T _fini
w __gmon_start__
00000000000006 d0 T _init
0000000000000920 T initnewton
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U PyArg_ParseTuple
U Py_BuildValue
U Py_InitModule4_64
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 21 / 52
![Page 22: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/22.jpg)
Native extensions
cpython/Python/dynload shlib.c
handle = dlopen(pathname , dlopenflags );
if (handle == NULL) {
const char *error = dlerror ();
if (error == NULL)
error = "unknown dlopen () error";
PyErr_SetString(PyExc_ImportError , error );
return NULL;
}
if (fp != NULL && nhandles < 128)
handles[nhandles ++]. handle = handle;
p = (dl_funcptr) dlsym(handle , funcname );
return p;
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 22 / 52
![Page 23: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/23.jpg)
Native extensionsMemory management
Manual memory management
Python GC - RC
Cycle detector
Py INCREF(x) Py DECREF(x)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 23 / 52
![Page 24: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/24.jpg)
Native extensionsError management
Return NULL as a convention
Register exceptions
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 24 / 52
![Page 25: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/25.jpg)
Native extensionsError management
if (err < 0) {
PyErr_SetString(PyExc_Exception , "Err");
return NULL;
}
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 25 / 52
![Page 26: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/26.jpg)
Native extensionsPython 3 differences
static struct PyModuleDef examplemodule = {
PyModuleDef_HEAD_INIT ,
"newton",
"newton module doc string",
-1,
module_functions ,
NULL ,
NULL ,
NULL ,
NULL};
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 26 / 52
![Page 27: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/27.jpg)
Native extensionsPython 3 differences
PyMODINIT_FUNC
PyInit_sum(void)
{
PyModule_Create (& examplemodule );
}
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 27 / 52
![Page 28: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/28.jpg)
CTypes
Advanced FFI for Python
Allows call functions from Shared libs
Create, access, manipulate C data types
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 28 / 52
![Page 29: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/29.jpg)
CTypesTypes correspondence
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 29 / 52
![Page 30: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/30.jpg)
CTypesStructs
from ctypes import *
class POINT(Structure ):
_fields_ = [("x", c_int), ("y", c_int )]
class RECT(Structure ):
_fields_ = [("upperleft", POINT),
("lowerright", POINT )]
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 30 / 52
![Page 31: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/31.jpg)
CTypesExample
Implemented fibonacci as c function
Map as Python code
Measure differences between Python and C
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 31 / 52
![Page 32: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/32.jpg)
CTypes
int fib(int n){
if (n < 2)
return n;
else
return fib(n - 1) + fib(n - 2);
}
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 32 / 52
![Page 33: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/33.jpg)
CTypes
import ctypes
lib_fib = ctypes.CDLL("libfib.so")
def ctypes_fib(n):
return lib_fib.fib(ctypes.c_int(n))
def py_fib(n):
if n < 2:
return n
else:
return py_fib(n-1) + py_fib(n-2)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 33 / 52
![Page 34: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/34.jpg)
CTypes
In [3]: %timeit fib.ctypes_fib (20)
10000 loops , best of 3: 63.8 micro s per loop
In [4]: %timeit fib.py_fib (20)
100 loops , best of 3: 3.62 ms per loop
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 34 / 52
![Page 35: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/35.jpg)
CTypesFortran example
Use of existing fortran code
Take random code at github
https://github.com/astrofrog/fortranlib
Wrap using ctypes
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 35 / 52
![Page 36: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/36.jpg)
CTypesFortran example
real(dp) function mean_dp(x, mask)
implicit none
real(dp),intent(in) :: x(:)
logical ,intent(in),optional :: mask (:)
if(present(mask)) then
mean_dp = sum(x, mask=mask)/size(x)
else
mean_dp = sum(x)/size(x)
end if
end function mean_dp
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 36 / 52
![Page 37: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/37.jpg)
CTypesFortran example
gfortran -fPIC -shared statistic.f90 -o lib_statistics.so
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 37 / 52
![Page 38: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/38.jpg)
CTypesFortran example
bin git:( master) -> nm -g lib_statistics.so
001eab T ___lib_statistics_MOD_clipped_mean_dp
000afc T ___lib_statistics_MOD_clipped_mean_sp
00306c T ___lib_statistics_MOD_mean_dp
001c55 T ___lib_statistics_MOD_mean_sp
002db0 T ___lib_statistics_MOD_median_dp
0019b0 T ___lib_statistics_MOD_median_sp
002544 T ___lib_statistics_MOD_quantile_dp
00115a T ___lib_statistics_MOD_quantile_sp
002299 T ___lib_statistics_MOD_variance_dp
000ec3 T ___lib_statistics_MOD_variance_sp
U __gfortran_arandom_r4
U __gfortran_arandom_r8
U __gfortran_os_error
U __gfortran_pack
U __gfortran_pow_i4_i4
U __gfortran_runtime_error
U __gfortran_runtime_error_at
U __gfortran_st_write
U __gfortran_st_write_done
U __gfortran_stop_string
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 38 / 52
![Page 39: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/39.jpg)
CTypesFortran example
from ctypes import *
# Statistics fortran lib
st_lib = CDLL(’lib_statistics.so’)
mean = st_lib.__lib_statistics_MOD_mean_dp
mean.argtypes = [POINTER(c_float *2)]
mean.restype = c_float
vals = (c_float *2)(2.0 , 3.0)
print mean(vals)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 39 / 52
![Page 40: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/40.jpg)
CTypesCTypes source
cpython/Modules/ ctypes/callproc.c
static PyObject *py_dl_open(PyObject *self , PyObject *args)
{
char *name;
void * handle;
#ifdef RTLD_LOCAL
int mode = RTLD_NOW | RTLD_LOCAL;
#else
/* cygwin doesn ’t define RTLD_LOCAL */
int mode = RTLD_NOW;
#endif
if (! PyArg_ParseTuple(args , "z|i:dlopen", &name , &mode))
return NULL;
mode |= RTLD_NOW;
handle = ctypes_dlopen(name , mode);
.
return PyLong_FromVoidPtr(handle );
}Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 40 / 52
![Page 41: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/41.jpg)
CFFI
Advanced FFI for Python
Allows call functions from Shared libs
Create, access, manipulate C data types
Both API and ABI access
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 41 / 52
![Page 42: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/42.jpg)
CFFI
Mostly the same as CTypes
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 42 / 52
![Page 43: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/43.jpg)
CFFI
Recommended way to extend PyPy
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 43 / 52
![Page 44: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/44.jpg)
CFFIABI
from cffi import FFI
ffi = FFI()
ffi.cdef(""" int printf(const char *format , ...); """)
C = ffi.dlopen(None)
arg = ffi.new("char[]", "world")
C.printf("hi there , %s!\n", arg)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 44 / 52
![Page 45: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/45.jpg)
CFFIABI- Fibonacci
from cffi import FFI
ffi = FFI()
ffi.cdef(""" int fib(int n);""")
libfib = ffi.dlopen(’libfib.so’)
libfib.fib (10)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 45 / 52
![Page 46: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/46.jpg)
CFFIAPI Level
import cffi
ffi = cffi.FFI()
ffi.cdef(""" int fib(int n);""")
lib = ffi.verify(r’’’
int fib(int n){
if ( n < 2 )
return n;
else
return fib(n-1) + fib(n-2);
}’’’)
print lib.fib (10)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 46 / 52
![Page 47: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/47.jpg)
CFFIAPI Level
import cffi
ffi = cffi.FFI()
ffi.cdef(""" int fib(int n);""")
lib = ffi.verify(r’’’
int fib(int n){
if ( n < 2 )
return n;
else
return fib(n-1) + fib(n-2);
}’’’)
print lib.fib(’asd’)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 47 / 52
![Page 48: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/48.jpg)
CFFIAPI Level
Traceback (most recent call last):
File "fib.py", line 16, in <module >
print lib.fib("asd")
TypeError: an integer is required
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 48 / 52
![Page 49: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/49.jpg)
CFFIAPI Level
from cffi import FFI
ffi = FFI()
ffi.cdef(""" typedef struct { float x, y; } point;""")
point = ffi.new("point *")
point.x = 2.0
point.y = 3.0
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 49 / 52
![Page 50: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/50.jpg)
CFFIInternals
cffi/c/ cffi backend.c
static PyObject *
b_load_library(PyObject *self , PyObject *args)
{
void *handle;
DynLibObject *dlobj;
if ((flags & (RTLD_NOW | RTLD_LAZY )) == 0)
flags |= RTLD_NOW;
printable_filename = filename_or_null ? filename_or_null : "<None >";
handle = dlopen(filename_or_null , flags );
dlobj = PyObject_New(DynLibObject , &dl_type );
dlobj ->dl_handle = handle;
dlobj ->dl_name = strdup(printable_filename );
return (PyObject *)dlobj;
}Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 50 / 52
![Page 51: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/51.jpg)
Conclusions
Three different ways
Same principles
Less portable - More portable
Harder - Easier
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 51 / 52
![Page 52: Extending Python, what is the best option for me?](https://reader034.vdocuments.mx/reader034/viewer/2022052601/5598cc921a28ab621a8b467f/html5/thumbnails/52.jpg)
Francisco Fernandez Castano (@fcofdezc) Extending Python November 29, 2014 52 / 52