Доброго времени суток, хабр!
Здесь мы не будем рассуждать о плюсах и минусах языков.
Мы будем использовать их вместе!
В этом нам поможет замечательная библиотека pyd. С её помощью можно как вызывать код на python из кода на d, так и наоборот.
Рассмотрим первый вариант. Заполняем файл dub.json
{ "name": "pydtest", "targetType": "executable", "dependencies": { "pyd": "~>0.9.7" }, "subConfigurations": { "pyd": "python34" } }
subConfigurations указывает, что мы будем использовать python 3.4
Создаём source/main.d
import std.stdio; import pyd.pyd, pyd.embedded; void main() { py_init(); auto script = new InterpContext; // следующие 2 строки позволяют искать модули в текущей директории // это нужно чтобы не устанавливать наш пакет myscript.py в систему script.py_stmts( "import sys" ); script.py_stmts( "sys.path.append('.')" ); script.py_stmts( "import myscript" ); writeln( script.py_eval!string( "myscript.func()" ) ~ " from pyd" ); }
Создаём myscript.py
def func(): return "hello habr!"
Запускаем сборку и её результат
dub build && ./pydtest
И всё!
hello habr! from pyd
Всё настолько просто!
Попробуем немного усложнить. Добавим функцию сложения чисел в myscript.py
def sum(a,b): return a + b
И вызовем её из кода на D. Добавим это в функцию main.main
... script.x = 13; script.y = 21; writefln( "result: %d", script.py_eval!int( "myscript.sum(x,y)" ) ); ...
Усложнить не получилось =)
Класс InterpContext олицетворяет контекст интерпретатора (как ни странно) и мы можем добавлять туда переменные таким простым способом. Поля x и y не являются частью объекта script — таких полей нет, но это работает потому, что в языке D есть возможность конвертировать вызовы несуществующих методов класса (или структуры) в вызов метода opDispatch, который, как в данном случае, может быть свойством.
@property PydObject opDispatch(string id)() { // возвращает значение из контекста return this.locals[id]; } @property void opDispatch(string id, T)(T t) { // записывает значение в контекст static if(is(T == PydObject)) { alias t s; }else{ PydObject s = py(t); } this.locals[id] = py(s); }
Таким же способом мы можем взять объект из контекста:
... script.py_stmts( "z = myscript.sum(8,7)" ); writefln( "result2: %d", script.z.to_d!int ); ...
Да и функции можно вызывать практически так же:
... auto sum = script.myscript.sum; writefln( "result3: %d", sum(14,15).to_d!int ); ...
script.myscript.sum(14,15).to_d!int; // сработает, что странно, было бы логично запретить script.myscript.oneargfunc(12).to_d!int; // не скомпилируется, так как oneargfunc(12) это вызов opDispatch с параметром 12 script.myscript.oneargfunc()(12).to_d!int; // тут всё в порядке: явно вызывается oneargfunc(), затем у результата вызывается opCall(12)
Теперь попробуем наоборот из кода на python вызвать код на D. Создадим новую папку для этого.
Создадим файл dcode.d с содержанием:
module dcode; import pyd.pyd; import std.math; float[] calc( float x, float y ) { return [ sqrt(x*y), x^^y, x/y ]; } extern(C) void PydMain() { def!(calc)(); module_init(); }
и файл setup_my_dcode.py (имя никак не влияет)
from pyd.support import setup, Extension projName = 'dcode' setup( name=projName, version='0.1', ext_modules=[ Extension(projName, ['dcode.d'], extra_compile_args=['-w'], build_deimos=True, d_lump=True ) ], )
соберём наше расширение (именно build, а не install, чтобы не засорять систему тестовыми файлами)
python3 setup_my_dcode.py build
создасться папочка build такого содержания
build ├── lib.linux-x86_64-3.4 │ └── dcode.cpython-34m.so └── temp.linux-x86_64-3.4 └── infra ├── pydmain.d ├── so_ctor.o └── temp.o
Нас интересует build/lib.linux-x86_64-3.4/dcode.cpython-34m.so. Копируем его в текущую директорию или переходим в папку с ним и можем проверять прямо в интерактивном интерпретаторе:
python3 Python 3.4.1 (default, Nov 3 2014, 14:38:10) [GCC 4.9.1 20140930 (Red Hat 4.9.1-11)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import dcode >>> dcode.calc( 5, 12 ) [7.745966911315918, 244140624.0, 0.4166666567325592] >>>
И опять всё достаточно просто!
И опять попробуем всё усложнить — добавим класс в dcode.d
class Foo { float a = 0, b = 0; static string desc() { return "some ops"; } this( float A, float B ) { a = A; b = B; } float sum() const { return a + b; } float div() const { return a / b; } } extern(C) void PydMain() { def!(calc)(); // сначала функции module_init(); // затем инициализация модуля wrap_class!( // только потом классы Foo, Init!(float,float), Repr!(Foo.toString), // как python будет это переводить в строку Def!(Foo.sum), Def!(Foo.div), StaticDef!(Foo.desc) )(); }
К сожалению в этой ситуации всё действительно немного усложнилось. Для работы с классами D в python нужно объявлять конструкторы, функции, и т.д.
Собираем, проверяем:
python3 Python 3.4.1 (default, Nov 3 2014, 14:38:10) [GCC 4.9.1 20140930 (Red Hat 4.9.1-11)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from dcode import Foo >>> Foo.desc() 'some ops' >>> a = Foo(1,2) >>> a.div() 0.5 >>> a.sum() 3.0 >>>
Работает!
О вариантах применения говорить не нужно: их много и они интересны. Стоит упомянуть, что библиотека ещё не дошла до стабильной версии 1.0.0 и могут встречаться ошибки.
Я нашёл только одну проблему: нельзя запустить код на D из кода на python, встроенного в код на D
Но мне кажется это не фундаментальная проблема и автор её сможет легко исправить.
Очень приятная документация по проекту находится здесь и ещё примеры здесь
ссылка на оригинал статьи http://habrahabr.ru/post/259727/
Добавить комментарий