Python functions are defined using the def statement, as in Python. They take Python objects as parameters and return Python objects.
C functions are defined using the new cdef statement. They take either Python objects or C values as parameters, and can return either Python objects or C values.
Within a Pyrex module, Python functions and C functions can call each other freely, but only Python functions can be called from outside the module by interpreted Python code. So, any functions that you want to "export" from your Pyrex module must be declared as Python functions.
Parameters of either type of function can be declared to have C data types, using normal C declaration syntax. For example,
When a parameter of a Python function is declared to have a C data type, it is passed in as a Python object and automatically converted to a C value, if possible. Automatic conversion is currently only possible for numeric types and string types; attempting to use any other type for the parameter of a Python function will result in a compile-time error.def spam(int i, char *s):
...cdef int eggs(unsigned long l, float f):
...
C functions, on the other hand, can have parameters of any type, since they're passed in directly using a normal C function call.
Reference counting for these objects is performed automatically according to the standard Python/C API rules (i.e. borrowed references are taken as parameters and a new reference is returned).cdef spamobjs(x, y):
...
The name object can also be used to explicitly declare something as a Python object. This can be useful if the name being declared would otherwise be taken as the name of a type, for example,
declares a parameter called int which is a Python object. You can also use object as the explicit return type of a function, e.g.cdef ftang(object int):
...
It is probably a good idea to always be explicit about object parameters in C functions, in the interests of clarity.cdef object ftang(object int):
...
and C struct, union or enum types:cdef int i, j, k
cdef float f, g[42], *h
There is currently no special syntax for defining a constant, but you can use an anonymous enum declaration for this purpose, for example,cdef struct Grail:
int age
float volumecdef union Food:
char *spam
float *eggscdef enum CheeseType:
cheddar, edam,
camembertcdef enum CheeseState:
hard = 1
soft = 2
runny = 3
cdef enum:Note that the words struct, union and enum are used only when defining a type, not when referring to it. For example, to declare a variable pointing to a Grail you would write
tons_of_spam = 3
and notcdef Grail *gp
There is also a ctypedef statement for giving names to types, e.g.cdef struct Grail *gp # WRONG
ctypedef unsigned long ULongctypedef int *IntPtr
If Python objects and C values are mixed in an expression, conversions are performed automatically between Python objects and C numeric or string types.
Reference counts are maintained automatically for all Python objects, and all Python operations are automatically checked for errors, with appropriate action taken.
cdef char *p, float *q
p = <char*>q
for i in range(n):won't be very fast, even if i and n are declared as C integers, because range is a Python function. For iterating over ranges of integers, Pyrex has another form of for-loop:
...
for i from 0 <= i < n:If the loop variable and the lower and upper bounds are all C integers, this form of loop will be much faster, because Pyrex will translate it into pure C code.
...
Some things to note about the for-from loop:
If you want such a C function to be able to propagate exceptions to its caller, you need to declare an exception value for it. Here is an example:
cdef int spam() except -1:With this declaration, whenever an exception occurs inside spam , it will immediately return with the value -1. Furthermore, whenever a call to spam returns -1, an exception will be assumed to have occurred and will be propagated.
...
Note that, in this case, you should never explicitly return -1 from spam. If all possible return values are legal and you can't reserve one entirely for signalling errors, you can use an alternative form of exception value declaration:
cdef int spam() except? -1:The "?" indicates that the value -1 only indicates a possible error. In this case, Pyrex generates a call to PyErr_Occurred if the exception value is returned, to make sure it really is an error.
...
There is also a third form of exception value declaration:
cdef int spam() except *:This form causes Pyrex to generate a call to PyErr_Occurred after every call to spam, regardless of what value it returns. If you have a function returning void that needs to propagate errors, you will have to use this form, since there isn't any return value to test.
...
Some things to note:
cdef extern int spam_countercdef extern void order_spam(int tons)
To achieve this, you can tell Pyrex that the declarations are to be found in a C header file, like this:
The cdef extern from clause does three things:cdef extern from "spam.h":int spam_countervoid order_spam(int tons)
ctypedef int size_t
cdef extern from *:
...
It's important to make the Pyrex declarations match the style used in the header file, so that Pyrex can emit the right sort of references to the type in the code it generates. To make this possible, Pyrex provides two different syntaxes for declaring a struct, union or enum type. The style introduced above corresponds to the use of a tag name. To get the other style, you prefix the declaration with ctypedef , as illustrated below.
The following table shows the various possible styles that can be found in a header file, and the corresponding Pyrex declaration that you should put in the cdef exern from block. Struct declarations are used as an example; the same applies equally to union and enum declarations.
Note that in all the cases below,
you refer to the type in Pyrex code simply as Foo
, not struct Foo.
C code | Possibilities for corresponding Pyrex code | Comments | |
1 | struct Foo { ... }; |
cdef struct Foo: ... |
Pyrex will refer to the type as struct Foo in the generated C code. |
2 | typedef struct { ... } Foo; |
ctypedef struct Foo: ... |
Pyrex will refer to the type simply as Foo in the generated C code. |
3 | typedef struct foo {
... } Foo; |
cdef struct foo:
... ctypedef foo Foo #optional |
If the C header uses both a tag and a typedef with different names, you can use either form of declaration in Pyrex (although if you need to forward reference the type, you'll have to use the first form). |
ctypedef struct Foo: ... |
|||
4 | typedef struct Foo { ... } Foo; |
cdef struct Foo: ... |
If the header uses the same name for the tag and the typedef, you won't be able to include a ctypedef for it -- but then, it's not necessary. |
will allow you to create Python strings containing null bytes.cdef extern from "Python.h":object PyString_FromStringAndSize(char *s, int len)
cdef class Shrubbery:As you can see, a Pyrex extension type definition looks a lot like a Python class definition. Within it, you use the def statement to define methods that can be called from Python code. You can even define many of the special methods such as __init__ as you would in Python.cdef int width, height
def __init__(self, w, h):
self.width = w
self.height = hdef describe(self):
print "This shrubbery is", self.width, \
"by", self.height, "cubits."
The main difference is that you can use the cdef statement to define attributes. The attributes may be Python objects (either generic or of a particular extension type), or they may be of any C data type. So you can use extension types to wrap arbitrary C data structures and provide a Python-like interface to them.
Some other differences between extension types and Python classes:
The set of attributes of an extension type is fixed at compile time; you can't add attributes to an extension type instance at run time simply by assigning to them, as you could with a Python class instance. (You can subclass the extension type in Python and add attributes to instances of the subclass, however.)
Attributes defined with cdef are only accessible from Pyrex code, not from Python code. (A way of defining Python-accessible attributes is planned, but not yet implemented. In the meantime, use accessor methods.)
To access the cdef-attributes of an extension type instance, the Pyrex compiler must know that you have an instance of that type, and not just a generic Python object. It knows this already in the case of the "self" parameter of the methods of that type, but in other cases you will have to tell it by means of a declaration. For example, def widen_shrubbery(Shrubbery sh, extra_width):
sh.width = sh.width + extra_widthSome of the __xxx__ special methods behave differently from their Python counterparts, and some of them are named differently as well. See here for more information.
cdef class Shrubbery # forward declarationcdef class Shrubber:
cdef Shrubbery work_in_progresscdef class Shrubbery:
cdef Shrubber creator
cdef extern from "complexobject.h":Note the use of ctypedef class. This is because, in the Python header files, the PyComplexObject struct is declared withstruct Py_complex:
double real
double imagctypedef class complex [type PyComplex_Type, object PyComplexObject]:
cdef Py_complex cval
ctypedef struct {Here is an example of a function which uses the complex type declared above.
...
} PyComplexObject;
def spam(complex c):When declaring an external extension type, you don't declare any methods. Declaration of methods is not required in order to call them, because the calls are Python method calls. Also, as with structs inside a cdef extern from block, you only need to declare those C members which you wish to access.
print "Real:", c.cval.real
print "Imag:", c.cval.imag
Both the type and object parts are optional. If you don't specify the object part, Pyrex assumes it's the same as the name of the class. For instance, the class declaration could also be written
class PyComplexObject [type PyComplex_Type]:but then you would have to write the function as
...
def spam(PyComplexObject c):You can also omit the type part of the specification, but this will severely limit what you can do with the type, because Pyrex needs the type object in order to perform type tests. A type test is required every time an argument is passed to a Python function declared as taking an argument of that type (such as spam() above), or a generic Python object is assigned to a variable declared to be of that type. Without access to the type object, Pyrex won't allow you to do any of those things. Supplying the type object name is therefore recommended if at all possible.
...
If we call the class something else, however, such as "PyComplexObject" as in the second version above, we would have to use "PyComplexObject" as the type name. Both "complex" and "PyComplexObject" would work as constructors ("complex" because it's a built-in name), but only "PyComplexObject" would work as a type name for declaring variables and arguments.
The above restrictions will most likely remain, since removing them would be difficult and they're not really needed for Pyrex's intended applications.Function definitions (whether using def or cdef) cannot be nested within other function definitions.
Class definitions can only appear at the top level of a module, not inside a function.
The import * form of import is not allowed anywhere (other forms of the import statement are fine, though).
Generators cannot be defined in Pyrex.
There are also some temporary limitations which may eventually be lifted:
There are probably also some other gaps which I can't think of at the moment.Class and function definitions cannot be placed inside control structures.
In-place operators (+=, etc) are not yet supported.
List comprehensions are not yet supported.
where grail.h actually containscdef extern from "grail.h":
char *nun
and you doextern const char *nun;
which will cause the compiler to complain. You can work around it by casting away the constness:cdef void oral(char *s):
#something that doesn't change s...oral(nun)
oral(<char *>nun)