Python динамически типизирован, в отличие от C++, который статично типизирован. Переменные Python могут содержать целое число, поплавок, список, дикт, кортеж, стр, длину и т. д., среди прочего. С точки зрения Boost. Python и C++, эти переменные являются примерами класса<object
>. В этой главе мы рассмотрим, как обращаться с объектами Python.
Как уже упоминалось, одной из целей Боуста. Python обеспечивает двунаправленное отображение между C++ и Python, сохраняя при этом ощущение Python. Boost.Python C++<object
>как можно ближе к Python. Это должно значительно минимизировать кривую обучения.

Класс<object
>Обертывание<PyObject*
>. Все тонкости работы с<PyObject
>, такие как управление подсчетом ссылок, обрабатываются классом<object
>. Совместимость объектов C++ бесшовна. Boost.Python C++<object
>может быть построено из любого объекта C++.
Чтобы проиллюстрировать этот фрагмент кода Python:
def f(x, y):
if (y == 'foo'):
x[3:7] = 'bar'
else:
x.items += y(3, x)
return x
def getfunc():
return f;
Можно переписать на C++ с помощью Boost. Python работает таким образом:
object f(object x, object y) {
if (y == "foo")
x.slice(3,7) = "bar";
else
x.attr("items") += y(3, x);
return x;
}
object getfunc() {
return object(f);
}
Помимо косметических различий из-за того, что мы пишем код на C++, внешний вид должен быть сразу же очевиден для программиста Python.
Повышаю. Python поставляется с набором производных<object
>типов, соответствующих типу Python:
- список
- диктовать
- кортеж
- полоскание
- длинный
- перечислять
Эти производные<object
>типы действуют как настоящие типы Python. Например:
str(1) ==> "1"
Там, где это уместно, конкретный производный<object
>имеет соответствующие методы типа Python. Например,<dict
>имеет<keys()
>метод:
d.keys()
<make_tuple
>предусмотрено декларированиекортежных букв. Пример:
make_tuple(123, 'D', "Hello, World", 0.0);
В C++, когда Boost.Python<object
>s используются в качестве аргументов для функций, требуется сопоставление подтипов. Например, когда функция<f
>, как указано ниже, обернута, она будет принимать только экземпляры типа и подтипов Python<str
>.
void f(str name)
{
object n2 = name.attr("upper")();
str NAME = name.upper();
object msg = "%s is bigger than %s" % make_tuple(NAME,name);
}
Более подробно:
str NAME = name.upper();
Иллюстрирует, что мы предоставляем версии методов типа Стр в качестве функций C++.
object msg = "%s is bigger than %s" % make_tuple(NAME,name);
Показывает, что вы можете написать эквивалент C++<"format"
% x,y,z
>на Python, что полезно, поскольку нет простого способа сделать это в std C++.
Python:
>>> d = dict(x.__dict__)
>>> d['whatever'] = 3
C++:
dict d(x.attr("__dict__"));
d['whatever'] = 3;
Из-за динамичного характера Boost. Объекты Python, любой<class_<T>
>также может быть одним из этих типов! Следующий фрагмент кода обертывает объект класса (типа).
Мы можем использовать это для создания завернутых экземпляров. Пример:
object vec345 = (
class_<Vec2>("Vec2", init<double, double>())
.def_readonly("length", &Point::length)
.def_readonly("angle", &Point::angle)
)(3.0, 4.0);
assert(vec345.attr("length") == 5.0);
В какой-то момент нам нужно будет получить значения C++ из экземпляров объектов. Этого можно достичь с помощью функции<extract<T>
>. Рассмотрим следующее:
double x = o.attr("length");
В коде выше мы получили ошибку компилятора, потому что Boost. Python<object
>не может быть преобразован в<double
>. Вместо этого, то, что мы хотели сделать выше, может быть достигнуто путем написания:
double l = extract<double>(o.attr("length"));
Vec2& v = extract<Vec2&>(o);
assert(l == v.length());
Первая линия пытается извлечь «длинный» атрибут Boost. Python<object
>. Вторая линия пытаетсяизвлечь<Vec2
>объект из удерживаемого Boost. Python<object
>.
Обратите внимание, что мы сказали «попытка» выше. Что, если буст. Python<object
>не поддерживает тип<Vec2
>? Это, безусловно, возможно, учитывая динамическую природу Python<object
>. Чтобы быть на безопасной стороне, если тип C++ не может быть извлечен, бросается соответствующее исключение. Чтобы избежать исключения, нужно проверить на экстрактивность:
extract<Vec2&> x(o);
if (x.check()) {
Vec2& v = x(); ...
Проницательный читатель мог заметить, что<extract<T>
>объект на самом деле решает проблему непостоянного копирования:
dict d = extract<dict>(x.attr("__dict__"));
d["whatever"] = 3;
Повышаю. Python имеет отличную возможность захватывать и обертывать C++ enums. В то время как Python не имеет<enum
>типа, мы часто хотим представить наши C++ перечни Python как<int
>. Повышаю. Средство перечня Python упрощает эту задачу, одновременно заботясь о правильном преобразовании динамической типизации Python в сильную статичную типизацию C++ (в C++ инты не могут быть неявно преобразованы в перечни). Чтобы проиллюстрировать, приведем список C++:
enum choice { red, blue };
Конструкция:
enum_<choice>("choice")
.value("red", red)
.value("blue", blue)
;
Его можно использовать для работы с Python. Новый тип enum создается в текущем<scope()
>, который обычно является текущим модулем. Сниппет выше создает класс Python, полученный из типа Python<int
>, который связан с типом C++, переданным в качестве его первого параметра.
![[Note]](/img/note.png) |
Note |
Что такое масштаб?
Область действия - это класс, который имеет связанный глобальный объект Python, который управляет пространством имен Python, в котором новые классы расширений и обернутые функции будут определены как атрибуты. Подробности можно найти здесь.
|
Вы можете использовать эти значения в Python.
>>> my_module.choice.red
my_module.choice.red
где my_module - модуль, в котором декларируется число. Вы также можете создать новую область вокруг класса:
scope in_X = class_<X>("X")
.def( ... )
.def( ... )
;
enum_<X::nested>("nested")
.value("red", red)
.value("blue", blue)
;
Если вы хотите, чтобы<boost::python::object
>управлял указателем на<PyObject*
>пиобдж, вы делаете:
boost::python::object o(boost::python::handle<>(pyobj));
В этом случае<o
>объект, управляющий<pyobj
>, выигрывает’t увеличение эталонного счета на строительство.
В противном случае использовать заимствованную ссылку:
boost::python::object o(boost::python::handle<>(boost::python::borrowed(pyobj)));
В этом случае<Py_INCREF
>называется, поэтому<pyobj
>не разрушается, когда объект выходит из сферы действия.