Skip to content

Commit 5758bd2

Browse files
more work on Extending lecture...
1 parent 1eb4f50 commit 5758bd2

File tree

2 files changed

+313
-2
lines changed

2 files changed

+313
-2
lines changed

week-08/extensions/code/ctypes/test_add.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,25 @@
4040
## prototype for pow():
4141
## double pow ( double x, double y )
4242

43-
print "This should be 81:",
43+
print "This should be 81.0:",
4444
print libm.pow(ctypes.c_double(3), ctypes.c_double(4))
4545

4646
## need to set the return type!
4747
libm.pow.restype = ctypes.c_double
48-
print "This should be 81:",
48+
print "This should be 81.0:",
4949
print libm.pow(ctypes.c_double(3), ctypes.c_double(4))
5050

51+
## if you are going to call the same function a lot,
52+
## you can specify the arument types:
53+
54+
libm.pow.restype = ctypes.c_double
55+
libm.pow.argtypes = [ctypes.c_double, ctypes.c_double]
56+
print "This should be 81.0:",
57+
print libm.pow(3, 4.0)
58+
59+
## much easier!
60+
61+
5162

5263

5364

week-08/extensions/source/extensions.rst

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,11 @@ Building your extension
207207
description='simple c extension for an example',
208208
ext_modules=[Extension('add', sources=['add.c'])],
209209
)
210+
210211
Run the setup.py::
211212

212213
python setup.py build_ext --inplace
214+
213215
(you can also just do ``install`` or ``develop`` if you want to properly installed)
214216

215217
Run the tests
@@ -319,3 +321,301 @@ Example::
319321
Further reading:
320322

