- Are considered by Python as “built-in” types.
- Can be used to wrap arbitrary C-data structures, and provide a Python-like interface to them from Python.
- Attributes and methods can be called from Python or Cython code
- Are defined by the cdef class statement.
cdef class Shrubbery:
cdef int width, height
def __init__(self, w, h):
self.width = w
self.height = h
def describe(self):
print "This shrubbery is", self.width, \
"by", self.height, "cubits."
- You can’t add attributes to an extension type instance at run time like in normal Python.
- You can sub-class the extenstion type in Python to add attributes at run-time.
- By Python look-up.
- Python code’s only method of access.
- By direct access to the C struct from Cython code.
- Cython code can use either method of access, though.
- Only accessible by direct access.
- Not accessible from Python code.
To make attributes accessible to Python, they must be declared public or readonly:
cdef class Shrubbery:
cdef public int width, height
cdef readonly float depth
- The width and height attributes are readable and writable from Python code.
- The depth attribute is readable but not writable.
Note
Note
You can only expose simple C types, such as ints, floats, and strings, for Python access. You can also expose Python-valued attributes.
Note
The public and readonly options apply only to Python access, not direct access. All the attributes of an extension type are always readable and writable by C-level access.
Cython provides a special syntax:
cdef class Spam:
property cheese:
"A doc string can go here."
def __get__(self):
# This is called when the property is read.
...
def __set__(self, value):
# This is called when the property is written.
...
def __del__(self):
# This is called when the property is deleted.
The __get__(), __set__(), and __del__() methods are all optional.
- If they are ommitted, An exception is raised when an access attempt is made.
- Add to a list each time it is written to ("__set__").
- Return the list when it is read ("__get__").
- Empty the list when it is deleted ("__del__").
# cheesy.pyx
cdef class CheeseShop:
cdef object cheeses
def __cinit__(self):
self.cheeses = []
property cheese:
def __get__(self):
return "We don't have: %s" % self.cheeses
def __set__(self, value):
self.cheeses.append(value)
def __del__(self):
del self.cheeses[:]
# Test input
from cheesy import CheeseShop
shop = CheeseShop()
print shop.cheese
shop.cheese = "camembert"
print shop.cheese
shop.cheese = "cheddar"
print shop.cheese
del shop.cheese
print shop.cheese
# Test output
We don't have: []
We don't have: ['camembert']
We don't have: ['camembert', 'cheddar']
We don't have: []
Note
- This a Python library limitation because the PyTypeObject data structure is limited
- This includes any allocation of C data structures.
- Caution is warranted as to what you do in this method.
- The object may not be fully valid Python object when it is called.
- Calling Python objects, including the extensions own methods, may be hazardous.
- By the time __cinit__() is called...
- Memory has been allocated for the object.
- All C-level attributes have been initialized to 0 or null.
- Python have been initialized to None, but you can not rely on that for each occasion.
- This initialization method is guaranteed to be called exactly once.
- For Extensions types that inherit a base type:
- The __cinit__() method of the base type is automatically called before this one.
- The inherited __cinit__() method can not be called explicitly.
- Passing modified argument lists to the base type must be done through __init__().
- It may be wise to give the __cinit__() method both "*" and "**" arguments.
- Allows the method to accept or ignore additional arguments.
- Eliminates the need for a Python level sub-class, that changes the __init__() method’s signature, to have to override both the __new__() and __init__() methods.
- If __cinit__() is declared to take no arguments except self, it will ignore any extra arguments passed to the constructor without complaining about a signature mis-match
- By the time this method is called, the extension type is a fully valid Python object.
- All operations are safe.
- This method may sometimes be called more than once, or possibly not at all.
- Take this into consideration to make sure the design of your other methods are robust of this fact.
- The Python object to which this method belongs may not be completely intact at this point.
- Avoid invoking any Python operations that may touch the object.
- Don’t call any of this object’s methods.
- It’s best to just deallocate C-data structures here.
Note
Most of these methods behave differently than in Python
- Also apply to ‘in-place’ method __ipow__().
- Do not apply to other ‘in-place’ methods like __iadd__(), in that these always take self as the first argument.
Note
There are no separate methods for individual rich comparison operations.
A single special method called __richcmp__() replaces all the individual rich compare, special method types.
__richcmp__() takes an integer argument, indicating which operation is to be performed as shown in the table below.
<
0
==
2
>
4
<=
1
!=
3
>=
5
An extension type may inherit from a built-in type or another extension type:
cdef class Parrot:
...
cdef class Norwegian(Parrot):
...
A complete definition of the base type must be available to Cython
- If the base type is a built-in type, it must have been previously declared as an extern extension type.
- cimport can be used to import the base type, if the extern declared base type is in a .pxd definition file.
- In Cython, multiple inheritance is not permitted.. singlular inheritance only
- Here multiple inhertance is permissible as is normal for Python.
- Even multiple extension types may be inherited, but C-layout of all the base classes must be compatible.
Extension types can be “forward-declared”.
This is necessary when two extension types refer to each other:
cdef class Shrubbery # forward declaration
cdef class Shrubber:
cdef Shrubbery work_in_progress
cdef class Shrubbery:
cdef Shrubber creator
An extension type that has a base-class, requires that both forward-declarations be specified:
cdef class A(B)
...
cdef class A(B):
# attributes and methods
Note
- This is because full dynamic type checking is applied
- Cython does not check this for reasons of efficency.
Be very aware of exposing Python functions that take extension types as arguments:
def widen_shrubbery(Shrubbery sh, extra_width): # This is
sh.width = sh.width + extra_width
* Users could **crash** the program by passing ``None`` for the ``sh`` parameter.
* This could be avoided by::
def widen_shrubbery(Shrubbery sh, extra_width):
if sh is None:
raise TypeError
sh.width = sh.width + extra_width
* Cython provides a more convenient way with a ``not None`` clause::
def widen_shrubbery(Shrubbery sh not None, extra_width):
sh.width = sh.width + extra_width
* Now this function automatically checks that ``sh`` is not ``None``, as well as that is the right type.
not None can only be used in Python functions (declared with def not cdef).
For cdef functions, you will have to provide the check yourself.
The self parameter of an extension type is guaranteed to never be None.
When comparing a value x with None, and x is a Python object, note the following:
- x is None and x is not None are very efficient.
- They translate directly to C-pointer comparisons.
- x == None and x != None or if x: ... (a boolean condition), will invoke Python operations and will therefore be much slower.
By default, weak references are not supported.
It can be enabled by declaring a C attribute of the object type called __weakref__():
cdef class ExplodingAnimal:
"""This animal will self-destruct when it is
no longer strongly referenced."""
cdef object __weakref__
- Python objects defined in the Python core.
- Non-Cython extension modules
The following example lets you get at the C-level members of Python’s built-in “complex” object:
cdef extern from "complexobject.h":
struct Py_complex:
double real
double imag
ctypedef class __builtin__.complex [object PyComplexObject]:
cdef Py_complex cval
# A function which uses the above type
def spam(complex c):
print "Real:", c.cval.real
print "Imag:", c.cval.imag
Note
Some important things in the example: #. ctypedef has been used because because Python’s header file has the struct decalared with:
ctypedef struct {
...
} PyComplexObject;
- Don’t declare any methods, because they are Python method class the are not needed.
- Similiar to structs and unions, extension classes declared inside a cdef extern from block only need to declare the C members which you will actually need to access in your module.
Note
Only available to public and extern extension types.
Example:
[object object_struct_name, type type_object_name ]
object_struct_name is the name to assume for the type’s C-struct.
type_object_name is the name to assume for the type’s statically declared type-object.
The object and type clauses can be written in any order.
For cdef extern from declarations, This clause is required.
- The object clause is required because Cython must generate code that is compatible with the declarations in the header file.
- Otherwise the object clause is optional.
- When used in an expression, it refers to a “module-level” global variable holding the type’s constructor (i.e. it’s type-object)
- It can also be used as a C-type name to declare a “type” for variables, arguments, and return values.
Example:
cdef extern class MyModule.Spam:
...
- The name “Spam” serves both of these roles.
- Only “Spam” can be used as the type-name.
- The constructor can be referred to by other names.
- Upon an explicit import of “MyModule”...
- MyModule.Spam() could be used as the constructor call.
- MyModule.Spam could not be used as a type-name
When an “as” clause is used, the name specified takes over both roles:
cdef extern class MyModule.Spam as Yummy:
...
- Yummy becomes both type-name and a name for the constructor.
- There other ways of course, to get hold of the constructor, but Yummy is the only usable type-name.