Python and D
- Tutorial
Good day, Habr!
Here we will not discuss the pros and cons of languages.

We will use them together!

The wonderful pyd library will help us with this . With its help, you can both call python code from d code and vice versa.
Consider the first option. We fill in the dub.json file:
subConfigurations indicates that we will use python 3.4.
Create source / main.d:
Create myscript.py
We start assembly and its result
And that’s it!
Everything is so simple!
Let's try to complicate a bit. Add the function of adding numbers to myscript.py:
And call it from the code in D. Add this to the main.main function:
Complicate failed.
The InterpContext class represents the context of the interpreter (oddly enough) and we can add variables there in such a simple way. The fields x and y are not part of the script object - there are no such fields, but this works because in the D language it is possible to convert calls to nonexistent methods of a class (or structure) into a call to the opDispatch method, which, in this case, can be a property.
In the same way, we can take an object from the context:
And you can call functions in much the same way:
Now let's try the other way from the python code to call the code on D. Create a new folder for this.
Create a dcode.d file with the contents:
And the setup_my_dcode.py file (the name has no effect):
let's build our extension (namely build, not install, so as not to clog the system with test files):
create a daddy build of such content
We are interested in build / lib.linux-x86_64-3.4 / dcode.cpython-34m.so. We copy it to the current directory or go to the folder with it and we can check it directly in the interactive interpreter:
And again, everything is quite simple!
And again, let's try to complicate everything - add a class to dcode.d:
Unfortunately, in this situation, things really got a little complicated. To work with D classes in python, you need to declare constructors, functions, etc.
We collect, check:
Works!
There is no need to talk about application options: there are a lot of them and they are interesting. It is worth mentioning that the library has not yet reached the stable version 1.0.0 and errors may occur.
I found only one problem: you cannot run D code from python code embedded in D code:

But it seems to me that this is not a fundamental problem and the author can easily fix it.
Very nice project documentation is here and more examples here .
Here we will not discuss the pros and cons of languages.

We will use them together!

The wonderful pyd library will help us with this . With its help, you can both call python code from d code and vice versa.
Consider the first option. We fill in the dub.json file:
{
"name": "pydtest",
"targetType": "executable",
"dependencies": {
"pyd": "~>0.9.7"
},
"subConfigurations": {
"pyd": "python34"
}
}
subConfigurations indicates that we will use python 3.4.
Create 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" );
}
Create myscript.py
def func():
return "hello habr!"
We start assembly and its result
dub build && ./pydtest
And that’s it!
hello habr! from pyd
Everything is so simple!
Let's try to complicate a bit. Add the function of adding numbers to myscript.py:
def sum(a,b):
return a + b
And call it from the code in D. Add this to the main.main function:
...
script.x = 13;
script.y = 21;
writefln( "result: %d", script.py_eval!int( "myscript.sum(x,y)" ) );
...
Complicate failed.
The InterpContext class represents the context of the interpreter (oddly enough) and we can add variables there in such a simple way. The fields x and y are not part of the script object - there are no such fields, but this works because in the D language it is possible to convert calls to nonexistent methods of a class (or structure) into a call to the opDispatch method, which, in this case, can be a property.
InterpContext.opDispatch Method Code
@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);
}
In the same way, we can take an object from the context:
...
script.py_stmts( "z = myscript.sum(8,7)" );
writefln( "result2: %d", script.z.to_d!int );
...
And you can call functions in much the same way:
...
auto sum = script.myscript.sum;
writefln( "result3: %d", sum(14,15).to_d!int );
...
some moments
The property syntax in the D language has been a long-discussed topic , and specifically the question is related to the situation when property returns an object with the opCall method:
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)
Now let's try the other way from the python code to call the code on D. Create a new folder for this.
Create a dcode.d file with the contents:
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();
}
And the setup_my_dcode.py file (the name has no effect):
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
)
],
)
let's build our extension (namely build, not install, so as not to clog the system with test files):
python3 setup_my_dcode.py build
create a daddy build of such content
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
We are interested in build / lib.linux-x86_64-3.4 / dcode.cpython-34m.so. We copy it to the current directory or go to the folder with it and we can check it directly in the interactive interpreter:
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]
>>>
And again, everything is quite simple!
And again, let's try to complicate everything - add a class to 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)
)();
}
Unfortunately, in this situation, things really got a little complicated. To work with D classes in python, you need to declare constructors, functions, etc.
We collect, check:
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
>>>
Works!
There is no need to talk about application options: there are a lot of them and they are interesting. It is worth mentioning that the library has not yet reached the stable version 1.0.0 and errors may occur.
I found only one problem: you cannot run D code from python code embedded in D code:

But it seems to me that this is not a fundamental problem and the author can easily fix it.
Very nice project documentation is here and more examples here .