321323
http://docs.python.org/2/library/ctypes.html
324+
325+
326+
Calling functions with ctypes
327+
==============================
328+
329+
The shared lib must be loaded::
330+
331+
add = ctypes.cdll.LoadLibrary("add.so")
332+
333+
An already loaded lib can be loaded with:
334+
335+
libc = ctypes.CDLL("/usr/lib/libc.dylib")
336+
337+
ctypes comes with a utility to help find libs::
338+
339+
ctypes.util.find_library(name)
340+
341+
(good for system libs)
342+
343+
.. nextslide::
344+
345+
Once loaded, a ctypes wrapper around a c function can be called directly::
346+
347+
print add.add(3,4)
348+
349+
But....
350+
351+
352+
C is statically typed -- once compiled, the function must be called with the correct types.
353+
354+
ctypes Data Types
355+
=================
356+
357+
ctypes will auto-translate these native types:
358+
359+
- ``None``
360+
- int
361+
- byte strings (``bytes()``, ``str()``)
362+
- ``unicode`` (careful! unicode is ugly in C!)
363+
364+
These can be directly used as parameters when calling C functions.
365+
366+
.. nextslide::
367+
368+
Most types must be wrapped in a ctypes data type::
369+
370+
printf("An int %d, a double %f\n", 1234, c_double(3.14))
371+
372+
There are ctypes wrappers for all the "standard" C types
373+
374+
http://docs.python.org/2/library/ctypes.html#fundamental-data-types
375+
376+
377+
You can also do pointers to types::
378+
379+
a_lib.a_function( ctypes.byref(c_float(x)))
380+
381+
http://docs.python.org/2/library/ctypes.html#passing-pointers-or-passing-parameters-by-reference
382+
383+
.. nextslide:: C structs
384+
385+
You can define C structs::
386+
387+
>>> class POINT(ctypes.Structure):
388+
... _fields_ = [("x", ctypes.c_int),
389+
... ("y", ctypes.c_int)]
390+
...
391+
>>> point = POINT(10, 20)
392+
>>> print point.x, point.y
393+
10 20
394+
>>> point = POINT(y=5)
395+
>>> print point.x, point.y
396+
0 5
397+
398+
.. nextslide:: Custom Python Classes
399+
400+
You can define how to pass data from your custom classes to ctypes:
401+
402+
Define an ``_as_parameter_`` attribute (or property)::
403+
404+
class MyObject(object):
405+
def __init__(self, number):
406+
self._as_parameter_ = number
407+
408+
obj = MyObject(32)
409+
printf("object value: %d\n", obj)
410+
411+
http://docs.python.org/2/library/ctypes.html#fundamental-data-types
412+
413+
(careful with types here!)
414+
415+
.. nextslide:: Return Types
416+
417+
To defining the return type, define the ``restype`` attribute.
418+
419+
Pre-defining the entire function signature::
420+
421+
libm.pow.restype = ctypes.c_double
422+
libm.pow.argtypes = [ctypes.c_double, ctypes.c_double]
423+
424+
And you can just call it like a regular python function -- ctypes will type check/convert at run time::
425+
426+
In [10]: libm.pow('a string', 4)
427+
---------------------------------------------------------------------------
428+
ArgumentError Traceback (most recent call last)
429+
<ipython-input-10-01be690a307b> in <module>()
430+
----> 1 libm.pow('a string', 4)
431+
432+
ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type
433+
434+
Some more features
435+
===================
436+
437+
Defining callbacks into Python code from C::
438+
439+
ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
440+
441+
http://docs.python.org/2/library/ctypes.html#ctypes.CFUNCTYPE
442+
443+
Numpy provides utilities for numpy arrays:
444+
445+
http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.ctypes.html
446+
447+
(works well for C code that takes "classic" C arrays)
448+
449+
450+
Summary:
451+
========
452+
453+
* ``ctypes`` allows you to call shared libraries:
454+
455+
- Your own custom libs
456+
- System libs
457+
- Proprietary libs
458+
459+
* Supports almost all of C:
460+
461+
- Custom data types
462+
463+
- structs
464+
- unions
465+
- pointers
466+
467+
- callbacks
468+
469+
.. nextslide::
470+
471+
* Upside:
472+
473+
- You can call system libs with little code
474+
- You don't need to compile anything
475+
476+
- at least for system and pre-compiled libs
477+
478+
* Downsides:
479+
480+
- You need to specify the interface
481+
482+
- and it is NOT checked for you!
483+
484+
- Translation is done on the fly at run time
485+
486+
- performance considerations
487+
488+
LAB
489+
====
490+
491+
In ``code/ctypes`` you'll find ``add.c``
492+
493+
You can build a shared lib with it with ``make``
494+
(``make.bat``) on Windows.
495+
496+
``test_ctypes.py`` will call that dll, and a few system dlls.
497+
498+
* Take a look at what's there, and how it works.
499+
* add another function to add.c, that takes different types (maybe divide?)
500+
* rebuild, and figure out how to call it with ctypes.
501+
502+
* Try calling other system functions with ctypes.
503+
504+
505+
*******
506+
Cython
507+
*******
508+
509+
A Python like language with static types which compiles down to C code for Python extensions.
510+
511+
Cython
512+
=======
513+
514+
* Can write pure python
515+
- Fully understands the python types
516+
517+
* With careful typing -- you get pure C (and pure C speed)
518+
519+
* Can also call other C code: libraries or compiled in.
520+
521+
* Used for custom Python extensions and/or call C and C++ code.
522+
523+
.. nextslide::
524+
525+
Further reading:
526+
527+
Web site:
528+
529+
http://www.cython.org/
530+
531+
Documentation:
532+
533+
http://docs.cython.org/
534+
535+
Wiki:
536+
537+
https://github.com/cython/cython/wiki
538+
539+
540+
541+
Developing with Cython
542+
========================
543+
544+
First, install cython with::
545+
546+
``pip install cython``
547+
548+
Cython files end in the .pyx extension. An example add.pyx::
549+
550+
def add(int x, int y):
551+
cdef int result=0
552+
result = x + y
553+
return result
554+
555+
556+
Basic Cython
557+
=============
558+
559+
Cython functions can be declared three ways::
560+
561+
def foo # callable from Python
562+
563+
cdef foo # only callable from Cython/C
564+
565+
cpdef foo # callable from both Cython and Python
566+
567+
Once your .pyx file is created, it is converted to C via
568+
569+
570+
cython cy_add.pyx
571+
Generate "annoted" C code in HTML
572+
573+
574+
cython -a cy_add.pyx
575+
To build your Python extension:
576+
577+
578+
python cy_setup.py build_ext --inplace # note Cython defines its' own build_ext in Cython.Distutils.build_ext
579+
580+
Cython can compile pure Python code to C to provide a performance improvement
581+
582+
583+
584+
::
585+
586+
587+
588+
589+
Consider a more expensive function
590+
591+
592+
def f(x):
593+
return x**2-x
594+
595+
def integrate_f(a, b, N):
596+
s = 0
597+
dx = (b-a)/N
598+
for i in range(N):
599+
s += f(a+i*dx)
600+
return s * dx
601+
602+
603+
Impovements with static typing
604+
605+
Convert the dynamically typed variables to static types and measure performance improvement before and after
606+
Can static types and dynamic types be mixed?
607+
Check the performance in converting the function type to static (see here)
608+
Use cython -a to compare the generated C code in all cases
609+
610+
def f(x):
611+
return x**2-x
612+
613+
def integrate_f(a, b, N):
614+
s = 0
615+
dx = (b-a)/N
616+
for i in range(N):
617+
s += f(a+i*dx)
618+
return s * dx
619+
620+
621+

0 commit comments

Comments
 (0)