πŸ“œ ⬆️ ⬇️

Python and D

Good day, Habr!

Here we will not talk about the pros and cons of languages.


')
We will use them together!



This will help us a great library pyd . With it, you can both call python code from d code, and vice versa.

Consider the first option. Fill in the file dub.json:

{ "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!" 

Run the assembly and its result
 dub build && ./pydtest 

And that's it!
 hello habr! from pyd 

Everything is so simple!

Let's try to complicate things a little. Add the function to add numbers to myscript.py:

 def sum(a,b): return a + b 

And call it from the code on D. Add this to the function main.main:

 ... script.x = 13; script.y = 21; writefln( "result: %d", script.py_eval!int( "myscript.sum(x,y)" ) ); ... 

Complicate did not work.

The InterpContext class represents the interpreter context (oddly enough) and we can add variables there in such a simple way. The x and y fields are not part of the script object β€” there are no such fields, but this works because in D there is the ability to convert calls to nonexistent methods of a class (or structure) into a call to the opDispatch method, which, as 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 ts; }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 ); ... 

Yes, and functions can be called almost the same:

 ... auto sum = script.myscript.sum; writefln( "result3: %d", sum(14,15).to_d!int ); ... 

some moments
The syntax of property in the D language is a topic that has been discussed for a long time , and the issue is specifically 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 opposite from the python code to call the code on D. Create a new folder for this.
Create a dcode.d file with the content:

 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 (build, not install, so as not to clutter 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. Copy it to the current directory or go to the folder with it and 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 things - we will 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, everything is really a bit 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!

We don’t need to talk about use cases: there are many of them and they are interesting. It is worth mentioning that the library has not yet reached a 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 .

Source: https://habr.com/ru/post/259727/


All Articles