mirror of
https://github.com/boostorg/python.git
synced 2026-07-22 13:43:45 +00:00
9a140643c8
[SVN r13471]
974 lines
35 KiB
HTML
974 lines
35 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<title>
|
|
Special Method and Operator Support
|
|
</title>
|
|
<div>
|
|
<h1>
|
|
<img width="277" height="86" id="_x0000_i1025" align="middle" src=
|
|
"../../../c++boost.gif" alt="c++boost.gif (8819 bytes)">Special Method and
|
|
Operator Support
|
|
</h1>
|
|
<h2>
|
|
Overview
|
|
</h2>
|
|
<p>
|
|
Boost.Python supports all of the standard <a href=
|
|
"http://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.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, <code> __init__</code> is defined by
|
|
|
|
<pre> my_class.def(boost::python::constructor<...>())</pre>
|
|
|
|
(see section <a href="example1.html">"A Simple Example Using Boost.Python"</a>).<p>
|
|
<dt>
|
|
<b><tt class='method'>__del__</tt></b>(<i>self</i>)
|
|
<dd>
|
|
Called when the extension instance is about to be destroyed. For extension classes
|
|
not subclassed in Python, <code> __del__</code> is always defined automatically by
|
|
means of the class' destructor.
|
|
<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'>__lt__</tt></b>(<i>self, other</i>)
|
|
<dt>
|
|
<b><tt class='method'>__le__</tt></b>(<i>self, other</i>)
|
|
<dt>
|
|
<b><tt class='method'>__eq__</tt></b>(<i>self, other</i>)
|
|
<dt>
|
|
<b><tt class='method'>__ne__</tt></b>(<i>self, other</i>)
|
|
<dt>
|
|
<b><tt class='method'>__gt__</tt></b>(<i>self, other</i>)
|
|
<dt>
|
|
<b><tt class='method'>__ge__</tt></b>(<i>self, other</i>)
|
|
<dd>
|
|
Rich Comparison methods.
|
|
New in Python 2.1.
|
|
See <a href="richcmp.html">Rich Comparisons</a>.
|
|
<dt>
|
|
<b><tt class='method'>__cmp__</tt></b>(<i>self, other</i>)
|
|
<dd>
|
|
Three-way compare function.
|
|
See <a href="richcmp.html">Rich Comparisons</a>.
|
|
<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 Boost.Python 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.python.org/doc/current/ref/numeric-types.html">numeric
|
|
protocols</a>. This is the same basic technique used to expose
|
|
<code>to_string()</code> as <code>__str__()</code> above, and is <a
|
|
href="#numeric_manual">covered in detail below</a>. Boost.Python 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. That is, in C++ we can write:
|
|
<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>
|
|
Boost.Python 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).
|
|
</pre></blockquote>
|
|
|
|
<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 called <code>mod()</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>
|
|
For automatic wrapping of the modulo function, <code>operator%()</code> would be needed.
|
|
Therefore, the <code>mod()</code>-functions must be wrapped manually. That is, we have
|
|
to export them explicitly with the Python special name "__mod__":
|
|
|
|
<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 of <code>mod()</code> (with <code>int</code> as left operand) cannot
|
|
be wrapped directly. 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__" (standing for "reverse mod"):
|
|
|
|
<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="inplace">Inplace Operators</a></h3>
|
|
<p>
|
|
Boost.Python can also be used to expose inplace numeric operations
|
|
(i.e., <code>+=</code> and so forth). These operators must be wrapped
|
|
manually, as described in the previous section. For example, suppose
|
|
the class BigNum has an <code>operator+=</code>:
|
|
|
|
<blockquote><pre>
|
|
BigNum& operator+= (BigNum const& right);
|
|
</pre></blockquote>
|
|
|
|
This can be exposed by first writing a wrapper function:
|
|
|
|
<blockquote><pre>
|
|
BigNum& iadd (BigNum& self, const BigNum& right)
|
|
{
|
|
return self += right;
|
|
}
|
|
</pre></blockquote>
|
|
|
|
and then exposing the wrapper with
|
|
|
|
<blockquote><pre>
|
|
bignum_class.def(&iadd, "__iadd__");
|
|
</pre></blockquote>
|
|
|
|
|
|
|
|
|
|
<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 types to a
|
|
common type before invoking the actual operator. Implementing good
|
|
coercion functions can be difficult if many type combinations must be
|
|
supported.
|
|
<p>
|
|
Boost.Python 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 Boost.Python automatically
|
|
ensures that the correct function is called in each case; there is no
|
|
need for user-defined coercion functions. To enable operator
|
|
overloading, Boost.Python 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 still define the "__coerce__" operator manually. 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>
|
|
// this must be called before any use of automatic operator
|
|
// wrapping or a call to some_class.def_standard_coerce()
|
|
some_class.def(&custom_coerce, "__coerce__");
|
|
</pre></blockquote>
|
|
|
|
Note that the standard coercion (defined by use of automatic
|
|
operator wrapping on a <code>class_builder</code> or a call to
|
|
<code>class_builder::def_standard_coerce()</code>) will never be applied if
|
|
a custom coercion function has been registered. Therefore, in
|
|
your coercion function you should call
|
|
|
|
<blockquote><pre>
|
|
boost::python::standard_coerce(left, right);
|
|
</pre></blockquote>
|
|
|
|
for all cases that you don't want to handle yourself.
|
|
|
|
<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& modulus);
|
|
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's the third argument. These functions
|
|
must be presented to Boost.Python 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, modulus);
|
|
}
|
|
|
|
BigNum rrpower(BigNum const& modulus, int first, int second)
|
|
{
|
|
return power(first, second, modulus);
|
|
}
|
|
</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>
|
|
Boost.Python can automatically wrap the following <a href=
|
|
"http://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>
|
|
<br>See <a href="richcmp.html">Rich Comparisons</a>.
|
|
<td>
|
|
<code>op_cmp</code>
|
|
<td>
|
|
<code>cpp_left < cpp_right�</code>
|
|
<br><code>cpp_right < cpp_left</code>
|
|
<tr>
|
|
<td>
|
|
<code>__lt__</code>
|
|
<br><code>__le__</code>
|
|
<br><code>__eq__</code>
|
|
<br><code>__ne__</code>
|
|
<br><code>__gt__</code>
|
|
<br><code>__ge__</code>
|
|
<td>
|
|
<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>
|
|
<br>See <a href="richcmp.html">Rich Comparisons</a>
|
|
<td>
|
|
<code>op_lt</code>
|
|
<br><code>op_le</code>
|
|
<br><code>op_eq</code>
|
|
<br><code>op_ne</code>
|
|
<br><code>op_gt</code>
|
|
<br><code>op_ge</code>
|
|
<td>
|
|
<code>cpp_left < cpp_right�</code>
|
|
<br><code>cpp_left <= cpp_right�</code>
|
|
<br><code>cpp_left == cpp_right�</code>
|
|
<br><code>cpp_left != cpp_right�</code>
|
|
<br><code>cpp_left > cpp_right�</code>
|
|
<br><code>cpp_left >= cpp_right�</code>
|
|
|
|
<tr>
|
|
<td>
|
|
<code>__neg__</code>
|
|
<td>
|
|
<code>-oper�</code> (unary negation)
|
|
<td>
|
|
<code>op_neg</code>
|
|
<td>
|
|
<code>-cpp_oper</code>
|
|
<tr>
|
|
<td>
|
|
<code>__pos__</code>
|
|
<td>
|
|
<code>+oper�</code> (identity)
|
|
<td>
|
|
<code>op_pos</code>
|
|
<td>
|
|
<code>+cpp_oper</code>
|
|
<tr>
|
|
<td>
|
|
<code>__abs__</code>
|
|
<td>
|
|
<code>abs(oper)�</code> (absolute value)
|
|
<td>
|
|
<code>op_abs</code>
|
|
<td>
|
|
<code>abs(cpp_oper)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__invert__</code>
|
|
<td>
|
|
<code>~oper�</code> (bitwise inversion)
|
|
<td>
|
|
<code>op_invert</code>
|
|
<td>
|
|
<code>~cpp_oper</code>
|
|
<tr>
|
|
<td>
|
|
<code>__int__</code>
|
|
<td>
|
|
<code>int(oper)�</code> (integer conversion)
|
|
<td>
|
|
<code>op_int</code>
|
|
<td>
|
|
<code>long(cpp_oper)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__long__</code>
|
|
<td>
|
|
<code>long(oper)�</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)�</code> (float conversion)
|
|
<td>
|
|
<code>op_float</code>
|
|
<td>
|
|
<code>double(cpp_oper)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__str__</code>
|
|
<td>
|
|
<code>str(oper)�</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:
|
|
</pre></blockquote>
|
|
|
|
while in C++ one writes
|
|
|
|
<blockquote><pre>
|
|
for (iterator i = S.begin(), end = S.end(); i != end; ++i)
|
|
</pre></blockquote>
|
|
|
|
<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.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.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));
|
|
boost::python::throw_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, Boost.Python extension classes support <a
|
|
href="http://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, Boost.Python 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(AnyBoost.PythonExtensionClass):
|
|
... 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>
|
|
Boost.Python 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>
|
|
Next: <a href="under-the-hood.html">A Peek Under the Hood</a>
|
|
Previous: <a href="inheritance.html">Inheritance</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>
|
|
|