summaryrefslogtreecommitdiff
path: root/docs/src/tutorial/cdef_classes.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/src/tutorial/cdef_classes.rst')
-rw-r--r--docs/src/tutorial/cdef_classes.rst133
1 files changed, 94 insertions, 39 deletions
diff --git a/docs/src/tutorial/cdef_classes.rst b/docs/src/tutorial/cdef_classes.rst
index a95b802a8..c3cd08ead 100644
--- a/docs/src/tutorial/cdef_classes.rst
+++ b/docs/src/tutorial/cdef_classes.rst
@@ -1,5 +1,9 @@
+***********************************
Extension types (aka. cdef classes)
-===================================
+***********************************
+
+.. include::
+ ../two-syntax-variants-used
To support object-oriented programming, Cython supports writing normal
Python classes exactly as in Python:
@@ -8,8 +12,8 @@ Python classes exactly as in Python:
Based on what Python calls a "built-in type", however, Cython supports
a second kind of class: *extension types*, sometimes referred to as
-"cdef classes" due to the keywords used for their declaration. They
-are somewhat restricted compared to Python classes, but are generally
+"cdef classes" due to the Cython language keywords used for their declaration.
+They are somewhat restricted compared to Python classes, but are generally
more memory efficient and faster than generic Python classes. The
main difference is that they use a C struct to store their fields and methods
instead of a Python dict. This allows them to store arbitrary C types
@@ -24,36 +28,68 @@ single inheritance. Normal Python classes, on the other hand, can
inherit from any number of Python classes and extension types, both in
Cython code and pure Python code.
-So far our integration example has not been very useful as it only
-integrates a single hard-coded function. In order to remedy this,
-with hardly sacrificing speed, we will use a cdef class to represent a
-function on floating point numbers:
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/math_function_2.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/math_function_2.pyx
+
+The ``cpdef`` command (or ``@cython.ccall`` in Python syntax) makes two versions
+of the method available; one fast for use from Cython and one slower for use
+from Python.
-.. literalinclude:: ../../examples/tutorial/cdef_classes/math_function_2.pyx
+Now we can add subclasses of the ``Function`` class that implement different
+math functions in the same ``evaluate()`` method.
-The directive cpdef makes two versions of the method available; one
-fast for use from Cython and one slower for use from Python. Then:
+Then:
-.. literalinclude:: ../../examples/tutorial/cdef_classes/sin_of_square.pyx
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/sin_of_square.py
+ :caption: sin_of_square.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/sin_of_square.pyx
+ :caption: sin_of_square.pyx
This does slightly more than providing a python wrapper for a cdef
method: unlike a cdef method, a cpdef method is fully overridable by
-methods and instance attributes in Python subclasses. It adds a
+methods and instance attributes in Python subclasses. This adds a
little calling overhead compared to a cdef method.
To make the class definitions visible to other modules, and thus allow for
efficient C-level usage and inheritance outside of the module that
-implements them, we define them in a :file:`sin_of_square.pxd` file:
+implements them, we define them in a ``.pxd`` file with the same name
+as the module. Note that we are using Cython syntax here, not Python syntax.
.. literalinclude:: ../../examples/tutorial/cdef_classes/sin_of_square.pxd
+ :caption: sin_of_square.pxd
+
+With this way to implement different functions as subclasses with fast,
+Cython callable methods, we can now pass these ``Function`` objects into
+an algorithm for numeric integration, that evaluates an arbitrary user
+provided function over a value interval.
Using this, we can now change our integration example:
-.. literalinclude:: ../../examples/tutorial/cdef_classes/integrate.pyx
+.. tabs::
+ .. group-tab:: Pure Python
-This is almost as fast as the previous code, however it is much more flexible
-as the function to integrate can be changed. We can even pass in a new
-function defined in Python-space::
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/integrate.py
+ :caption: integrate.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/integrate.pyx
+ :caption: integrate.pyx
+
+We can even pass in a new ``Function`` defined in Python space, which overrides
+the Cython implemented method of the base class::
>>> import integrate
>>> class MyPolynomial(integrate.Function):
@@ -63,39 +99,58 @@ function defined in Python-space::
>>> integrate(MyPolynomial(), 0, 1, 10000)
-7.8335833300000077
-This is about 20 times slower, but still about 10 times faster than
-the original Python-only integration code. This shows how large the
-speed-ups can easily be when whole loops are moved from Python code
-into a Cython module.
+Since ``evaluate()`` is a Python method here, which requires Python objects
+as input and output, this is several times slower than the straight C call
+to the Cython method, but still faster than a plain Python variant.
+This shows how large the speed-ups can easily be when whole computational
+loops are moved from Python code into a Cython module.
Some notes on our new implementation of ``evaluate``:
- - The fast method dispatch here only works because ``evaluate`` was
- declared in ``Function``. Had ``evaluate`` been introduced in
- ``SinOfSquareFunction``, the code would still work, but Cython
- would have used the slower Python method dispatch mechanism
- instead.
+- The fast method dispatch here only works because ``evaluate`` was
+ declared in ``Function``. Had ``evaluate`` been introduced in
+ ``SinOfSquareFunction``, the code would still work, but Cython
+ would have used the slower Python method dispatch mechanism
+ instead.
- - In the same way, had the argument ``f`` not been typed, but only
- been passed as a Python object, the slower Python dispatch would
- be used.
+- In the same way, had the argument ``f`` not been typed, but only
+ been passed as a Python object, the slower Python dispatch would
+ be used.
- - Since the argument is typed, we need to check whether it is
- ``None``. In Python, this would have resulted in an ``AttributeError``
- when the ``evaluate`` method was looked up, but Cython would instead
- try to access the (incompatible) internal structure of ``None`` as if
- it were a ``Function``, leading to a crash or data corruption.
+- Since the argument is typed, we need to check whether it is
+ ``None``. In Python, this would have resulted in an ``AttributeError``
+ when the ``evaluate`` method was looked up, but Cython would instead
+ try to access the (incompatible) internal structure of ``None`` as if
+ it were a ``Function``, leading to a crash or data corruption.
There is a *compiler directive* ``nonecheck`` which turns on checks
for this, at the cost of decreased speed. Here's how compiler directives
are used to dynamically switch on or off ``nonecheck``:
-.. literalinclude:: ../../examples/tutorial/cdef_classes/nonecheck.pyx
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/nonecheck.py
+ :caption: nonecheck.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/nonecheck.pyx
+ :caption: nonecheck.pyx
Attributes in cdef classes behave differently from attributes in regular classes:
- - All attributes must be pre-declared at compile-time
- - Attributes are by default only accessible from Cython (typed access)
- - Properties can be declared to expose dynamic attributes to Python-space
+- All attributes must be pre-declared at compile-time
+- Attributes are by default only accessible from Cython (typed access)
+- Properties can be declared to expose dynamic attributes to Python-space
+
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/wave_function.py
+ :caption: wave_function.py
+
+ .. group-tab:: Cython
-.. literalinclude:: ../../examples/tutorial/cdef_classes/wave_function.pyx
+ .. literalinclude:: ../../examples/tutorial/cdef_classes/wave_function.pyx
+ :caption: wave_function.pyx