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 │   └── 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *