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 using def.
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):
...
In the interests of clarity, it is probably a good idea to always be explicit about object parameters in C functions.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
cdef:
struct Spam:
int tons
int i
float f
Spam *p
void f(Spam *s):
print s.tons, "Tons of spam"
C
types |
From
Python types |
To
Python types |
---|---|---|
[unsigned]
char [unsigned] short int, long |
int,
long |
int |
unsigned
int unsigned long [unsigned] long long |
int, long |
long |
float,
double, long double |
int,
long, float |
float |
char
* |
str |
str |
cdef char *sthen Pyrex will produce the error message "Obtaining char * from temporary Python value". The reason is that concatenating the two Python strings produces a new Python string object that is referenced only by a temporary internal variable that Pyrex generates. As soon as the statement has finished, the temporary variable will be decrefed and the Python string deallocated, leaving s dangling. Since this code could not possibly work, Pyrex refuses to compile it.
s = pystring1 + pystring2
cdef char *sIt is then your responsibility to hold the reference p for as long as necessary.
p = pystring1 + pystring2
s = p
In Pyrex, instead of printing the name of the current module, this prints the name of the builtins module. The reason is that because Pyrex hasn't seen a declaration of anything called __name__ in the module, it's assumed to reside in the builtins. The solution is to use a global statement to declare __name__ as a module-level name:
global
__name__
print __name__
because, due to the assignment in the last line, True will always be looked up in the module-level scope. You would have to do something like this instead:try:
x = True
except NameError:
True = 1
import __builtin__
try:
True = __builtin__.True
except AttributeError:
True = 1
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.
c'X'
cdef char *p, float *qWarning: Don't attempt to use a typecast to convert between Python and C data types -- it won't do the right thing. Leave Pyrex to perform the conversion automatically.
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 0 <= i < n:Provided 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 integer for-loop:
try:
start_engine()
except HovercraftError, e, tb:
print "Got an error:", e
traceback.print_tb(tb)
try:
start_engine()
except HovercraftError, e:
print "Unable to start:", e
raise # the exception caught by the enclosing except clause
If you want a C function that does not return a Python object 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.
...
When you declare an exception value for a function, you should never explicitly return that value. 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:
int (*grail)(int, char *) except -1
and expect an exception to be automatically raised if a call to fopen returns NULL. The except clause doesn't work that way; its only purpose is for propagating exceptions that have already been raised, either by a Pyrex function or a C function that calls Python/C API routines. To get an exception from a non-Python-aware function such as fopen, you will have to check the return value and raise it yourself, for example,cdef extern FILE *fopen(char *filename, char *mode) except NULL # WRONG!
cdef FILE *p
p = fopen("spam.txt", "r")
if p == NULL:
raise SpamError("Couldn't open the spam file")
The contents of the named file are textually included at that point. The included file can contain any complete statements or declarations that are valid in the context where the include statement appears, including other include statements. The contents of the included file should begin at an indentation level of zero, and will be treated as though they were indented to the level of the include statement that is including the file.include "spamstuff.pxi"
Python functions can have keyword-only arguments listed after the * parameter and before the ** paramter if any, e.g.
def f(a, b, *args, c, d = 42, e, **kwds):Here c, d and e cannot be passed as position arguments and must be passed as keyword arguments. Furthermore, c and e are required keyword arguments, since they do not have a default value.
...
def g(a, b, *, c, d):takes exactly two positional parameters and has two required keyword parameters.
...
Type objects (type type) | Exceptions (type type) | |
buffer enumerate file float int long open property str tuple xrange |
Exception StopIteration StandardError ArithmeticError LookupError AsssertionError EOFError FloatingPointError EnvironmentError IOError OSError ImportError IndexError KeyError KeyboardInterrupt MemoryError NameError OverflowError RuntimeError NotImplementedError SyntaxError |
IndentationError TabError ReferenceError SystemError SystemExit TypeError UnboundLocalError UnicodeError UnicodeEncodeError UnicodeDecodeError UnicodeTranslateError ValueError ZeroDivisionError Warning UserWarning DeprecationWarning PendingDeprecationWarning SyntaxWarning OverflowWarning RuntimeWarning FutureWarning |
Constants (type object) | ||
True False Ellipsis |
||
Function and arguments | Return type | Python/C API Equivalent |
abs(obj) | object | PyNumber_Absolute |
bool(obj) (Note 3) | int | PyObject_IsTrue |
delattr(obj, name) | int | PyObject_DelAttr |
dir(obj) | object | PyObject_Dir |
divmod(x, y) | object | PyNumber_Divmod |
getattr(obj, name)
(Note 1) getattr3(obj, name, default) |
object | PyObject_GetAttr |
hasattr(obj, name) | int | PyObject_HasAttr |
hash(obj) | int | PyObject_Hash |
cintern(char *) (Note 5) | object | PyString_InternFromString |
isinstance(obj, type) | int | PyObject_IsInstance |
issubclass(obj, type) | int | PyObject_IsSubclass |
issubtype(type, type) (Note 4) | int | PyType_IsSubType |
iter(obj) | object | PyObject_GetIter |
iter2(obj, obj) | object | PyCallIter_New |
len(obj) | Py_ssize_t | PyObject_Length |
pow(x, y, z) (Note 2) | object | PyNumber_Power |
reload(obj) | object | PyImport_ReloadModule |
repr(obj) | object | PyObject_Repr |
setattr(obj, name) | void | PyObject_SetAttr |
typecheck(obj, type) (Note 4) | int | PyObject_TypeCheck |
Method or Attribute | Return type | Python/C API Equivalent | Notes |
Type dict | |||
clear() | PyDict_Clear | ||
copy() | PyDict_Copy | ||
items() | object | PyDict_Items | |
keys() | object | PyDict_Keys | |
values() | object | PyDict_Values | |
merge(obj, override) | PyDict_Merge | Merge items from a mapping | |
update(obj) | PyDict_Update | ||
merge_pairs(obj, override) | PyDict_MergeFromSeq2 | Merge (key, value) pairs from a sequence | |
Type list | |||
insert(int, obj) | PyList_Insert | ||
append(obj) | PyList_Append | ||
sort() | PyList_Sort | ||
reverse() | PyList_Reverse | ||
as_tuple() | object | PyList_AsTuple | |
Type slice | |||
indices() | object | PySlice_Indices | |
start | object | ||
stop | object | ||
step | object |
DEF FavouriteFood = "spam"The right-hand side of the DEF must be a valid compile-time expression. Such expressions are made up of literal values and names defined using DEF statements, combined using any of the Python expression syntax.
DEF ArraySize = 42
DEF OtherArraySize = 2 * ArraySize + 17
UNAME_SYSNAME, UNAME_NODENAME, UNAME_RELEASE,The following selection of builtin constants and functions are also available:
UNAME_VERSION, UNAME_MACHINE
None, True, False,A name defined using DEF can be used anywhere an identifier can appear, and it is replaced with its compile-time value as though it were written into the source at that point as a literal. For this to work, the compile-time expression must evaluate to a Python value of type int, long, float or str.
abs, bool, chr, cmp, complex, dict, divmod, enumerate,
float, hash, hex, int, len, list, long, map, max, min,
oct, ord, pow, range, reduce, repr, round, slice, str,
sum, tuple, xrange, zip
cdef int a1[ArraySize]
cdef int a2[OtherArraySize]
print "I like", FavouriteFood
IF UNAME_SYSNAME == "Windows":The ELIF and ELSE clauses are optional. An IF statement can appear anywhere that a normal statement or declaration can appear, and it can contain any statements or declarations that would be valid in that context, including DEF statements and other IF statements.
include "icky_definitions.pxi"
ELIF UNAME_SYSNAME == "Darwin":
include "nice_definitions.pxi"
ELIF UNAME_SYSNAME == "Linux":
include "penguin_definitions.pxi"
ELSE:
include "other_definitions.pxi"