Доброго времени суток, хабр!
Здесь мы не будем рассуждать о плюсах и минусах языков.
Мы будем использовать их вместе!
В этом нам поможет замечательная библиотека pyd. С её помощью можно как вызывать код на python из кода на d, так и наоборот.
Рассмотрим первый вариант. Заполняем файл dub.json:
subConfigurations указывает, что мы будем использовать python 3.4
Создаём source/main.d:
Создаём myscript.py
Запускаем сборку и её результат
И всё!
Всё настолько просто!
Попробуем немного усложнить. Добавим функцию сложения чисел в myscript.py:
И вызовем её из кода на D. Добавим это в функцию main.main:
Усложнить не получилось.
Класс InterpContext олицетворяет контекст интерпретатора (как ни странно) и мы можем добавлять туда переменные таким простым способом. Поля x и y не являются частью объекта script — таких полей нет, но это работает потому, что в языке D есть возможность конвертировать вызовы несуществующих методов класса (или структуры) в вызов метода opDispatch, который, как в данном случае, может быть свойством.
Таким же способом мы можем взять объект из контекста:
Да и функции можно вызывать практически так же:
Теперь попробуем наоборот из кода на python вызвать код на D. Создадим новую папку для этого.
Создадим файл dcode.d с содержанием:
И файл setup_my_dcode.py (имя никак не влияет):
соберём наше расширение (именно build, а не install, чтобы не засорять систему тестовыми файлами):
создасться папочка build такого содержания
Нас интересует build/lib.linux-x86_64-3.4/dcode.cpython-34m.so. Копируем его в текущую директорию или переходим в папку с ним и можем проверять прямо в интерактивном интерпретаторе:
И опять всё достаточно просто!
И опять попробуем всё усложнить — добавим класс в dcode.d:
К сожалению в этой ситуации всё действительно немного усложнилось. Для работы с классами D в python нужно объявлять конструкторы, функции и т.д.
Собираем, проверяем:
Работает!
О вариантах применения говорить не нужно: их много и они интересны. Стоит упомянуть, что библиотека ещё не дошла до стабильной версии 1.0.0 и могут встречаться ошибки.
Я нашёл только одну проблему: нельзя запустить код на D из кода на python, встроенного в код на D:
Но мне кажется, это не фундаментальная проблема и автор её сможет легко исправить.
Очень приятная документация по проекту находится здесь и ещё примеры здесь.
Здесь мы не будем рассуждать о плюсах и минусах языков.
Мы будем использовать их вместе!
В этом нам поможет замечательная библиотека 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, который, как в данном случае, может быть свойством.
Код метода InterpContext.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 );
...
некоторые моменты
синтаксис property в языке D является давно обсуждаемой темой, а конкретно вопрос связан с ситуацией, когда property возвращает объект с методом opCall:
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
¦ L-- dcode.cpython-34m.so
L-- temp.linux-x86_64-3.4
L-- infra
+-- pydmain.d
+-- so_ctor.o
L-- 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:
Но мне кажется, это не фундаментальная проблема и автор её сможет легко исправить.
Очень приятная документация по проекту находится здесь и ещё примеры здесь.
Dreyk
Jabher
главное чтобы ORM не реализовали…
dannote
Фундаментальная проблема. Без callback-ов жить не очень здорово и наличие этой проблемы говорит о том, что виртуальная машина не fully-resumable, что влечет за собой еще целый ряд сюрпризов.
deviator Автор
Насчёт фундаментальности я говорил в плане исправления. Ошибка при запуске программы была вида 'already defined', и относилась к загрузке дин. библиотеки phobos. Но видимо я несколько ошибся насчёт простоты её исправления. Не совсем понял насчёт виртуальной машины. pyd не является самостоятельной виртуальной машиной, используется cpython, может я не совсем понял о чём Вы? И что означает термин full-resumable?
dannote
Есть стандартная проблема при вызове нативного кода из виртуальной машины: казалось бы, необходимо просто сохранить контекст выполнения, выполнить код и возобновить работу из контекста, но если вызвать из нативного кода интерпретируемый, с этим самым сохраненным контекстом возникнет нештатная ситуация. Еще одна стандартная проблема в подобной ситуации — сборка мусора. Во многих случаях обе проблемы решаемы, но это явно не 3 строчки кода.
ZyXI
Почему не три строчки? Я писал биндинги к Python в Vim и там есть следующие случаи:
Сборка мусора вызывала не больше проблем, чем прямое управление памятью в других участках кода, при том проблемы были только с собственными типами, определёнными в C.
vintage
Самый шик — это написать интерпретатор Python на D в виде статического компилятора.
wiygn
Интерпретатор в виде статического компилятора? Это как?
Или вы про JIT/AOT?
vintage
Это трансляция Python в D налету во время компиляции.
deviator Автор
Трансляция python в D это кажется вот это.
Не совсем понимаю зачем это может быть нужно, имхо смысл python встраивать в D в том, что python динамичный, его не надо компилировать, его можно использовать как конфиг программы, изменять на лету и тд, то есть по настоящему скриптовой язык. А так получается ни рыба ни мясо.
vintage
Нет, delight — это D с немного другим синтаксисом.
В том-то и дело, что эту его динамичность не сложно реализовать на D. Да и сам D можно использовать как скриптовый язык с AOT компиляцией.
brainick
смешал селедку с малиновым вареньем — такая фигня получилась