diff options
Diffstat (limited to 'docs/src/tutorial/cdef_classes.rst')
-rw-r--r-- | docs/src/tutorial/cdef_classes.rst | 133 |
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 |