Commit 694361d1 authored by Robert Bradshaw's avatar Robert Bradshaw

extension types and special methods

parent 729e88c1
...@@ -65,17 +65,14 @@ and the depth attribute readable but not writable. ...@@ -65,17 +65,14 @@ and the depth attribute readable but not writable.
.. note:: .. note::
You can only expose simple C types, such as ints, floats and You can only expose simple C types, such as ints, floats, and
strings, for Python access. You can also expose Python-valued attributes, strings, for Python access. You can also expose Python-valued attributes.
although read-write exposure is only possible for generic Python attributes
(of type object). If the attribute is declared to be of an extension type, it
must be exposed :keyword:`readonly`.
.. note:: .. note::
Also the :keyword:`public` and :keyword:`readonly` options apply only to Also the :keyword:`public` and :keyword:`readonly` options apply only to
Python access, not direct access. All the attributes of an extension type Python access, not direct access. All the attributes of an extension type
are always readable and writable by direct access. are always readable and writable by C-level access.
Type declarations Type declarations
=================== ===================
...@@ -114,6 +111,45 @@ The same consideration applies to local variables, for example,:: ...@@ -114,6 +111,45 @@ The same consideration applies to local variables, for example,::
sh2.height = sh1.height sh2.height = sh1.height
return sh2 return sh2
Type Testing and Casting
------------------------
Suppose I have a method :meth:`quest` which returns an object of type :class:`Shrubbery`.
To access it's width I could write::
cdef Shrubbery sh = quest()
print sh.width
which requires the use of a local variable and performs a type test on assignment.
If you *know* the return value of :meth:`quest` will be of type :class:`Shrubbery`
you can use a cast to write::
print (<Shrubbery>quest()).width
This may be dangerous if :meth:`quest()` is not actually a :class:`Shrubbery`, as it
will try to access width as a C struct member which may not exist. At the C level,
rather than raising an :class:`AttributeError`, either an nonsensical result will be
returned (interpreting whatever data is at at that address as an int) or a segfault
may result from trying to access invalid memory. Instead, one can write::
print (<Shrubbery?>quest()).width
which performs a type check (possibly raising a :class:`TypeError`) before making the
cast and allowing the code to proceed.
To explicitly test the type of an object, use the :meth:`isinstance` method. By default,
in Python, the :meth:`isinstance` method checks the :class:`__class__` attribute of the
first argument to determine if it is of the required type. However, this is potentially
unsafe as the :class:`__class__` attribute can be spoofed or changed, but the C structure
of an extension type must be correct to access its :keyword:`cdef` attributes and call its :keyword:`cdef` methods. Cython detects if the second argument is a known extension
type and does a type check instead, analogous to Pyrex's :meth:`typecheck`.
The old behavior is always available by passing a tuple as the second parameter::
print isinstance(sh, Shrubbery) # Check the type of sh
print isinstance(sh, (Shrubbery,)) # Check sh.__class__
Extension types and None Extension types and None
========================= =========================
...@@ -178,7 +214,7 @@ Special methods ...@@ -178,7 +214,7 @@ Special methods
Although the principles are similar, there are substantial differences between Although the principles are similar, there are substantial differences between
many of the :meth:`__xxx__` special methods of extension types and their Python many of the :meth:`__xxx__` special methods of extension types and their Python
counterparts. There is a separate page devoted to this subject, and you should counterparts. There is a :ref:`separate page <special-methods>` devoted to this subject, and you should
read it carefully before attempting to use any special methods in your read it carefully before attempting to use any special methods in your
extension types. extension types.
...@@ -281,7 +317,7 @@ must be compatible). ...@@ -281,7 +317,7 @@ must be compatible).
C methods C methods
========= =========
Extension types can have C methods as well as Python methods. Like C Extension types can have C methods as well as Python methods. Like C
functions, C methods are declared using :keyword:`cdef` instead of functions, C methods are declared using :keyword:`cdef` or :keyword:`cpdef` instead of
:keyword:`def`. C methods are "virtual", and may be overridden in derived :keyword:`def`. C methods are "virtual", and may be overridden in derived
extension types.:: extension types.::
...@@ -366,14 +402,14 @@ Cython module. A public extension type declaration makes an extension type ...@@ -366,14 +402,14 @@ Cython module. A public extension type declaration makes an extension type
defined in a Cython module available to external C code. defined in a Cython module available to external C code.
External extension types External extension types
^^^^^^^^^^^^^^^^^^^^^^^^ ------------------------
An extern extension type allows you to gain access to the internals of Python An extern extension type allows you to gain access to the internals of Python
objects defined in the Python core or in a non-Cython extension module. objects defined in the Python core or in a non-Cython extension module.
.. note:: .. note::
In Pyrex versions before 0.8, extern extension types were also used to In previous versions of Pyrex, extern extension types were also used to
reference extension types defined in another Pyrex module. While you can still reference extension types defined in another Pyrex module. While you can still
do that, Cython provides a better mechanism for this. See do that, Cython provides a better mechanism for this. See
:ref:`sharing-declarations`. :ref:`sharing-declarations`.
...@@ -418,8 +454,30 @@ built-in complex object.:: ...@@ -418,8 +454,30 @@ built-in complex object.::
declaration is inside a :keyword:`cdef` extern from block, you only need to declaration is inside a :keyword:`cdef` extern from block, you only need to
declare those C members which you wish to access. declare those C members which you wish to access.
Name specification clause
-------------------------
The part of the class declaration in square brackets is a special feature only
available for extern or public extension types. The full form of this clause
is::
[object object_struct_name, type type_object_name ]
where ``object_struct_name`` is the name to assume for the type's C struct,
and type_object_name is the name to assume for the type's statically declared
type object. (The object and type clauses can be written in either order.)
If the extension type declaration is inside a :keyword:`cdef` extern from
block, the object clause is required, because Cython must be able to generate
code that is compatible with the declarations in the header file. Otherwise,
for extern extension types, the object clause is optional.
For public extension types, the object and type clauses are both required,
because Cython must be able to generate code that is compatible with external C
code.
Implicit importing Implicit importing
^^^^^^^^^^^^^^^^^^ ------------------
Cython requires you to include a module name in an extern extension class Cython requires you to include a module name in an extern extension class
declaration, for example,:: declaration, for example,::
...@@ -452,7 +510,7 @@ which corresponds to the implicit import statement:: ...@@ -452,7 +510,7 @@ which corresponds to the implicit import statement::
from My.Nested.Package import Spam as Yummy from My.Nested.Package import Spam as Yummy
Type names vs. constructor names Type names vs. constructor names
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------------------------
Inside a Cython module, the name of an extension type serves two distinct Inside a Cython module, the name of an extension type serves two distinct
purposes. When used in an expression, it refers to a module-level global purposes. When used in an expression, it refers to a module-level global
...@@ -482,33 +540,12 @@ there are other ways that you could get hold of the constructor, but only ...@@ -482,33 +540,12 @@ there are other ways that you could get hold of the constructor, but only
Yummy is usable as a type name. Yummy is usable as a type name.
Public extension types Public extension types
^^^^^^^^^^^^^^^^^^^^^^ ======================
An extension type can be declared public, in which case a ``.h`` file is An extension type can be declared public, in which case a ``.h`` file is
generated containing declarations for its object struct and type object. By generated containing declarations for its object struct and type object. By
including the ``.h`` file in external C code that you write, that code can including the ``.h`` file in external C code that you write, that code can
access the attributes of the extension type. access the attributes of the extension type.
Name specification clause
^^^^^^^^^^^^^^^^^^^^^^^^^
The part of the class declaration in square brackets is a special feature only
available for extern or public extension types. The full form of this clause
is::
[object object_struct_name, type type_object_name ]
where ``object_struct_name`` is the name to assume for the type's C struct,
and type_object_name is the name to assume for the type's statically declared
type object. (The object and type clauses can be written in either order.)
If the extension type declaration is inside a :keyword:`cdef` extern from
block, the object clause is required, because Cython must be able to generate
code that is compatible with the declarations in the header file. Otherwise,
for extern extension types, the object clause is optional.
For public extension types, the object and type clauses are both required,
because Cython must be able to generate code that is compatible with external C
code.
.. _special-methods:
Special Methods of Extension Types Special Methods of Extension Types
=================================== ===================================
...@@ -14,16 +16,17 @@ mention. ...@@ -14,16 +16,17 @@ mention.
Declaration Declaration
------------ ------------
Special methods of extension types must be declared with :keyword:`def`, not Special methods of extension types must be declared with :keyword:`def`, not
:keyword:`cdef`. :keyword:`cdef`. This does not impact their performance--Python uses different
calling conventions to invoke these special methods.
Docstrings Docstrings
----------- -----------
Currently, docstrings are not fully supported in special methods of extension Currently, docstrings are not fully supported in some special methods of extension
types. You can place a docstring in the source to serve as a comment, but it types. You can place a docstring in the source to serve as a comment, but it
won't show up in the corresponding :attr:`__doc__` attribute at run time. (This is a won't show up in the corresponding :attr:`__doc__` attribute at run time. (This
Python limitation -- there's nowhere in the `PyTypeObject` data structure to put seems to be is a Python limitation -- there's nowhere in the `PyTypeObject`
such docstrings.) data structure to put such docstrings.)
Initialisation methods: :meth:`__cinit__` and :meth:`__init__` Initialisation methods: :meth:`__cinit__` and :meth:`__init__`
--------------------------------------------------------------- ---------------------------------------------------------------
...@@ -32,10 +35,9 @@ There are two methods concerned with initialising the object. ...@@ -32,10 +35,9 @@ There are two methods concerned with initialising the object.
The :meth:`__cinit__` method is where you should perform basic C-level The :meth:`__cinit__` method is where you should perform basic C-level
initialisation of the object, including allocation of any C data structures initialisation of the object, including allocation of any C data structures
that your object will own. You need to be careful what you do in the that your object will own. You need to be careful what you do in the
:meth:`__cinit__` method, because the object may not yet be a valid Python :meth:`__cinit__` method, because the object may not yet be fully valid Python
object when it is called. Therefore, you must not invoke any Python operations object when it is called. Therefore, you should be careful invoking any Python
which might touch the object; in particular, do not try to call any of its operations which might touch the object; in particular, its methods.
methods.
By the time your :meth:`__cinit__` method is called, memory has been allocated for the By the time your :meth:`__cinit__` method is called, memory has been allocated for the
object and any C attributes it has have been initialised to 0 or null. (Any object and any C attributes it has have been initialised to 0 or null. (Any
...@@ -64,11 +66,18 @@ subclassing your extension type in Python, you may find it useful to give the ...@@ -64,11 +66,18 @@ subclassing your extension type in Python, you may find it useful to give the
ignore extra arguments. Otherwise, any Python subclass which has an ignore extra arguments. Otherwise, any Python subclass which has an
:meth:`__init__` with a different signature will have to override :meth:`__init__` with a different signature will have to override
:meth:`__new__` as well as :meth:`__init__`, which the writer of a Python :meth:`__new__` as well as :meth:`__init__`, which the writer of a Python
class wouldn't expect to have to do. Finalization method: :meth:`__dealloc__` class wouldn't expect to have to do. As a convenience, if you declare
your :meth:`__cinit__`` method to take no arguments (other than self) it
will simply ignore any extra arguments passed to the constructor without
complaining about the signature mismatch.
Finalization method: :meth:`__dealloc__`
----------------------------------------
The counterpart to the :meth:`__cinit__` method is the :meth:`__dealloc__` The counterpart to the :meth:`__cinit__` method is the :meth:`__dealloc__`
method, which should perform the inverse of the :meth:`__cinit__` method. Any method, which should perform the inverse of the :meth:`__cinit__` method. Any
C data structures that you allocated in your :meth:`__cinit__` method should C data that you explicitly allocated (e.g. via malloc) in your
be freed in your :meth:`__dealloc__` method. :meth:`__cinit__` method should be freed in your :meth:`__dealloc__` method.
You need to be careful what you do in a :meth:`__dealloc__` method. By the time your You need to be careful what you do in a :meth:`__dealloc__` method. By the time your
:meth:`__dealloc__` method is called, the object may already have been partially :meth:`__dealloc__` method is called, the object may already have been partially
...@@ -80,7 +89,7 @@ deallocating C data. ...@@ -80,7 +89,7 @@ deallocating C data.
You don't need to worry about deallocating Python attributes of your object, You don't need to worry about deallocating Python attributes of your object,
because that will be done for you by Cython after your :meth:`__dealloc__` method because that will be done for you by Cython after your :meth:`__dealloc__` method
returns. returns.
.. Note: There is no :meth:`__del__` method for extension types. .. Note: There is no :meth:`__del__` method for extension types.
...@@ -94,9 +103,9 @@ operation, the same method of the second operand is called, with the operands ...@@ -94,9 +103,9 @@ operation, the same method of the second operand is called, with the operands
in the same order. in the same order.
This means that you can't rely on the first parameter of these methods being This means that you can't rely on the first parameter of these methods being
"self", and you should test the types of both operands before deciding what to "self" or being the right type, and you should test the types of both operands
do. If you can't handle the combination of types you've been given, you should before deciding what to do. If you can't handle the combination of types you've
return `NotImplemented`. been given, you should return `NotImplemented`.
This also applies to the in-place arithmetic method :meth:`__ipow__`. It doesn't apply This also applies to the in-place arithmetic method :meth:`__ipow__`. It doesn't apply
to any of the other in-place methods (:meth:`__iadd__`, etc.) which always to any of the other in-place methods (:meth:`__iadd__`, etc.) which always
...@@ -130,13 +139,7 @@ The :meth:`__next__` method ...@@ -130,13 +139,7 @@ The :meth:`__next__` method
Extension types wishing to implement the iterator interface should define a Extension types wishing to implement the iterator interface should define a
method called :meth:`__next__`, not next. The Python system will automatically method called :meth:`__next__`, not next. The Python system will automatically
supply a next method which calls your :meth:`__next__`. Do *NOT* explicitly supply a next method which calls your :meth:`__next__`. Do *NOT* explicitly
give your type a next method, or bad things could happen. give your type a :meth:`next` method, or bad things could happen.
Type Testing in Special Methods
--------------------------------
.. TODO document the Cython way using the overridden isinstance
Special Method Table Special Method Table
--------------------- ---------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment