diff options
author | 0dminnimda <52697657+0dminnimda@users.noreply.github.com> | 2021-07-14 22:02:02 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-14 21:02:02 +0200 |
commit | 54fa2b822565fc9ddef89aa399eb7562d5b76f07 (patch) | |
tree | 1aa89d6a85ea296dc85721358dd0be782c36c539 | |
parent | 696a1959b44ca9028d3f9468723b6b24335ad921 (diff) | |
download | cython-54fa2b822565fc9ddef89aa399eb7562d5b76f07.tar.gz |
docs: Pythonise the "Extension types (aka. cdef classes)" page (cdef_classes.rst) (GH-4232)
-rw-r--r-- | docs/examples/tutorial/cdef_classes/integrate.py | 17 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/integrate.pyx | 3 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/math_function_2.py | 5 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/math_function_2.pyx | 2 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/nonecheck.py | 20 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/nonecheck.pyx | 1 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/sin_of_square.py | 13 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/sin_of_square.pyx | 4 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/wave_function.py | 22 | ||||
-rw-r--r-- | docs/examples/tutorial/cdef_classes/wave_function.pyx | 1 | ||||
-rw-r--r-- | docs/src/tutorial/cdef_classes.rst | 97 | ||||
-rw-r--r-- | docs/src/two-syntax-variants-used | 22 |
12 files changed, 173 insertions, 34 deletions
diff --git a/docs/examples/tutorial/cdef_classes/integrate.py b/docs/examples/tutorial/cdef_classes/integrate.py new file mode 100644 index 000000000..cd02554e5 --- /dev/null +++ b/docs/examples/tutorial/cdef_classes/integrate.py @@ -0,0 +1,17 @@ +from cython.cimports.sin_of_square import Function, SinOfSquareFunction + +def integrate(f: Function, a: float, b: float, N: cython.int): + i: cython.int + + if f is None: + raise ValueError("f cannot be None") + + s: float = 0 + dx: float = (b - a) / N + + for i in range(N): + s += f.evaluate(a + i * dx) + + return s * dx + +print(integrate(SinOfSquareFunction(), 0, 1, 10000)) diff --git a/docs/examples/tutorial/cdef_classes/integrate.pyx b/docs/examples/tutorial/cdef_classes/integrate.pyx index a3e96f398..ad4c8540b 100644 --- a/docs/examples/tutorial/cdef_classes/integrate.pyx +++ b/docs/examples/tutorial/cdef_classes/integrate.pyx @@ -5,10 +5,13 @@ def integrate(Function f, double a, double b, int N): cdef double s, dx if f is None: raise ValueError("f cannot be None") + s = 0 dx = (b - a) / N + for i in range(N): s += f.evaluate(a + i * dx) + return s * dx print(integrate(SinOfSquareFunction(), 0, 1, 10000)) diff --git a/docs/examples/tutorial/cdef_classes/math_function_2.py b/docs/examples/tutorial/cdef_classes/math_function_2.py new file mode 100644 index 000000000..ba5917639 --- /dev/null +++ b/docs/examples/tutorial/cdef_classes/math_function_2.py @@ -0,0 +1,5 @@ +@cython.cclass +class Function: + @cython.ccall + def evaluate(self, x: float) -> float: + return 0 diff --git a/docs/examples/tutorial/cdef_classes/math_function_2.pyx b/docs/examples/tutorial/cdef_classes/math_function_2.pyx index 7d1446b7c..a4bdb7aa2 100644 --- a/docs/examples/tutorial/cdef_classes/math_function_2.pyx +++ b/docs/examples/tutorial/cdef_classes/math_function_2.pyx @@ -1,3 +1,5 @@ + cdef class Function: + cpdef double evaluate(self, double x) except *: return 0 diff --git a/docs/examples/tutorial/cdef_classes/nonecheck.py b/docs/examples/tutorial/cdef_classes/nonecheck.py new file mode 100644 index 000000000..dccb97435 --- /dev/null +++ b/docs/examples/tutorial/cdef_classes/nonecheck.py @@ -0,0 +1,20 @@ +# cython: nonecheck=True +# ^^^ Turns on nonecheck globally + +import cython + +@cython.cclass +class MyClass: + pass + +# Turn off nonecheck locally for the function +@cython.nonecheck(False) +def func(): + obj: MyClass = None + try: + # Turn nonecheck on again for a block + with cython.nonecheck(True): + print(obj.myfunc()) # Raises exception + except AttributeError: + pass + print(obj.myfunc()) # Hope for a crash! diff --git a/docs/examples/tutorial/cdef_classes/nonecheck.pyx b/docs/examples/tutorial/cdef_classes/nonecheck.pyx index 51c61006d..92c8fa42b 100644 --- a/docs/examples/tutorial/cdef_classes/nonecheck.pyx +++ b/docs/examples/tutorial/cdef_classes/nonecheck.pyx @@ -3,6 +3,7 @@ import cython + cdef class MyClass: pass diff --git a/docs/examples/tutorial/cdef_classes/sin_of_square.py b/docs/examples/tutorial/cdef_classes/sin_of_square.py new file mode 100644 index 000000000..1904ea934 --- /dev/null +++ b/docs/examples/tutorial/cdef_classes/sin_of_square.py @@ -0,0 +1,13 @@ +from cython.cimports.libc.math import sin + +@cython.cclass +class Function: + @cython.ccall + def evaluate(self, x: float) -> float: + return 0 + +@cython.cclass +class SinOfSquareFunction(Function): + @cython.ccall + def evaluate(self, x: float) -> float: + return sin(x ** 2) diff --git a/docs/examples/tutorial/cdef_classes/sin_of_square.pyx b/docs/examples/tutorial/cdef_classes/sin_of_square.pyx index d39ff374d..67af294b5 100644 --- a/docs/examples/tutorial/cdef_classes/sin_of_square.pyx +++ b/docs/examples/tutorial/cdef_classes/sin_of_square.pyx @@ -1,9 +1,13 @@ from libc.math cimport sin + cdef class Function: + cpdef double evaluate(self, double x) except *: return 0 + cdef class SinOfSquareFunction(Function): + cpdef double evaluate(self, double x) except *: return sin(x ** 2) diff --git a/docs/examples/tutorial/cdef_classes/wave_function.py b/docs/examples/tutorial/cdef_classes/wave_function.py new file mode 100644 index 000000000..7ff59a762 --- /dev/null +++ b/docs/examples/tutorial/cdef_classes/wave_function.py @@ -0,0 +1,22 @@ +from cython.cimports.sin_of_square import Function + +@cython.cclass +class WaveFunction(Function): + + # Not available in Python-space: + offset: float + + # Available in Python-space: + freq = cython.declare(cython.double, visibility='public') + + # Available in Python-space, but only for reading: + scale = cython.declare(cython.double, visibility='readonly') + + # Available in Python-space: + @property + def period(self): + return 1.0 / self.freq + + @period.setter + def period(self, value): + self.freq = 1.0 / value diff --git a/docs/examples/tutorial/cdef_classes/wave_function.pyx b/docs/examples/tutorial/cdef_classes/wave_function.pyx index 82ba1c7bc..34b144667 100644 --- a/docs/examples/tutorial/cdef_classes/wave_function.pyx +++ b/docs/examples/tutorial/cdef_classes/wave_function.pyx @@ -1,5 +1,6 @@ from sin_of_square cimport Function + cdef class WaveFunction(Function): # Not available in Python-space: diff --git a/docs/src/tutorial/cdef_classes.rst b/docs/src/tutorial/cdef_classes.rst index a95b802a8..ec2566dbd 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: @@ -24,17 +28,33 @@ 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. +.. 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 + 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: -.. literalinclude:: ../../examples/tutorial/cdef_classes/math_function_2.pyx - The directive cpdef makes two versions of the method available; one fast for use from Cython and one slower for use from Python. 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 @@ -43,13 +63,24 @@ 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: .. literalinclude:: ../../examples/tutorial/cdef_classes/sin_of_square.pxd + :caption: sin_of_square.pxd Using this, we can now change our integration example: -.. literalinclude:: ../../examples/tutorial/cdef_classes/integrate.pyx +.. tabs:: + .. group-tab:: Pure Python + + .. literalinclude:: ../../examples/tutorial/cdef_classes/integrate.py + :caption: integrate.py + + .. group-tab:: Cython + + .. literalinclude:: ../../examples/tutorial/cdef_classes/integrate.pyx + :caption: integrate.pyx 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 @@ -70,32 +101,50 @@ 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 diff --git a/docs/src/two-syntax-variants-used b/docs/src/two-syntax-variants-used index ef7b38903..4a9815345 100644 --- a/docs/src/two-syntax-variants-used +++ b/docs/src/two-syntax-variants-used @@ -1,13 +1,15 @@ -This page uses two different syntax variants: the Cython specific ``cdef`` syntax -and static Cython type declarations in -:ref:`pure Python code <pep484_type_annotations>`, -following `PEP-484 <https://www.python.org/dev/peps/pep-0484/>`_ type hints -and `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_ variable annotations. -To make good use of the latter, including C data types, etc., you need -the special ``cython`` module, which you can import with +.. note:: -.. code-block:: python + This page uses two different syntax variants: the Cython specific ``cdef`` syntax + and static Cython type declarations in + :ref:`pure Python code <pep484_type_annotations>`, + following `PEP-484 <https://www.python.org/dev/peps/pep-0484/>`_ type hints + and `PEP 526 <https://www.python.org/dev/peps/pep-0526/>`_ variable annotations. + To make good use of the latter, including C data types, etc., you need + the special ``cython`` module, which you can import with - import cython + .. code-block:: python -in the Python module that you want to compile. + import cython + + in the Python module that you want to compile. |