mirror of
https://github.com/boostorg/python.git
synced 2026-01-19 16:32:16 +00:00
889 lines
32 KiB
HTML
889 lines
32 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
||
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
||
<title>
|
||
Special Method and Operator Support
|
||
</title>
|
||
<div>
|
||
<h1>
|
||
<img width="277" height="86" id="_x0000_i1025" align="center" src=
|
||
"../../../c++boost.gif" alt="c++boost.gif (8819 bytes)">Special Method and
|
||
Operator Support
|
||
</h1>
|
||
<h2>
|
||
Overview
|
||
</h2>
|
||
<p>
|
||
Py_cpp supports all of the standard <a href=
|
||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/specialnames.html">
|
||
special method names</a> supported by real Python class instances <em>
|
||
except</em> <code>__complex__</code> (more on the reasons <a href=
|
||
"#reasons">below</a>). In addition, it can quickly and easily expose
|
||
suitable C++ functions and operators as Python operators. The following
|
||
categories of special method names are supported:
|
||
<ul>
|
||
<li><a href="#general">Basic Customization</a>
|
||
<li><a href="#numeric">Numeric Operators</a>
|
||
<li><a href="#sequence_and_mapping">Sequence and Mapping protocols</a>
|
||
<li><a href="#getter_setter">Attribute Getters and Setters</a>
|
||
</ul>
|
||
|
||
<h2><a name="general">Basic Customization</a></h2>
|
||
|
||
|
||
<p>
|
||
Python provides a number of special operators for basic customization of a
|
||
class. Only a brief description is provided below; more complete
|
||
documentation can be found <a
|
||
href="http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/customization.html">here</a>.
|
||
|
||
<dl>
|
||
<dt>
|
||
<b><tt class='method'>__init__</tt></b>(<i>self</i>)
|
||
<dd>
|
||
Initialize the class instance. For extension classes not subclassed in
|
||
Python, this is provided by the
|
||
<code>boost::python::constructor<...>()</code> construct and should <i>not</i> be explicitly <code>def</code>ed.
|
||
<dt>
|
||
<b><tt class='method'>__del__</tt></b>(<i>self</i>)
|
||
<dd>
|
||
Called when the extension instance is about to be destroyed.
|
||
<dt>
|
||
<b><tt class='method'>__repr__</tt></b>(<i>self</i>)
|
||
<dd>
|
||
Create a string representation from which the object can be
|
||
reconstructed.
|
||
<dt>
|
||
<b><tt class='method'>__str__</tt></b>(<i>self</i>)
|
||
<dd>
|
||
Create a string representation which is suitable for printing.
|
||
<dt>
|
||
<b><tt class='method'>__cmp__</tt></b>(<i>self, other</i>)
|
||
<dd>
|
||
Three-way compare function, used to implement comparison operators
|
||
(< etc.) Should return a negative integer if <code> self < other
|
||
</code> , zero if <code> self == other </code> , a positive integer if
|
||
<code> self > other </code>.
|
||
<dt>
|
||
<b><tt class='method'>__hash__</tt></b>(<i>self</i>)
|
||
<dd>
|
||
Called for the key object for dictionary operations, and by the
|
||
built-in function hash(). Should return a 32-bit integer usable as a
|
||
hash value for dictionary operations (only allowed if __cmp__ is also
|
||
defined)
|
||
<dt>
|
||
<b><tt class='method'>__nonzero__</tt></b>(<i>self</i>)
|
||
<dd>
|
||
called if the object is used as a truth value (e.g. in an if
|
||
statement)
|
||
<dt>
|
||
<b><tt class='method'>__call__</tt></b> (<var>self</var><big>[</big><var>, args...</var><big>]</big>)
|
||
<dd>
|
||
Called when the instance is “called” as a function; if this method
|
||
is defined, <code><var>x</var>(arg1, arg2, ...)</code> is a shorthand for
|
||
<code><var>x</var>.__call__(arg1, arg2, ...)</code>.
|
||
</dl>
|
||
|
||
If we have a suitable C++ function that supports any of these features,
|
||
we can export it like any other function, using its Python special name.
|
||
For example, suppose that class <code>Foo</code> provides a string
|
||
conversion function:
|
||
<blockquote><pre>
|
||
std::string to_string(Foo const& f)
|
||
{
|
||
std::ostringstream s;
|
||
s << f;
|
||
return s.str();
|
||
}
|
||
</pre></blockquote>
|
||
This function would be wrapped like this:
|
||
<blockquote><pre>
|
||
boost::python::class_builder<Foo> foo_class(my_module, "Foo");
|
||
foo_class.def(&to_string, "__str__");
|
||
</pre></blockquote>
|
||
Note that py_cpp also supports <em>automatic wrapping</em> of
|
||
<code>__str__</code> and <code>__cmp__</code>. This is explained in the <a
|
||
href="#numeric">next section</a> and the <a href="#numeric_table">Table of
|
||
Automatically Wrapped Methods</a>.
|
||
|
||
<h2><a name="numeric">Numeric Operators</a></h2>
|
||
|
||
<p>
|
||
Numeric operators can be exposed manually, by <code>def</code>ing C++
|
||
[member] functions that support the standard Python <a
|
||
href="http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">numeric
|
||
protocols</a>. This is the basic same technique used to expose
|
||
<code>to_string()</code> as <code>__str__()</code> above, and is <a
|
||
href="#numeric_manual">covered in detail below</a>. Py_cpp also supports
|
||
<i>automatic wrapping</i> of numeric operators whenever they have already
|
||
been defined in C++.
|
||
|
||
<h3><a name="numeric_auto">Exposing C++ Operators Automatically</a></h3>
|
||
|
||
<p>
|
||
Supose we wanted to expose a C++ class
|
||
<code>BigNum</code> which supports addition, so that we can write (in C++):
|
||
<blockquote><pre>
|
||
BigNum a, b, c;
|
||
...
|
||
c = a + b;
|
||
</pre></blockquote>
|
||
<p>
|
||
To enable the same functionality in Python, we first wrap the <code>
|
||
BigNum</code> class as usual:
|
||
<blockquote><pre>
|
||
boost::python::class_builder<BigNum> bignum_class(my_module, "BigNum");
|
||
bignum_class.def(boost::python::constructor<>());
|
||
...
|
||
</pre></blockquote>
|
||
Then we export the addition operator like this:
|
||
|
||
<blockquote><pre>
|
||
bignum_class.def(boost::python::operators<boost::python::op_add>());
|
||
</pre></blockquote>
|
||
|
||
Since BigNum also supports subtraction, multiplication, and division, we
|
||
want to export those also. This can be done in a single command by
|
||
“or”ing the operator identifiers together (a complete list of these
|
||
identifiers and the corresponding operators can be found in the <a href=
|
||
"#numeric_table">Table of Automatically Wrapped Methods</a>):
|
||
<blockquote><pre>
|
||
bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>());
|
||
</pre></blockquote>
|
||
[Note that the or-expression must be enclosed in parentheses.]
|
||
|
||
<p>This form of operator definition can be used to wrap unary and
|
||
homogeneous binary operators (a <i>homogeneous</i> operator has left and
|
||
right operands of the same type). Now suppose that our C++ library also
|
||
supports addition of BigNums and plain integers:
|
||
|
||
<blockquote><pre>
|
||
BigNum a, b;
|
||
int i;
|
||
...
|
||
a = b + i;
|
||
a = i + b;
|
||
</pre></blockquote>
|
||
To wrap these heterogeneous operators, we need to specify a different type for
|
||
one of the operands. This is done using the <code>right_operand</code>
|
||
and <code>left_operand</code> templates:
|
||
<blockquote><pre>
|
||
bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::right_operand<int>());
|
||
bignum_class.def(boost::python::operators<boost::python::op_add>(), boost::python::left_operand<int>());
|
||
</pre></blockquote>
|
||
Py_cpp uses overloading to register several variants of the same
|
||
operation (more on this in the context of <a href="#coercion">
|
||
coercion</a>). Again, several operators can be exported at once:
|
||
<blockquote><pre>
|
||
bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>(),
|
||
boost::python::right_operand<int>());
|
||
bignum_class.def(boost::python::operators<(boost::python::op_sub | boost::python::op_mul | boost::python::op_div)>(),
|
||
boost::python::left_operand<int>());
|
||
</pre></blockquote>
|
||
The type of the operand not mentioned is taken from the class being wrapped. In
|
||
our example, the class object is <code>bignum_class</code>, and thus the
|
||
other operand's type is “<code>BigNum const&</code>”. You can override
|
||
this default by explicitly specifying a type in the <code>
|
||
operators</code> template:
|
||
<blockquote><pre>
|
||
bignum_class.def(boost::python::operators<boost::python::op_add, BigNum>(), boost::python::right_operand<int>());
|
||
</pre></blockquote>
|
||
<p>
|
||
Note that automatic wrapping uses the <em>expression</em>
|
||
“<code>left + right</code>” and can be used uniformly
|
||
regardless of whether the C++ operators are supplied as free functions
|
||
<blockquote><pre>
|
||
BigNum operator+(BigNum, BigNum)
|
||
</pre></blockquote>
|
||
or as member
|
||
functions <blockquote><pre>
|
||
BigNum::operator+(BigNum).
|
||
</blockquote></pre>
|
||
|
||
<p>
|
||
For the Python built-in functions <code>pow()</code> and
|
||
<code>abs()</code>, there is no corresponding C++ operator. Instead,
|
||
automatic wrapping attempts to wrap C++ functions of the same name. This
|
||
only works if those functions are known in namespace
|
||
<code>python</code>. On some compilers (e.g. MSVC) it might be
|
||
necessary to add a using declaration prior to wrapping:
|
||
|
||
<blockquote><pre>
|
||
namespace boost { namespace python {
|
||
using my_namespace::pow;
|
||
using my_namespace::abs;
|
||
}
|
||
</pre></blockquote>
|
||
|
||
<h3><a name="numeric_manual">Wrapping Numeric Operators Manually</a></h3>
|
||
<p>
|
||
In some cases, automatic wrapping of operators may be impossible or
|
||
undesirable. Suppose, for example, that the modulo operation for BigNums
|
||
is defined by a set of functions <code>mod()</code> (for automatic
|
||
wrapping, we would need <code>operator%()</code>):
|
||
|
||
<blockquote><pre>
|
||
BigNum mod(BigNum const& left, BigNum const& right);
|
||
BigNum mod(BigNum const& left, int right);
|
||
BigNum mod(int left, BigNum const& right);
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
In order to create the Python operator "__mod__" from these functions, we
|
||
have to wrap them manually:
|
||
|
||
<blockquote><pre>
|
||
bignum_class.def((BigNum (*)(BigNum const&, BigNum const&))&mod, "__mod__");
|
||
bignum_class.def((BigNum (*)(BigNum const&, int))&mod, "__mod__");
|
||
</pre></blockquote>
|
||
|
||
<p>
|
||
The third form (with <code>int</code> as left operand) cannot be wrapped
|
||
this way. We must first create a function <code>rmod()</code> with the
|
||
operands reversed:
|
||
|
||
<blockquote><pre>
|
||
BigNum rmod(BigNum const& right, int left)
|
||
{
|
||
return mod(left, right);
|
||
}
|
||
</pre></blockquote>
|
||
|
||
This function must be wrapped under the name "__rmod__":
|
||
|
||
<blockquote><pre>
|
||
bignum_class.def(&rmod, "__rmod__");
|
||
</pre></blockquote>
|
||
|
||
Many of the possible operator names can be found in the <a href=
|
||
"#numeric_table">Table of Automatically Wrapped Methods</a>. Special treatment is
|
||
necessary to export the <a href="#ternary_pow">ternary pow</a> operator.
|
||
|
||
<p>
|
||
Automatic and manual wrapping can be mixed arbitrarily. Note that you
|
||
cannot overload the same operator for a given extension class on both
|
||
“<code>int</code>” and “<code>float</code>”, because Python implicitly
|
||
converts these types into each other. Thus, the overloaded variant
|
||
found first (be it “<code>int</code>“ or “<code>float</code>”) will be
|
||
used for either of the two types.
|
||
|
||
<h3><a name="coercion">Coercion</a></h3>
|
||
|
||
|
||
Plain Python can only execute operators with identical types on the left
|
||
and right hand side. If it encounters an expression where the types of
|
||
the left and right operand differ, it tries to coerce these type to a
|
||
common type before invoking the actual operator. Implementing good
|
||
coercion functions can be difficult if many type combinations must be
|
||
supported.
|
||
<p>
|
||
Py_cpp solves this problem the same way that C++ does: with <em><a
|
||
href="overloading.html">overloading</a></em>. This technique drastically
|
||
simplifies the code neccessary to support operators: you just register
|
||
operators for all desired type combinations, and py_cpp automatically
|
||
ensures that the correct function is called in each case; there is no
|
||
need for user-defined coercion functions. To enable operator
|
||
overloading, py_cpp provides a standard coercion which is <em>implicitly
|
||
registered</em> whenever automatic operator wrapping is used.
|
||
<p>
|
||
If you wrap all operator functions manually, but still want to use
|
||
operator overloading, you have to register the standard coercion
|
||
function explicitly:
|
||
|
||
<blockquote><pre>
|
||
// this is not necessary if automatic operator wrapping is used
|
||
bignum_class.def_standard_coerce();
|
||
</pre></blockquote>
|
||
|
||
If you encounter a situation where you absolutely need a customized
|
||
coercion, you can overload the "__coerce__" operator itself. The signature
|
||
of a coercion function should look like one of the following (the first is
|
||
the safest):
|
||
|
||
<blockquote><pre>
|
||
boost::python::tuple custom_coerce(boost::python::reference left, boost::python::reference right);
|
||
boost::python::tuple custom_coerce(PyObject* left, PyObject* right);
|
||
PyObject* custom_coerce(PyObject* left, PyObject* right);
|
||
</pre></blockquote>
|
||
|
||
The resulting <code>tuple</code> must contain two elements which
|
||
represent the values of <code>left</code> and <code>right</code>
|
||
converted to the same type. Such a function is wrapped as usual:
|
||
|
||
<blockquote><pre>
|
||
some_class.def(&custom_coerce, "__coerce__");
|
||
</pre></blockquote>
|
||
|
||
Note that the later use of automatic operator wrapping on a
|
||
<code>class_builder</code> or a call to
|
||
“<code>some_class.def_standard_coerce()</code>” will cause any
|
||
custom coercion function to be replaced by the standard one.
|
||
|
||
<h3><a name="ternary_pow">The Ternary <code>pow()</code> Operator</a></h3>
|
||
|
||
<p>
|
||
In addition to the usual binary <code>pow(x, y)</code> operator (meaning
|
||
<i>x<sup>y</sup></i>), Python also provides a ternary variant that implements
|
||
<i>x<sup>y</sup> <b>mod</b> z</i>, presumably using a more efficient algorithm than
|
||
concatenation of power and modulo operators. Automatic operator wrapping
|
||
can only be used with the binary variant. Ternary <code>pow()</code> must
|
||
always be wrapped manually. For a homgeneous ternary <code>pow()</code>,
|
||
this is done as usual:
|
||
|
||
<blockquote><pre>
|
||
BigNum power(BigNum const& first, BigNum const& second, BigNum const& module);
|
||
typedef BigNum (ternary_function1)(const BigNum&, const BigNum&, const BigNum&);
|
||
...
|
||
bignum_class.def((ternary_function1)&power, "__pow__");
|
||
</pre></blockquote>
|
||
|
||
If you want to support this function with non-uniform argument
|
||
types, wrapping is a little more involved. Suppose you have to wrap:
|
||
|
||
<blockquote><pre>
|
||
BigNum power(BigNum const& first, int second, int modulus);
|
||
BigNum power(int first, BigNum const& second, int modulus);
|
||
BigNum power(int first, int second, BigNum const& modulus);
|
||
</pre></blockquote>
|
||
|
||
The first variant can be wrapped as usual:
|
||
|
||
<blockquote><pre>
|
||
typedef BigNum (ternary_function2)(const BigNum&, int, int);
|
||
bignum_class.def((ternary_function2)&power, "__pow__");
|
||
</pre></blockquote>
|
||
|
||
In the second variant, however, <code>BigNum</code> appears only as second
|
||
argument, and in the last one it is the third argument. These functions
|
||
must be presented to py_cpp such that that the <code>BigNum</code>
|
||
argument appears in first position:
|
||
|
||
<blockquote><pre>
|
||
BigNum rpower(BigNum const& second, int first, int modulus)
|
||
{
|
||
return power(first, second, third);
|
||
}
|
||
|
||
BigNum rrpower(BigNum const& third, int first, int second)
|
||
{
|
||
return power(first, second, third);
|
||
}
|
||
</pre></blockquote>
|
||
|
||
<p>These functions must be wrapped under the names "__rpow__" and "__rrpow__"
|
||
respectively:
|
||
|
||
<blockquote><pre>
|
||
bignum_class.def((ternary_function2)&rpower, "__rpow__");
|
||
bignum_class.def((ternary_function2)&rrpower, "__rrpow__");
|
||
</pre></blockquote>
|
||
|
||
Note that "__rrpow__" is an extension not present in plain Python.
|
||
|
||
<h2><a name="numeric_table">Table of Automatically Wrapped Methods</a></h2>
|
||
<p>
|
||
Py_cpp can automatically wrap the following <a href=
|
||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/specialnames.html">
|
||
special methods</a>:
|
||
|
||
<p>
|
||
<table summary="special numeric methods" cellpadding="5" border="1"
|
||
width="100%">
|
||
<tr>
|
||
<td align="center">
|
||
<b>Python Operator Name</b>
|
||
<td align="center">
|
||
<b>Python Expression</b>
|
||
<td align="center">
|
||
<b>C++ Operator Id</b>
|
||
<td align="center">
|
||
<b>C++ Expression Used For Automatic Wrapping</b><br>
|
||
with <code>cpp_left = from_python(left,
|
||
type<Left>())</code>,<br>
|
||
<code>cpp_right = from_python(right,
|
||
type<Right>())</code>,<br>
|
||
and <code>cpp_oper = from_python(oper, type<Oper>())</code>
|
||
<tr>
|
||
<td>
|
||
<code>__add__, __radd__</code>
|
||
<td>
|
||
<code>left + right</code>
|
||
<td>
|
||
<code>op_add</code>
|
||
<td>
|
||
<code>cpp_left + cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__sub__, __rsub__</code>
|
||
<td>
|
||
<code>left - right</code>
|
||
<td>
|
||
<code>op_sub</code>
|
||
<td>
|
||
<code>cpp_left - cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__mul__, __rmul__</code>
|
||
<td>
|
||
<code>left * right</code>
|
||
<td>
|
||
<code>op_mul</code>
|
||
<td>
|
||
<code>cpp_left * cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__div__, __rdiv__</code>
|
||
<td>
|
||
<code>left / right</code>
|
||
<td>
|
||
<code>op_div</code>
|
||
<td>
|
||
<code>cpp_left / cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__mod__, __rmod__</code>
|
||
<td>
|
||
<code>left % right</code>
|
||
<td>
|
||
<code>op_mod</code>
|
||
<td>
|
||
<code>cpp_left % cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__divmod__, __rdivmod__</code>
|
||
<td>
|
||
<code>(quotient, remainder)<br>
|
||
= divmod(left, right)</code>
|
||
<td>
|
||
<code>op_divmod</code>
|
||
<td>
|
||
<code>cpp_left / cpp_right</code>
|
||
<br><code>cpp_left % cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__pow__, __rpow__</code>
|
||
<td>
|
||
<code>pow(left, right)</code><br>
|
||
(binary power)
|
||
<td>
|
||
<code>op_pow</code>
|
||
<td>
|
||
<code>pow(cpp_left, cpp_right)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__rrpow__</code>
|
||
<td>
|
||
<code>pow(left, right, modulo)</code><br>
|
||
(ternary power modulo)
|
||
<td colspan="2">
|
||
no automatic wrapping, <a href="#ternary_pow">special treatment</a>
|
||
required
|
||
<tr>
|
||
<td>
|
||
<code>__lshift__, __rlshift__</code>
|
||
<td>
|
||
<code>left << right</code>
|
||
<td>
|
||
<code>op_lshift</code>
|
||
<td>
|
||
<code>cpp_left << cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__rshift__, __rrshift__</code>
|
||
<td>
|
||
<code>left >> right</code>
|
||
<td>
|
||
<code>op_rshift</code>
|
||
<td>
|
||
<code>cpp_left >> cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__and__, __rand__</code>
|
||
<td>
|
||
<code>left & right</code>
|
||
<td>
|
||
<code>op_and</code>
|
||
<td>
|
||
<code>cpp_left & cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__xor__, __rxor__</code>
|
||
<td>
|
||
<code>left ^ right</code>
|
||
<td>
|
||
<code>op_xor</code>
|
||
<td>
|
||
<code>cpp_left ^ cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__or__, __ror__</code>
|
||
<td>
|
||
<code>left | right</code>
|
||
<td>
|
||
<code>op_or</code>
|
||
<td>
|
||
<code>cpp_left | cpp_right</code>
|
||
|
||
<tr>
|
||
<td>
|
||
<code>__cmp__, __rcmp__</code>
|
||
<td>
|
||
<code>cmp(left, right)</code><br>
|
||
<code>left < right</code><br>
|
||
<code>left <= right</code><br>
|
||
<code>left > right</code><br>
|
||
<code>left >= right</code><br>
|
||
<code>left == right</code><br>
|
||
<code>left != right</code>
|
||
<td>
|
||
<code>op_cmp</code>
|
||
<td>
|
||
<code>cpp_left < cpp_right<68></code>
|
||
<br><code>cpp_right < cpp_left</code>
|
||
|
||
<tr>
|
||
<td>
|
||
<code>__neg__</code>
|
||
<td>
|
||
<code>-oper<65></code> (unary negation)
|
||
<td>
|
||
<code>op_neg</code>
|
||
<td>
|
||
<code>-cpp_oper</code>
|
||
<tr>
|
||
<td>
|
||
<code>__pos__</code>
|
||
<td>
|
||
<code>+oper<65></code> (identity)
|
||
<td>
|
||
<code>op_pos</code>
|
||
<td>
|
||
<code>+cpp_oper</code>
|
||
<tr>
|
||
<td>
|
||
<code>__abs__</code>
|
||
<td>
|
||
<code>abs(oper)<29></code> (absolute value)
|
||
<td>
|
||
<code>op_abs</code>
|
||
<td>
|
||
<code>abs(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__invert__</code>
|
||
<td>
|
||
<code>~oper<65></code> (bitwise inversion)
|
||
<td>
|
||
<code>op_invert</code>
|
||
<td>
|
||
<code>~cpp_oper</code>
|
||
<tr>
|
||
<td>
|
||
<code>__int__</code>
|
||
<td>
|
||
<code>int(oper)<29></code> (integer conversion)
|
||
<td>
|
||
<code>op_int</code>
|
||
<td>
|
||
<code>long(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__long__</code>
|
||
<td>
|
||
<code>long(oper)<29></code><br>
|
||
(infinite precision integer conversion)
|
||
<td>
|
||
<code>op_long</code>
|
||
<td>
|
||
<code>PyLong_FromLong(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__float__</code>
|
||
<td>
|
||
<code>float(oper)<29></code> (float conversion)
|
||
<td>
|
||
<code>op_float</code>
|
||
<td>
|
||
<code>double(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__str__</code>
|
||
<td>
|
||
<code>str(oper)<29></code> (string conversion)
|
||
<td>
|
||
<code>op_str</code>
|
||
<td>
|
||
<code>std::ostringstream s; s << oper;</code>
|
||
<tr>
|
||
<td>
|
||
<code>__coerce__</code>
|
||
<td>
|
||
<code>coerce(left, right)</code>
|
||
<td colspan="2">
|
||
usually defined automatically, otherwise <a href="#coercion">
|
||
special treatment</a> required
|
||
</table>
|
||
|
||
<h2><a name="sequence_and_mapping">Sequence and Mapping Operators</a></h2>
|
||
|
||
<p>
|
||
Sequence and mapping operators let wrapped objects behave in accordance
|
||
to Python's iteration and access protocols. These protocols differ
|
||
considerably from the ones found in C++. For example, Python's typical
|
||
iteration idiom looks like
|
||
<blockquote><pre>
|
||
for i in S:
|
||
</blockquote></pre>
|
||
|
||
while in C++ one writes
|
||
|
||
<blockquote><pre>
|
||
for (iterator i = S.begin(), end = S.end(); i != end)
|
||
</blockquote></pre>
|
||
|
||
<p>One could try to wrap C++ iterators in order to carry the C++ idiom into
|
||
Python. However, this does not work very well because
|
||
|
||
<ol>
|
||
<li>It leads to
|
||
non-uniform Python code (wrapped sequences support a usage different from
|
||
Python built-in sequences) and
|
||
|
||
<li>Iterators (e.g. <code>std::vector::iterator</code>) are often implemented as plain C++
|
||
pointers which are <a href="pointers.html#problem">problematic</a> for any automatic
|
||
wrapping system.
|
||
</ol>
|
||
|
||
<p>
|
||
It is a better idea to support the standard <a
|
||
href="http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/sequence-types.html">Python
|
||
sequence and mapping protocols</a> for your wrapped containers. These
|
||
operators have to be wrapped manually because there are no corresponding
|
||
C++ operators that could be used for automatic wrapping. The Python
|
||
documentation lists the relevant <a href=
|
||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/sequence-types.html">
|
||
container operators</a>. In particular, expose __getitem__, __setitem__
|
||
and remember to raise the appropriate Python exceptions
|
||
(<code>PyExc_IndexError</code> for sequences,
|
||
<code>PyExc_KeyError</code> for mappings) when the requested item is not
|
||
present.
|
||
|
||
<p>
|
||
In the following example, we expose <code>std::map<std::size_t,std::string></code>:
|
||
<blockquote>
|
||
<pre>
|
||
typedef std::map<std::size_t, std::string> StringMap;
|
||
|
||
// A helper function for dealing with errors. Throw a Python exception
|
||
// if p == m.end().
|
||
void throw_key_error_if_end(
|
||
const StringMap& m,
|
||
StringMap::const_iterator p,
|
||
std::size_t key)
|
||
{
|
||
if (p == m.end())
|
||
{
|
||
PyErr_SetObject(PyExc_KeyError, boost::python::converters::to_python(key));
|
||
throw boost::python::error_already_set();
|
||
}
|
||
}
|
||
|
||
// Define some simple wrapper functions which match the Python protocol
|
||
// for __getitem__, __setitem__, and __delitem__. Just as in Python, a
|
||
// free function with a “self” first parameter makes a fine class method.
|
||
|
||
const std::string& get_item(const StringMap& self, std::size_t key)
|
||
{
|
||
const StringMap::const_iterator p = self.find(key);
|
||
throw_key_error_if_end(self, p, key);
|
||
return p->second;
|
||
}
|
||
|
||
// Sets the item corresponding to key in the map.
|
||
void StringMapPythonClass::set_item(StringMap& self, std::size_t key, const std::string& value)
|
||
{
|
||
self[key] = value;
|
||
}
|
||
|
||
// Deletes the item corresponding to key from the map.
|
||
void StringMapPythonClass::del_item(StringMap& self, std::size_t key)
|
||
{
|
||
const StringMap::iterator p = self.find(key);
|
||
throw_key_error_if_end(self, p, key);
|
||
self.erase(p);
|
||
}
|
||
|
||
class_builder<StringMap> string_map(my_module, "StringMap");
|
||
string_map.def(boost::python::constructor<>());
|
||
string_map.def(&StringMap::size, "__len__");
|
||
string_map.def(get_item, "__getitem__");
|
||
string_map.def(set_item, "__setitem__");
|
||
string_map.def(del_item, "__delitem__");
|
||
</pre>
|
||
</blockquote>
|
||
<p>
|
||
Then in Python:
|
||
<blockquote>
|
||
<pre>
|
||
>>> m = StringMap()
|
||
>>> m[1]
|
||
Traceback (innermost last):
|
||
File "<stdin>", line 1, in ?
|
||
KeyError: 1
|
||
>>> m[1] = 'hello'
|
||
>>> m[1]
|
||
'hello'
|
||
>>> del m[1]
|
||
>>> m[1] # prove that it's gone
|
||
Traceback (innermost last):
|
||
File "<stdin>", line 1, in ?
|
||
KeyError: 1
|
||
>>> del m[2]
|
||
Traceback (innermost last):
|
||
File "<stdin>", line 1, in ?
|
||
KeyError: 2
|
||
>>> len(m)
|
||
0
|
||
>>> m[0] = 'zero'
|
||
>>> m[1] = 'one'
|
||
>>> m[2] = 'two'
|
||
>>> m[3] = 'three'
|
||
>>> len(m)
|
||
4
|
||
</pre>
|
||
</blockquote>
|
||
|
||
<h2><a name="getter_setter">Customized Attribute Access</a></h2>
|
||
|
||
<p>
|
||
Just like built-in Python classes, py_cpp extension classes support <a
|
||
href="http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/attribute-access.html">special
|
||
the usual attribute access methods</a> <code>__getattr__</code>,
|
||
<code>__setattr__</code>, and <code>__delattr__</code>.
|
||
Because writing these functions can
|
||
be tedious in the common case where the attributes being accessed are
|
||
known statically, py_cpp checks the special names
|
||
|
||
<ul>
|
||
<li>
|
||
<code>__getattr__<em><name></em>__</code>
|
||
<li>
|
||
<code>__setattr__<em><name></em>__</code>
|
||
<li>
|
||
<code>__delattr__<em><name></em>__</code>
|
||
</ul>
|
||
|
||
to provide functional access to the attribute <em><name></em>. This
|
||
facility can be used from C++ or entirely from Python. For example, the
|
||
following shows how we can implement a “computed attribute” in Python:
|
||
<blockquote>
|
||
<pre>
|
||
>>> class Range(AnyPy_cppExtensionClass):
|
||
... def __init__(self, start, end):
|
||
... self.start = start
|
||
... self.end = end
|
||
... def __getattr__length__(self):
|
||
... return self.end - self.start
|
||
...
|
||
>>> x = Range(3, 9)
|
||
>>> x.length
|
||
6
|
||
</pre>
|
||
</blockquote>
|
||
<h4>
|
||
Direct Access to Data Members
|
||
</h4>
|
||
<p>
|
||
Py_cpp uses the special <code>
|
||
__xxxattr__<em><name></em>__</code> functionality described above
|
||
to allow direct access to data members through the following special
|
||
functions on <code>class_builder<></code> and <code>
|
||
extension_class<></code>:
|
||
<ul>
|
||
<li>
|
||
<code>def_getter(<em>pointer-to-member</em>, <em>name</em>)</code> //
|
||
read access to the member via attribute <em>name</em>
|
||
<li>
|
||
<code>def_setter(<em>pointer-to-member</em>, <em>name</em>)</code> //
|
||
write access to the member via attribute <em>name</em>
|
||
<li>
|
||
<code>def_readonly(<em>pointer-to-member</em>, <em>name</em>)</code>
|
||
// read-only access to the member via attribute <em>name</em>
|
||
<li>
|
||
<code>def_read_write(<em>pointer-to-member</em>, <em>
|
||
name</em>)</code> // read/write access to the member via attribute
|
||
<em>name</em>
|
||
</ul>
|
||
<p>
|
||
Note that the first two functions, used alone, may produce surprising
|
||
behavior. For example, when <code>def_getter()</code> is used, the
|
||
default functionality for <code>setattr()</code> and <code>
|
||
delattr()</code> remains in effect, operating on items in the extension
|
||
instance's name-space (i.e., its <code>__dict__</code>). For that
|
||
reason, you'll usually want to stick with <code>def_readonly</code> and
|
||
<code>def_read_write</code>.
|
||
<p>
|
||
For example, to expose a <code>std::pair<int,long></code> we
|
||
might write:
|
||
<blockquote>
|
||
<pre>
|
||
typedef std::pair<int,long> Pil;
|
||
int first(const Pil& x) { return x.first; }
|
||
long second(const Pil& x) { return x.second; }
|
||
...
|
||
my_module.def(first, "first");
|
||
my_module.def(second, "second");
|
||
|
||
class_builder<Pil> pair_int_long(my_module, "Pair");
|
||
pair_int_long.def(boost::python::constructor<>());
|
||
pair_int_long.def(boost::python::constructor<int,long>());
|
||
pair_int_long.def_read_write(&Pil::first, "first");
|
||
pair_int_long.def_read_write(&Pil::second, "second");
|
||
</pre>
|
||
</blockquote>
|
||
<p>
|
||
Now your Python class has attributes <code>first</code> and <code>
|
||
second</code> which, when accessed, actually modify or reflect the
|
||
values of corresponding data members of the underlying C++ object. Now
|
||
in Python:
|
||
<blockquote>
|
||
<pre>
|
||
>>> x = Pair(3,5)
|
||
>>> x.first
|
||
3
|
||
>>> x.second
|
||
5
|
||
>>> x.second = 8
|
||
>>> x.second
|
||
8
|
||
>>> second(x) # Prove that we're not just changing the instance __dict__
|
||
8
|
||
</pre>
|
||
</blockquote>
|
||
<h2>
|
||
<a name="reasons">And what about <code>__complex__</code>?</a>
|
||
</h2>
|
||
<p>
|
||
That, dear reader, is one problem we don't know how to solve. The
|
||
Python source contains the following fragment, indicating the
|
||
special-case code really is hardwired:
|
||
<blockquote>
|
||
<pre>
|
||
/* XXX Hack to support classes with __complex__ method */
|
||
if (PyInstance_Check(r)) { ...
|
||
</pre>
|
||
</blockquote>
|
||
<p>
|
||
Previous: <a href="inheritance.html">Inheritance</a> Next: <a href=
|
||
"under-the-hood.html">A Peek Under the Hood</a> Up: <a href=
|
||
"index.html">Top</a>
|
||
<p>
|
||
© Copyright David Abrahams and Ullrich Köthe 2000.
|
||
Permission to copy, use, modify, sell and distribute this document is
|
||
granted provided this copyright notice appears in all copies. This
|
||
document is provided “as is” without express or implied
|
||
warranty, and with no claim as to its suitability for any purpose.
|
||
<p>
|
||
Updated: Nov 26, 2000
|
||
</div>
|
||
|