mirror of
https://github.com/boostorg/python.git
synced 2026-01-22 05:22:45 +00:00
502 lines
18 KiB
HTML
502 lines
18 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"
|
|
"http://www.w3.org/TR/REC-html40/strict.dtd">
|
|
<title>
|
|
Special Method Name 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 Name
|
|
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>).
|
|
|
|
|
|
<h2>Numeric Operators</h2>
|
|
|
|
There are two fundamental ways to define numeric operators within py_cpp: automatic wrapping
|
|
and manual wrapping. Suppose, C++ defines an addition operator for type <code>Rational</code>, so that we can write:
|
|
|
|
<pre>
|
|
Rational a, b, c;
|
|
...
|
|
c = a + b;
|
|
</pre>
|
|
|
|
To enable the same functionality in Python, we first wrap the Rational class as usual:
|
|
|
|
<pre>
|
|
py::ClassWrapper<Rational> rational_class(my_module, "Rational");
|
|
rational_class.def(py::Constructor<>());
|
|
...
|
|
</pre>
|
|
|
|
Then we export the addition operator like this:
|
|
|
|
<pre>
|
|
rational_class.def(py::operators<py::op_add>());
|
|
</pre>
|
|
|
|
Since Rational 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>
|
|
rational_class.def(py::operators<(py::op_sub | py::op_mul | py::op_div)>());
|
|
</pre>
|
|
|
|
Note that the or-expression must be enclosed in parentheses. This form of operator definition will wrap homogeneous operators, that is operators whose left and right operand have the same type. Now, suppose that our C++ library also supports addition of Rationals and integers:
|
|
|
|
<pre>
|
|
Rational 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 operand type. This is done using the <code>right_operand</code> and <code>left_operand</code> templates:
|
|
|
|
<pre>
|
|
rational_class.def(py::operators<py::op_add>(), py::right_operand<int>());
|
|
rational_class.def(py::operators<py::op_add>(), py::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>
|
|
rational_class.def(py::operators<(py::op_sub | py::op_mul | py::op_div)>(),
|
|
py::right_operand<int>());
|
|
rational_class.def(py::operators<(py::op_sub | py::op_mul | py::op_div)>(),
|
|
py::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>rational_class</code>, and thus the other operand's type is `<code>Rational const &</code>'. You can override this default by explicitly specifying a type in the <code>operators</code> template:
|
|
|
|
<pre>
|
|
rational_class.def(py::operators<py::op_add, Rational>(), py::right_operand<int>());
|
|
</pre>
|
|
|
|
Here, `<code>Rational</code>' would be used instead of `<code>Rational const &</code>'.
|
|
<p>
|
|
Note that automatic wrapping doesn't need any specific form of <code>operator+()</code> (or any other operator), 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>Rational operator+(Rational, Rational)</code>' or a member function `<code>Rational Rational::operator+(Rational)</code>'.
|
|
|
|
<p>
|
|
In some cases, automatic wrapping of operators is not possible or not desirable. Suppose, for example, that the power operation for Rationals is defined by a set of functions <code>pow()</code>:
|
|
|
|
<pre>
|
|
Rational pow(Rational const & left, Rational const & right);
|
|
Rational pow(Rational const & left, int right);
|
|
Rational pow(int left, Rational const & right);
|
|
</pre>
|
|
|
|
In order to create the Python operator "pow" from these functions, we have to wrap them manually:
|
|
|
|
<pre>
|
|
rational_class.def((Rational (*)(Rational const &, Rational const &))&pow, "__pow__");
|
|
rational_class.def((Rational (*)(Rational const &, int))&pow, "__pow__");
|
|
</pre>
|
|
|
|
The third form (with <code>int</code> as left operand) cannot be wrapped this way. We must first create a function <code>rpow()</code> with the operands reversed:
|
|
|
|
<pre>
|
|
Rational rpow(Rational const & right, int left)
|
|
{
|
|
return pow(left, right);
|
|
}
|
|
</pre>
|
|
|
|
This function must be wrapped under the name "__rpow__":
|
|
|
|
<pre>
|
|
rational_class.def(&rpow, "__rpow__");
|
|
</pre>
|
|
|
|
A list of the possible operator names is also found in the <a href="#numeric_table">table</a>.
|
|
Special treatment is necessary to define the <a href="#ternary_pow">ternary pow</a>.
|
|
<p>
|
|
Automatic and manual wrapping can be mixed arbitrarily.
|
|
|
|
<a name="coercion">
|
|
<h4>Coercion</h4></a>
|
|
|
|
|
|
So, for example, we can wrap a
|
|
<code>std::map<std::size_t,std::string></code> as follows:
|
|
<h2>
|
|
Example
|
|
</h2>
|
|
<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, py::converters::to_python(key));
|
|
throw py::ErrorAlreadySet();
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
ClassWrapper<StringMap> string_map(my_module, "StringMap");
|
|
string_map.def(py::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[3] = 'farther'
|
|
>>> len(m)
|
|
1
|
|
</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>
|
|
<h2>
|
|
Direct Access to Data Members
|
|
</h2>
|
|
<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>ClassWrapper<></code> and <code>
|
|
ExtensionClass<></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");
|
|
|
|
ClassWrapper<Pil> pair_int_long(my_module, "Pair");
|
|
pair_int_long.def(py::Constructor<>());
|
|
pair_int_long.def(py::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="numerics">Numeric Method Support</a>
|
|
</h2>
|
|
<p>
|
|
Py_cpp supports the following <a href=
|
|
"http://www.pythonlabs.com/pub/www.python.org/doc/current/ref/numeric-types.html">
|
|
Python special numeric method names</a>:
|
|
<p>
|
|
<table summary="special numeric methods" cellpadding="5" border="1">
|
|
<thead>
|
|
<tr>
|
|
<td>
|
|
Name
|
|
<td>
|
|
Notes
|
|
<tr>
|
|
<td>
|
|
<code>__add__(self, other)</code>
|
|
<td>
|
|
<code>operator+(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__sub__(self, other)</code>
|
|
<td>
|
|
<code>operator-(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__mul__(self, other)</code>
|
|
<td>
|
|
<code>operator*(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__div__(self, other)</code>
|
|
<td>
|
|
<code>operator/(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__mod__(self, other)</code>
|
|
<td>
|
|
<code>operator%(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__divmod__(self, other)</code>
|
|
<td>
|
|
return a <code> py::Tuple</code> initialized with <code>
|
|
(</code><em>quotient</em><code>,</code> <em>
|
|
remainder</em><code>)</code>.
|
|
<tr>
|
|
<td>
|
|
<code>__pow__(self, other [, modulo])</code>
|
|
<td>
|
|
use <a href="overloading.html">overloading</a> to support both
|
|
forms of __pow__
|
|
<tr>
|
|
<td>
|
|
<code>__lshift__(self, other)</code>
|
|
<td>
|
|
<code>operator<<(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__rshift__(self, other)</code>
|
|
<td>
|
|
<code>operator>>(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__and__(self, other)</code>
|
|
<td>
|
|
<code>operator&(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__xor__(self, other)</code>
|
|
<td>
|
|
<code>operator^(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__or__(self, other)</code>
|
|
<td>
|
|
<code>operator|(const T&, const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__neg__(self)</code>
|
|
<td>
|
|
<code>operator-(const T&)</code> (unary negation)
|
|
<tr>
|
|
<td>
|
|
<code>__pos__(self)</code>
|
|
<td>
|
|
<code>operator+(const T&)</code> (identity)
|
|
<tr>
|
|
<td>
|
|
<code>__abs__(self)</code>
|
|
<td>
|
|
Called to implement the built-in function abs()
|
|
<tr>
|
|
<td>
|
|
<code>__invert__(self)</code>
|
|
<td>
|
|
<code>operator~(const T&)</code>
|
|
<tr>
|
|
<td>
|
|
<code>__int__(self)</code>
|
|
<td>
|
|
<code>operator long() const</code>
|
|
<tr>
|
|
<td>
|
|
<code>__long__(self)</code>
|
|
<td>
|
|
Should return a Python <code>long</code> object. Can be
|
|
implemented with <code>PyLong_FromLong(<em>value</em>)</code>,
|
|
for example.
|
|
<tr>
|
|
<td>
|
|
<code>__float__(self)</code>
|
|
<td>
|
|
<code>operator double() const</code>
|
|
<tr>
|
|
<td>
|
|
<code>__oct__(self)</code>
|
|
<td>
|
|
Called to implement the built-in function oct(). Should return a
|
|
string value.
|
|
<tr>
|
|
<td>
|
|
<code>__hex__(self)</code>
|
|
<td>
|
|
Called to implement the built-in function hex(). Should return a
|
|
string value.
|
|
<tr>
|
|
<td>
|
|
<code>__coerce__(self, other)</code>
|
|
<td>
|
|
Should return a Python 2-<em>tuple</em> (C++ code may return a
|
|
<code>py::Tuple</code>) where the elements represent the values
|
|
of <code> self</code> and <code>other</code> converted to the
|
|
same type.
|
|
</table>
|
|
|
|
<h2><a name="reasons">Where are the <code>__r</code><i><name></i><code>__</code>
|
|
functions?</a></h2>
|
|
|
|
<p>
|
|
At first we thought that supporting <code>__radd__</code> and its ilk would be
|
|
impossible, since Python doesn't supply any direct support and in fact
|
|
implements a special case for its built-in class instances. <a
|
|
href="http://starship.python.net/crew/arcege/extwriting/pyextnum.html">This
|
|
article</a> gives a pretty good overview of the direct support for numerics
|
|
that Python supplies for extension types. We've since discovered that it can
|
|
be done, but there are some pretty convincing <a
|
|
href="http://starship.python.net/crew/lemburg/CoercionProposal.html">arguments</a>
|
|
out there that this arrangement is less-than-ideal. Instead of supplying a
|
|
sub-optimal solution for the sake of compatibility with built-in Python
|
|
classes, we're doing the neccessary research so we can "do it right". This
|
|
will also give us a little time to hear from users about what they want. The
|
|
direction we're headed in is based on the idea of <a
|
|
href="http://www.sff.net/people/neelk/open-source/Multimethod.py">multimethods</a>
|
|
rather than on trying to find a coercion function bound to one of the
|
|
arguments.
|
|
|
|
<h3>And what about <code>__complex__</code>?</h3>
|
|
<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 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: Oct 19, 2000
|
|
</div>
|
|
|