Numerical Recipes code, or for that matter any other C++ code, can easily be invoked from within Python. For some tasks, C++ executes hugely faster than Python code, and you can also access features in Numerical Recipes (or elsewhere) that are not available in Python. You can code some parts of your project in Python and other parts in C++, and control the whole project from the Python console or a Python script.
Conventions and Overview
Compiling "Hello, world!"
Example 1: Getting and Return Scalar Variables
Example 2: Vector, Matrix, String, List, and Dict Variables
Example 3: Encapsulating a C++ Function for Other C++ Extensions
Example 4: Sending Functions to C++ Extensions as Arguments
Example 5: Persistent Objects and Object-Oriented Extensions
Example 6: Sharing Objects by Name Between Python and C++
Appendix: List of All NRpy Facilities
Python code (or interactive input) is shown on a red background:
C++ code is shown on a green background:
A blue background is used for input to the Linux shell, or output from running code.
NR always means, of course, Numerical Recipes.
By convention, identifiers beginning with "Py" are Python things, as documented in the Python C API Reference Manual. We hope to shield you from as many of those things as we can, but, if you want, you can use any or all of the Python C API interface inside of a NR-style Python extension.
Identifiers that begin with "NRpy" (we pronounce it "nurpee") are defined in our header file nr3python.h, and are designed to make it easier to go back and forth between Python and C++ (especially C++ with NR). In the code above, the only example is "NRpyObject()" which, as written, returns the more Pythonic "Py_None"
When we say "standard boilerplate", we always mean code analgous to the above for (i) a PyMethodDef array, and (ii) a PyMODINIT_FUNC function. These are just ugly little pieces of the Python C API that we can't easily shield you from. They always go at the end of your code and are completely formulaic. Get used to them.
The hardest part of this whole tutorial is getting code like the above to compile. Once you figure out how to do this, the rest will be easy.
Copy the green box above into a file "somemodule.cpp" and try to compile it. In Windows, we do this in MS Visual Studio. In Linux we use g++ on the command line, like this:
(You might or might not need the "-fpermissive" and the "-w" options. You will definitely need the "-fPIC" and "-shared".)
Your first hurdle might be that the compiler can't find "nr3python.h". This means that you haven't downloaded our interface file and put it in a place that the compiler can find. Do that.
Your second hurdle might be that the compiler can't find files "Python.h" and "numpy/arrayobject.h", which are referenced in nr3python.h. These are files that come with your Python distribution. But you can't just put copies of them somewhere, because they include other files expected to be at fixed relative locations. You have to figure out where their directories are, and add these to your #include search path. (By the way, feel free to edit the first few lines of your downloaded copy of nr3python.h if that makes things easier.)
For Linux, the command lines above will produce the right kind of output file, ending in ".so". and you are done. In Windows, there is one more step: If all now goes well, your file will compile but the linker will complain that it can't find "main()". This is a good sign! You just need to change what kind of file that the compiler is producing, as described in this fine print:
(By the way, Visual Studio doesn't make it easy to copy all of a project's settings to a new project. There are various commercial tools for this, so that you don't have to keep going through all the above steps for each new Python extension that you write. We use a tool called CopyWiz.)
You might have an additional hurdle that the linker can't find some expected libraries that are in the Python distribution. If so, you'll need to locate them and set your library path accordingly. Hint: If it can't find a library like "python27_d" (ending in "_d"), then you are compiling in debug mode. As normally distributed, Python doesn't have a debug mode library, so you'll have to instead compile in release (or not debug) mode.
Some users prefer to use Python's distutils package to get the compiling, linking, and installation steps right. You don't really need it for the kinds of things in this tutorial, but you can find out about it here.
When you get things right, you will have produced a ".pyd" file (Windows), or a ".so" file (Linux). Put it in your working directory, fire up the Python interpreter and try it out:
It can be frustrating to get this to work the first time, but you only have to climb this particular tree once. It will be worth it! If you are stuck, try Googling "compile python extension" or "build python extension", or similar.
Here is a module file that makes available three functions that can be called from Python. The first function, func, has one double precision floating point argument (Python calls this "float", while C++ calls this "double") and returns one of the same type. Numerical functions of any complexity are very slow in Python, because they (and any functions they contain) are evaluated from byte code over and over; so writing them in C++ can give an enormous speedup.
Notice the use of NRpyArgs to unpack the incoming arguments, and the use of NRpyDoub (or, below, NRpyInt) to cast them to NR's "Doub" (double) or "Int" (int) C++ types. Likewise notice the use of NRpyObject to return a single variable. (If you were returning nothing, you would return "NRpyObject()", as in the "Hello, world" example, above.)
The second function takes three arguments, two Doub and one Int, and returns three values, also two Doub and one Int. Notice how NRpyTuple is used to return more than one value. Since NRpyTuple is actually a varargs C++ function, it needs a way to recognize its last argument; hence that last argument (not returned to Python) must always be NULL, as shown.
Your C++ function can determine dynamically the number of arguments being sent from Python and do whatever it wants with them. Here is an example that simply sums all its Doub arguments.
The file ends with standard boilerplate,
Notice how the "standard boilerplate" works for the whole file. Its PyMethodDef contains an entry for every function that you are making available to Python. The first (string) argument is what its name is in Python. The second argument is which function in this file it corresponds to, the rest is self-explanatory.
Notice also that the PyMODINIT_FUNC must always be named "init" concatenated with the module name, here "example1".
Now we can use all three functions from Python:
For vectors and matrices on the Python side, we always use numpy, and we assume that you do, too. On the C++ side we use NR's vector and matrix classes, for example, VecDoub and MatDoub.
Our second example file exports to Python a single function "func" that does nothing useful except demonstrate how to move different kinds of objects from Python to C++ and the reverse. The file begins,
By now you get the idea: You have to know the intended type of each argument that comes in, and you call a C++ constructor that binds it to a corresponding C++ object. These constructors are all in nr3python.h. If you are a C++ wizard, you can easily add new ones to meet additional needs.
Let's look at the string argument:
Mystring is a null-terminated string, actually a pointer to Python's internal representation of that string. Since the consequences of your altering a Python immutable object are imponderable (doing so might cause the world to end) mystring was declared as "const" on the C++ side above. Then the compiler protects us from altering it. If you want to monkey with a string's contents, make a local copy, as shown above.
Vectors and matrices are instantiated as objects whose data points back into Python. Thus, there is no unnecessary copying of large objects. You can change values in the vector or matrix, or even resize it, and the corresponding change will be reflected on the Python side. For most purposes you can think of the vector or matrix as a reference to the corresponding Python object. However, copying the vector or matrix on the C++ side produces a copy, not a reference, as shown here.
We provide only a rudimentary interface to Python lists, intended for grabbing their values when you want a C++ extension to compute with them. (We also let you reset any existing value, and create a list of numerical values or strings.) Generally you'll want to do any sophisticated manipulation of lists on the Python side. If you really need to do more on the C++ side, you can of course use the Python C API directly.
Our easy interface to Python dicts (what C++ programmers would call hash memory objects) allows for values and keys that are Doub, Int, or string. If you need anything more complicated, do it on the Python side. It is sometimes a big convenience to use a dict in C++ programs, however. As shown below, you can create your own, independently of whether you intend to return it to the calling Python program.
Notice above that set() takes Doub, Int, or string arguments, while get() returns a PyObject*, and must be cast to the desired type (e.g., Doub, Int, or string) by NRpyDoub() or one of its siblings. We do it this way so that you can test if the result of a get() is Py_None, meaning that there is nothing stored under that key.
Next, how do we return objects like the above to the calling Python program? Any single object can be returned by returning a single NRpyObject(). Multiple objects, as we saw in Example 1, are returned by using NRpyTuple(), as below.
It might seem perverse that for incoming arguments we must know exactly what type to cast them to, while for returned values a single NRpyObject() function works for all. That is just an illusion. All C++ objects are in fact statically typed. NRpyObject() is just a templated function that specializes to the type of its argument -- which it knows at compile time. You'll soon get used to the oddities of such interactions between a fully dynamically typed language, like Python, and a fully statically typed one, like C++.
Finally, at the end of the file there is the standard boilerplate,
Here is what Example 2 looks like from the Python side.
Example 1 already showed how export a C++ function so that it is callable from Python. But we might also want to use the fast C++ function as the argument of another C++ module, for example one that does numerical integration or solves a differential equation. This Example 3 shows how to do this. (In Example 4 we'll see what this looks like on the receiving side, in the C++ module that receives the function argument.)
So we have exported to Python two objects, one, "func" for evaluating our function "thefunc" within Python, and another, "funcwrapper", for encapsulating it for use in another C++ module.
If your function "thefunc" needed some global state, for example the values of some parameters, you could provide an additional method for setting that state, for example, adding to the above file,
(Of course you also need to add "setstate" to the standard boilerplate.) The above would not be a good way to define large persistent objects, because we would not be giving Python a chance to manage their memory. For that, see Example 5. But for the occasional global parameter value it is fine (stipulating all the usual cautions about global variables).
Let's now see how Example3 is used from the Python side:
This is the companion example to Example 3: How do we use functions in a C++ extension when they have been sent in as arguments. They might be functions defined in Python (think if you really want to do this, since these would be slow every time they are called), or else fast functions defined in another C++ module.
The example starts with a Python-callable routine whose first argument is a Python function that is then called as a Doub(Doub) C++ function.
You always have to know the return type of a Python function from
the very start -- again a consequence of C++ being a statically
typed language. Notice that "NRpyPyFunction
If the Python function referenced by "args[0]" had more than one
argument, the constructor call above would be unchanged. However
the invocation would change to "y = func(x,z)" or whatever.
This works by overloading "func" in nr3python.h. We provide overloads
for up to 4 arguments. (You can add more in nr3python.h if you
want.) Checking is done to be sure that your C++ code is calling Python
functions with the correct number of arguments.
If you are coding for speed, it is more likely that the function
that you want to use was already coded in another C++ module,
and that you brought it into Python as an encapsulated C++ function,
as demonstrated in Example 3. Here is how to use such an
encapsulated object in a C++ module, when it arrives as
an argument.
Now you have to know the whole function type, here Doub(Doub). Once
you use NRpyCFunction to bind the capsule to the function
pointer, here "func", then it really is a C++ function.
Calls to it do not go through Python at all.
We have found it useful to have a way of using a function that
can be either a Python function or an encapsulated C++
function, which one not known except at run time. This has
some extra overhead on each function call, and nr3python.h
provides the facility only for functions with a single argment.
The usage is illustrated by,
We'll still need the standard boilerplate (below), but let's jump ahead to see
how these functions look from the Python side. This piece of a
.py file,
yields this output
So we've evaluated the same function with six different
combinations of where it is called from and whether it is evaluated
in Python or C++. You might be sure you understand how each of
the six is different.
On the other hand, this additional code,
produces the output,
We finish this example with a couple of additional tricks for using
Python functions within C++. First, you can set a global object
on the C++ side that remembers the name of a Python function.
Second, you can find out at run time, on each call, how many arguments
a Python function has, and do different things accordingly.
But, while these kinds of things are cute, remember that it is
usually foolish to use slow Python functions within fast C++ code!
Python input (continuing above input file):
Yielding:
The boilerplate for all of Example 4 is
If you are a serious C++ programmer, you will probably want,
a some point, to "wrap" a non-trivial C++ class (or struct)
for use in Python. In particular, you may want to create
more than one instance of that class, and be able to manipulate
the data within each instance independently -- a key aspect
of object-oriented programming.
Let's see how we might wrap the Numerical Recipes class
IQagent. We want to make a Python-callable constructor
and give access to the methods add(),
and report(), each acting on either a float quantity
or a vector of float quantities.
Here is what we want the Python to look like:
In the above, agent1 and agent2 are two instances of IQagent
(here called "example5" to fit the lesson plan). They each
store their own data (set with the addarray methods) and
then report back some computed quantities (by the report
and reportarray methods).
The C++ code looks like this:
The idea here is that IQagentWrapper is a class that
itself instantiates one instance of IQagent, internally
called myagent. IQagentWrapper could
additionally instantiate anything else, either as automatic
variables, or else by the "new" command. We just need to be sure
that the IQagentWrapper destructor properly deallocates anything that is
created.
NRpyCONSTRUCTOR is a special macro that binds IQagentWrapper's
constructor to (here) the Python callable function create(); and it
also registers IQagentWrapper's destructor as a Python callback,
so that it gets executed if you delete the instance with
Python's "del" command. Similarly NRpyCONNECT is a macro
that connects the other Python callable functions (add, addarray,
report, and reportarray) not just to their corresponding
IQagent methods in general, but to them in a particular
instance of IQagent. (You can look at the code in nr3python.h
to see how we do this, but you shouldn't actually need to
understand it!)
The boilerplate at the end of the file is:
Execution of the above Python code produces the output:
Up to now, we have moved data between Python and C++ by the
"approved" method of function arguments and return values. Actually,
for vectors and matrices, we didn't really move the data. For efficiency,
we moved only a reference to the data, not the data itself. Thus,
changes made in a vector or matrix argument on the C++ side would
also be made on the Python side. (This is a feature, not a bug!)
We can do the same kind of thing not just for function arguments,
but for anything by name in Python's namespace. For
integer and float scalars, this just retrieves a copy of the value.
For vectors and matrices, it binds a reference, so that changes made
in the C++ are also made back in Python. These changes can include
not just changing data values, but also resizing the objects.
The syntax is as follows:
By default, the above constructors look for the objects, by name,
in Python's __main__ namespace. If you want to look in a different
namespace, add an argument with the namespace name (as a string)
just after the Python variable name. Note also that if you
bind a Python name to a C++ global variable, then (back in Python) change
what that name's reference, the C++ global will remain bound to the
original reference -- which Python might deallocate. So, you
should only bind by name into objects that will pass out of
C++ scope when your C++ function returns to Python.
We can also do the reverse, namely to create scalars, vectors,
or matrices on the C++ side, then send them back into the Python
namespace. This is implemented in such a way that Python then
takes over management of the data memory, so it is ok to let it
pass out of scope on the C++ side. Here are examples:
The Python side of this example is:
This looks odd, because it prints variables that seemingly
have not been defined. But they have been defined,
in Python's __main__ namespace, by the call to example6.func.
The Python output is:
The boilerplate at the end of the above C++ file is:
Here is a list of all the "NRpy" facilities defined in nr3python.h.
Usage shown in the above examples:
unpack arguments from Python:
bind Python objects (by reference or name) to C++ variables:
wrappers for Python List, Dict, Tuple:
function wrappers:
wrap C++ variables for returning to Python:
put reference to C++ variable into Python namespace:
macros used to bind persistent objects:
Not directly used in the above examples (see nr3python.h for
usage):
PyObject* NRpyException(char *str, int die=1, int val=0)
NRpyArgs(PyObject* pyaargs)
int NRpyInt(PyObject* ob)
int NRpyInt(char *name, char *dict = NULL)
double NRpyDoub(PyObject* ob)
double NRpyDoub(char *name, char *dict = NULL)
char* NRpyCharP(PyObject *ob)
char* NRpyCharP(char *name, char *dict = NULL)
NRvector(PyObject *a) // used for VecDoub and VecInt
NRvector(char *name, char *dict = NULL)
NRmatrix(PyObject *a) // used for MatDoub and MatInt
NRmatrix(char *name, char *dict = NULL)
struct NRpyDict // and various methods
struct NRpyList // and various methods
PyObject* NRpyTuple(PyObject *first, ...)
void NRpyCFunction(T* &fptr, PyObject* ob)
NRpyPyFunction(PyObject *ob)
NRpyAnyFunction(PyObject *ob)
PyObject* NRpyObject(const double a)
PyObject* NRpyObject(const int a)
PyObject* NRpyObject(const bool a)
PyObject* NRpyObject(const char *a)
PyObject* NRpyObject() // Python None
PyObject* NRpyObject(NRpyList &a)
PyObject* NRpyObject(NRpyDict &a)
PyObject* NRpyObject(NRvector
PyObject* NRpyObject(NRmatrix
void NRpySend(T &a, char *name, char *dict=NULL)
NRpyCONNECT(CLASS,METHOD) // macro
NRpyCONSTRUCTOR(CLASS,METHOD) // macro
char NRpyMainName[] = "__main__";
PyObject* NRpyGetByName(char *name, char *dict = NULL)
int NRpyIsNumber(PyObject* ob)
int NRpyTypeOK
int NRpyTypeOK
int NRpyDataType
int NRpyDataType
double NRpyCast
int NRpyCast
char* NRpyCast