%load_ext Cython
This is a smattering of useful, but less widely known, features in
Cython has a huge number of directives documented at http://docs.cython.org/src/reference/compilation.html#compiler-directives These are like Python's __future__
directive in that they change the semantics of the language and control things like bounds checking and options like profiling and line tracing.
These can be set in the source file via #cython: directive=value
or locally via with cython.directive(value)
or in the setup.py file.
Cython has very good support for unicode, including optimized .encode()
and .decode()
methods. Unfortuantly, lots of C libraries take raw char *
. You can manually set the directives c_string_type
and c_string_encoding
to do conversions automatically.
%%cython
cimport cython # for directives
def default_behavior(x):
s = x.encode('UTF-8')
cdef char* c = s
# cdef char* c = x # if x is of type str, won't even compile
return c
default_behavior("abc")
default_behavior(u"不好")
%%cython
# cython: c_string_encoding=UTF-8
# cython: c_string_type=unicode
cimport cython # for directives
def good_behavior(x):
s = x.encode('UTF-8')
cdef char* c = s
return c
print good_behavior(u"好")
Enums and functions can be declared cpdef to export them to Python.
%%cython
cdef extern from "math.h":
cdef double cos(double)
cpdef double sin(double)
cpdef enum Status:
GOOD
BAD
UGLY
cos(0)
sin(0)
GOOD, BAD
%%cython
cdef extern from "math.h":
cdef double cos(double)
def give_me_cos():
return cos
give_me_cos()
give_me_cos()(0)
Cython understands many stl containers, has automatic iteration and conversion.
%%cython --cplus
#distutils: language=c++
from libcpp.vector cimport vector
def use_vector(vector[double] x):
for item in x:
print "contains", item
x[0] = 10
return x
use_vector([1, 2, 3])
Use the new
and del
keyword for C++ object creation and deletion. Use x[0]
or cython.operator.dereference
for dereferencing.
%%cython --cplus
#distutils: language=c++
from libcpp.vector cimport vector
from cython.operator cimport dereference as deref
cdef vector[int] *x = new vector[int]()
x.push_back(3)
print x[0]
print deref(x)
del x
Use func[T]
to declare template types.
%%cython --cplus
#distutils: language=c++
cdef extern from "<algorithm>" namespace "std":
cdef T max[T](T, T)
print max(2, 4)
print max[double](2, 4)
Leverage Cython memory management to manage C resources.
%%cython
from libc.stdlib cimport malloc, free
cdef class HoldMemory(object):
cdef void* data
def __cinit__(self, size):
self.data = malloc(size)
def __dealloc__(self):
free(self.data)
%%cython --cplus
#distutils: language=c++
from libcpp.vector cimport vector
cdef class PyVector(object):
cdef vector[int] c_vector
def __init__(self, data):
self.c_vector = data
def __str__(self):
return str(self.c_vector)
cdef class PyWrapper(object):
cdef vector[int]* c_vector # use this if there is not a no-arg constructor
def __cinit__(self):
self.c_vector = new vector[int]()
def __dealloc__(self):
del self.c_vector
print PyVector([1,2,3])
C structs can be defined using the cdef struct StructName:
. Anonomous tuples can be created via (type, type, ...)
.
%%cython
cdef struct Point:
double x
double y
double z
cdef Point p = Point(1, 2, z=3)
print p
p.x = 100
print p
cpdef (double, double) project(Point p):
return p.x / p.z, p.y / p.z
project({'x': 10, 'y': 1, 'z': 100})
Cython doesn't have templates (for that use an external templating engine), but does have a special construct called "fused types" that allows one to create several specializations for an enumeriated set of types.
%%cython
cdef fused MyType:
long
double
str
def add_to_self(MyType a):
return a+a
add_to_self(10)
add_to_self(1.5)
add_to_self("abc")
add_to_self['double'](2)
add_to_self([1,2,3])
The GIL, or global interpreter lock, must be held for all CPython interactions, however we can release it for pure C code to incrase concurrency. Without the GIL, you can only call code that also does not require the GIL and is explicitly marked as such.
%%cython
def release_gil(int x):
cdef int y
with nogil:
y = x*x
with gil:
print "Got", y
y = no_gil_required(y)
aquites_gil(y)
return y
cdef int no_gil_required(int a) nogil:
return 2 * a
cdef void aquites_gil(int a) with gil:
print "a", a
release_gil(100)
%%cython
import cython
@cython.infer_types(None)
def default_inference(int x, double y):
a = x
a += 1
print cython.typeof(a)
b = y
b += 1
print cython.typeof(b)
@cython.infer_types(False)
def no_inference(int x, double y):
a = x
a += 1
print cython.typeof(a)
b = y
b += 1
print cython.typeof(b)
@cython.infer_types(True)
def unsafe_inference(int x, double y):
a = x
a += 1
print cython.typeof(a)
b = y
b += 1
print cython.typeof(b)
default_inference(1, 1.0)
no_inference(1, 1.0)
unsafe_inference(1, 1.0)
%%cython
cdef extern from "math.h":
cpdef double foo "sin" (double)
cdef double foo2 "sin(3)"
ctypedef int my_int "int"
ctypedef double blah
cdef blah my_var = 4.5
print foo(3.14)
print foo2
foo(100)