mirror of
https://github.com/boostorg/python.git
synced 2026-01-21 17:12:22 +00:00
817 lines
30 KiB
HTML
817 lines
30 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 is able to wrap suitable C++ functions and C++ operators into
|
||
Python operators. It 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>). Supported operators include <a href="#general">
|
||
general</a>, <a href="#numeric">numeric</a>, and <a href=
|
||
"#sequence_and_mapping">sequence and mapping</a> operators. In
|
||
addition, py_cpp provides a simple way to export member variables and
|
||
define attributes by means of <a href="#getter_setter">getters and
|
||
setters</a>.
|
||
<h2>
|
||
<a name="general">General Operators</a>
|
||
</h2>
|
||
Python provides a number of special operatos for basic customization of a
|
||
class:
|
||
<dl>
|
||
<dt>
|
||
<b><tt class='method'>__repr__:</tt></b>
|
||
<dd>
|
||
create a string representation from which the object can be
|
||
reconstructed
|
||
<dt>
|
||
<b><tt class='method'>__str__:</tt></b>
|
||
<dd>
|
||
create a string representation which is suitable for printing
|
||
<dt>
|
||
<b><tt class='method'>__cmp__:</tt></b>
|
||
<dd>
|
||
three-way compare function, used to implement comparison operators
|
||
(< etc.)
|
||
<dt>
|
||
<b><tt class='method'>__hash__:</tt></b>
|
||
<dd>
|
||
needed to use the object as a dictionary key (only allowed if __cmp__
|
||
is also defined)
|
||
<dt>
|
||
<b><tt class='method'>__nonzero__:</tt></b>
|
||
<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>
|
||
<dd>
|
||
make instances of the class callable like a function
|
||
</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:
|
||
<pre>
|
||
std::string to_string(Foo const & f)
|
||
{
|
||
std::ostringstream s;
|
||
s << f;
|
||
return s.str();
|
||
}
|
||
</pre>
|
||
This function would be wrapped like this:
|
||
<pre>
|
||
python::class_builder<Foo> foo_class(my_module, "Foo");
|
||
foo_class.def(&to_string, "__str__");
|
||
</pre>
|
||
|
||
Note that py_cpp also supports <em>automatic wrapping</em> of
|
||
"__str__" and "__cmp__". This is explained in the <a href="#numeric">next
|
||
section</a> and the <a href="#numeric_table">table of numeric
|
||
operators</a>.
|
||
|
||
|
||
<h2>
|
||
<a name="numeric">Numeric Operators</a>
|
||
</h2>
|
||
There are two fundamental ways to define numeric operators within py_cpp:
|
||
manual wrapping (as is done with <a href="#general">general
|
||
operators</a>) and automatic wrapping. Lets start with the second
|
||
possibility. Suppose, C++ defines a class <code>Int</code> (which might
|
||
represent an infinite-precision integer) which supports addition, so that
|
||
we can write (in C++):
|
||
<pre>
|
||
Int a, b, c;
|
||
...
|
||
c = a + b;
|
||
</pre>
|
||
To enable the same functionality in Python, we first wrap the <code>
|
||
Int</code> class as usual:
|
||
<pre>
|
||
python::class_builder<Int> int_class(my_module, "Int");
|
||
int_class.def(python::constructor<>());
|
||
...
|
||
</pre>
|
||
Then we export the addition operator like this:
|
||
<pre>
|
||
int_class.def(python::operators<python::op_add>());
|
||
</pre>
|
||
Since Int also supports subtraction, multiplication, adn 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</a>):
|
||
<pre>
|
||
int_class.def(python::operators<(python::op_sub | python::op_mul | python::op_div)>());
|
||
</pre>
|
||
Note that the or-expression must be enclosed in parentheses. This form of
|
||
operator definition will wrap homogeneous operators, i.e. operators whose
|
||
left and right operand have the same type. Now, suppose that our C++
|
||
library also supports addition of Ints and plain integers:
|
||
<pre>
|
||
Int a, b;
|
||
int i;
|
||
...
|
||
a = b + i;
|
||
a = i + b;
|
||
</pre>
|
||
To wrap these heterogeneous operators (left and right hand side have
|
||
different types), we need a possibility 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:
|
||
<pre>
|
||
int_class.def(python::operators<python::op_add>(), python::right_operand<int>());
|
||
int_class.def(python::operators<python::op_add>(), python::left_operand<int>());
|
||
</pre>
|
||
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:
|
||
<pre>
|
||
int_class.def(python::operators<(python::op_sub | python::op_mul | python::op_div)>(),
|
||
python::right_operand<int>());
|
||
int_class.def(python::operators<(python::op_sub | python::op_mul | python::op_div)>(),
|
||
python::left_operand<int>());
|
||
</pre>
|
||
The type of the operand not mentioned is taken from the class object. In
|
||
our example, the class object is <code>int_class</code>, and thus the
|
||
other operand's type is `<code>Int const &</code>'. You can override
|
||
this default by explicitly specifying a type in the <code>
|
||
operators</code> template:
|
||
<pre>
|
||
int_class.def(python::operators<python::op_add, Int>(), python::right_operand<int>());
|
||
</pre>
|
||
Here, `<code>Int</code>' would be used instead of `<code>Int const
|
||
&</code>'.
|
||
<p>
|
||
Note that automatic wrapping doesn't need any specific form of <code>
|
||
operator+()</code> (or one of the other operators), but rather wraps
|
||
the <em>expression</em> `<code>left + right</code>'. That is, this
|
||
mechanism can be used for any definition of <code>operator+()</code>,
|
||
such as a free function `<code>Int operator+(Int, Int)</code>' or a
|
||
member function `<code>Int Int::operator+(Int)</code>'.
|
||
<p>
|
||
For the Python operators <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::detail</code>.
|
||
Thus it might be necessary to add a using declaration prior to
|
||
wrapping:
|
||
<pre>
|
||
namespace python {
|
||
namespace detail {
|
||
using my_namespace::pow;
|
||
using my_namespace::abs;
|
||
}}
|
||
</pre>
|
||
<p>
|
||
In some cases, automatic wrapping of operators is not possible or not
|
||
desirable. Suppose, for example, that the modulo operation for Ints is
|
||
defined by a set of functions <code>mod()</code> (for automatic
|
||
wrapping, we would need <code>operator%()</code>):
|
||
<pre>
|
||
Int mod(Int const & left, Int const & right);
|
||
Int mod(Int const & left, int right);
|
||
Int mod(int left, Int const & right);
|
||
</pre>
|
||
In order to create the Python operator "__mod__" from these functions, we
|
||
have to wrap them manually:
|
||
<pre>
|
||
int_class.def((Int (*)(Int const &, Int const &))&mod, "__mod__");
|
||
int_class.def((Int (*)(Int const &, int))&mod, "__mod__");
|
||
</pre>
|
||
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:
|
||
<pre>
|
||
Int rmod(Int const & right, int left)
|
||
{
|
||
return mod(left, right);
|
||
}
|
||
</pre>
|
||
This function must be wrapped under the name "__rmod__":
|
||
<pre>
|
||
int_class.def(&rmod, "__rmod__");
|
||
</pre>
|
||
A list of the possible operator names is also found in the <a href=
|
||
"#numeric_table">table</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.
|
||
<h4>
|
||
<a name="coercion">Coercion</a>
|
||
</h4>
|
||
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>
|
||
In contrast, py_cpp provides <em><a href="overloading.html">
|
||
overloading</a></em>. By means of overloading, operator calling can be
|
||
simplyfied drastically: you just register operators for all desired
|
||
type combinations, and py_cpp automatically ensures that the correct
|
||
function is called in each case. User defined coercion functions are
|
||
<em>not necessary</em>. 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:
|
||
<pre>
|
||
// this is not necessary if automatic operator wrapping is used
|
||
int_class.def_standard_coerce();
|
||
</pre>
|
||
In case you encounter a situation where you absolutely need a customized
|
||
coercion, you can overload the "__coerce__" operator itself. The
|
||
signature of a coercion function must look like this:
|
||
<pre>
|
||
python::tuple custom_coerce(PyObject * left, PyObject * right);
|
||
</pre>
|
||
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:
|
||
<pre>
|
||
some_class.def(&custom_coerce, "__coerce__");
|
||
</pre>
|
||
Note that the custom coercion function is only used if it is defined <em>
|
||
before</em> any automatic operator wrapping on the given class or a call
|
||
to `<code>some_class.def_standard_coerce()</code>'.
|
||
<h4>
|
||
<a name="ternary_pow">The Ternary <code>pow()</code> Operator</a>
|
||
</h4>
|
||
In addition to the usual binary <code>pow()</code>-operator (meaning
|
||
<code>x^y</code>), Python also provides a ternary variant that implements
|
||
<code>(x^y) % z</code> (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:
|
||
<pre>
|
||
Int power(Int const & first, Int const & second, Int const & module);
|
||
typedef Int (ternary_function1)(const Int&, const Int&, const Int&);
|
||
...
|
||
int_class.def((ternary_function1)&power, "__pow__");
|
||
</pre>
|
||
In case you want to support this function with non-uniform argument
|
||
types, wrapping is a little more involved. Suppose, you have to wrap:
|
||
<pre>
|
||
Int power(Int const & first, int second, int module);
|
||
Int power(int first, Int const & second, int module);
|
||
Int power(int first, int second, Int const & module);
|
||
</pre>
|
||
The first variant can be wrapped as usual:
|
||
<pre>
|
||
typedef Int (ternary_function2)(const Int&, int, int);
|
||
int_class.def((ternary_function2)&power, "__pow__");
|
||
</pre>
|
||
In the second variant, however, <code>Int</code> appears only as second
|
||
argument, and in the last one it is the third argument. Therefor we must
|
||
first provide functions where the argumant order is changed so that
|
||
<code>Int</code> appears in first place:
|
||
<pre>
|
||
Int rpower(Int const & second, int first, int module)
|
||
{
|
||
return power(first, second, third);
|
||
}
|
||
Int rrpower(Int const & third, int first, int second)
|
||
{
|
||
return power(first, second, third);
|
||
}
|
||
</pre>
|
||
These functions must be wrapped under the names "__rpow__" and
|
||
"__rrpow__" respectively:
|
||
<pre>
|
||
int_class.def((ternary_function2)&rpower, "__rpow__");
|
||
int_class.def((ternary_function2)&rrpower, "__rrpow__");
|
||
</pre>
|
||
Note that "__rrpow__" is an extension not present in plain Python.
|
||
<h4>
|
||
<a name="numeric_table">Table of Numeric Operators</a>
|
||
</h4>
|
||
<p>
|
||
Py_cpp supports the <a href=
|
||
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">
|
||
Python operators</a> listed in the following table. Note that
|
||
comparison (__cmp__) and string conversion (__str__) operators are
|
||
included in the list, although they are not strictly "numeric".
|
||
<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>python::op_add</code>
|
||
<td>
|
||
<code>cpp_left + cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__sub__, __rsub__</code>
|
||
<td>
|
||
<code>left - right</code>
|
||
<td>
|
||
<code>python::op_sub</code>
|
||
<td>
|
||
<code>cpp_left - cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__mul__, __rmul__</code>
|
||
<td>
|
||
<code>left * right</code>
|
||
<td>
|
||
<code>python::op_mul</code>
|
||
<td>
|
||
<code>cpp_left * cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__div__, __rdiv__</code>
|
||
<td>
|
||
<code>left / right</code>
|
||
<td>
|
||
<code>python::op_div</code>
|
||
<td>
|
||
<code>cpp_left / cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__mod__, __rmod__</code>
|
||
<td>
|
||
<code>left % right</code>
|
||
<td>
|
||
<code>python::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>python::op_divmod</code>
|
||
<td>
|
||
<code>cpp_left / cpp_right </code> and <code> cpp_left %
|
||
cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__pow__, __rpow__</code>
|
||
<td>
|
||
<code>pow(left, right)</code><br>
|
||
(binary power)
|
||
<td>
|
||
<code>python::op_pow</code>
|
||
<td>
|
||
<code>pow(cpp_left, cpp_right)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__pow__</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>python::op_lshift</code>
|
||
<td>
|
||
<code>cpp_left << cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__rshift__, __rrshift__</code>
|
||
<td>
|
||
<code>left >> right</code>
|
||
<td>
|
||
<code>python::op_rshift</code>
|
||
<td>
|
||
<code>cpp_left >> cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__and__, __rand__</code>
|
||
<td>
|
||
<code>left & right</code>
|
||
<td>
|
||
<code>python::op_and</code>
|
||
<td>
|
||
<code>cpp_left & cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__xor__, __rxor__</code>
|
||
<td>
|
||
<code>left ^ right</code>
|
||
<td>
|
||
<code>python::op_xor</code>
|
||
<td>
|
||
<code>cpp_left ^ cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__or__, __ror__</code>
|
||
<td>
|
||
<code>left | right</code>
|
||
<td>
|
||
<code>python::op_or</code>
|
||
<td>
|
||
<code>cpp_left | cpp_right</code>
|
||
<tr>
|
||
<td>
|
||
<code>__cmp__, __rcmp__</code>
|
||
<td>
|
||
<code>cmp(left, right)</code> (3-way compare)<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>python::op_cmp</code>
|
||
<td>
|
||
<code>cpp_left < cpp_right </code> and <code> cpp_right <
|
||
cpp_left</code>
|
||
<tr>
|
||
<td>
|
||
<code>__neg__</code>
|
||
<td>
|
||
<code>-oper </code> (unary negation)
|
||
<td>
|
||
<code>python::op_neg</code>
|
||
<td>
|
||
<code>-cpp_oper</code>
|
||
<tr>
|
||
<td>
|
||
<code>__pos__</code>
|
||
<td>
|
||
<code>+oper </code> (identity)
|
||
<td>
|
||
<code>python::op_pos</code>
|
||
<td>
|
||
<code>+cpp_oper</code>
|
||
<tr>
|
||
<td>
|
||
<code>__abs__</code>
|
||
<td>
|
||
<code>abs(oper) </code> (absolute value)
|
||
<td>
|
||
<code>python::op_abs</code>
|
||
<td>
|
||
<code>abs(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__invert__</code>
|
||
<td>
|
||
<code>~oper </code> (bitwise inversion)
|
||
<td>
|
||
<code>python::op_invert</code>
|
||
<td>
|
||
<code>~cpp_oper</code>
|
||
<tr>
|
||
<td>
|
||
<code>__int__</code>
|
||
<td>
|
||
<code>int(oper) </code> (integer conversion)
|
||
<td>
|
||
<code>python::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>python::op_long</code>
|
||
<td>
|
||
<code>PyLong_FromLong(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__float__</code>
|
||
<td>
|
||
<code>float(oper) </code> (float conversion)
|
||
<td>
|
||
<code>python::op_float</code>
|
||
<td>
|
||
<code>double(cpp_oper)</code>
|
||
<tr>
|
||
<td>
|
||
<code>__oct__</code>
|
||
<td>
|
||
<code>oct(oper) </code> (octal conversion)
|
||
<td colspan="2">
|
||
must be wrapped manually (wrapped function should return a string)
|
||
<tr>
|
||
<td>
|
||
<code>__hex__</code>
|
||
<td>
|
||
<code>hex(oper) </code> (hex conversion)
|
||
<td colspan="2">
|
||
must be wrapped manually (wrapped function should return a string)
|
||
<tr>
|
||
<td>
|
||
<code>__str__</code>
|
||
<td>
|
||
<code>str(oper) </code> (string conversion)
|
||
<td>
|
||
<code>python::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>
|
||
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 typically
|
||
iteration idiom looks like "<code>for i in S:</code>" , while in C++ one
|
||
uses "<code>for(iterator i = S.begin(); i != S.end(); ++i)</code>". One
|
||
could try to wrap C++ iterators in order to carry the C++ idiom into
|
||
Python. However, this does not work very well because (1) it leads to
|
||
non-uniform Python code (wrapped types must be used in a different way
|
||
than Python built-in types) and (2) iterators are often implemented as
|
||
plain C++ pointers which cannot be wrapped easily because py_cpp is
|
||
designed to handle objects only.
|
||
<p>
|
||
Thus, it is a good idea to provide sequence and mapping operators 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 throw the <code>PyExc_IndexError</code> when the index
|
||
is out-of-range in order to enable the "<code>for i in S:</code>"
|
||
idiom.
|
||
<p>
|
||
Here is an example. Suppose, we want to wrap a <code>
|
||
std::map<std::size_t,std::string></code>. This is done as follows
|
||
as follows:
|
||
<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, python::converters::to_python(key));
|
||
throw 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(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
|
||
>>> for i in m:
|
||
... print i
|
||
...
|
||
zero
|
||
one
|
||
two
|
||
three
|
||
</pre>
|
||
</blockquote>
|
||
<h2>
|
||
<a name="getter_setter">Getters and Setters</a>
|
||
</h2>
|
||
<p>
|
||
Py_cpp extension classes support some additional "special method"
|
||
protocols not supported by built-in Python classes. Because writing
|
||
<code>__getattr__</code>, <code> __setattr__</code>, and <code>
|
||
__delattr__</code> 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(python::constructor<>());
|
||
pair_int_long.def(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=
|
||
"py_cpp.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 21, 2000
|
||
</div>
|
||
|