相互運用性
GraalPyを埋め込む最善の方法は、GraalVM SDKポリグロットAPIを使用することです。
ポリグロットAPI
GraalVMでは、JavaScript、R、Rubyや、LLVMビットコードにコンパイルされるものを含め、他の様々なプログラミング言語がサポートされているため、これらとやり取りするためのPython APIも提供されます。実際、GraalVMは、GraalVM LLVMランタイムを使用してPythonネイティブ拡張を実行するときに、このAPIを内部的に使用します。
polyglot
モジュールをインポートして、他の言語を操作できます:
>>> import polyglot
別の言語のインライン化されたコードを評価できます:
>>> polyglot.eval(string="1 + 1", language="ruby")
2
パスを渡すことによって、ファイル内のコードを評価できます:
>>> with open("./my_ruby_file.rb", "w") as f:
... f.write("Polyglot.export('RubyPolyglot', Polyglot)")
41
>>> polyglot.eval(path="./my_ruby_file.rb", language="ruby")
<foreign object at ...>
ポリグロット・スコープ全体からグローバル値をインポートできます:
>>> ruby_polyglot = polyglot.import_value("RubyPolyglot")
その後、このグローバル値は予期したとおりに動作します:
- 属性へのアクセスは、
members
ネームスペースから読み取ることを前提としています。>>> ruby_polyglot.to_s <foreign object at ...>
- 結果に対してメソッドをコールすると、直接呼出しが試行され、メンバーの読取りおよびその実行の試行にフォールバックします。
>>> ruby_polyglot.to_s() Polyglot
- アイテムへのアクセスは、文字列と数値の両方でサポートされています。
>>> ruby_polyglot.methods()[10] is not None True
Pythonからサポートされている他の言語にオブジェクトをエクスポートして、他の言語がそれをインポートできるようにすることができます:
>>> foo = object()
>>> polyglot.export_value(value=foo, name="python_foo")
<object object at ...>
>>> jsfoo = polyglot.eval(language="js", string="Polyglot.import('python_foo')")
>>> jsfoo is foo
True
エクスポート関数はデコレータとして使用できます。この場合、関数名がグローバルにエクスポートされた名前として使用されます:
>>> @polyglot.export_value
... def python_method():
... return "Hello from Python!"
JavaScript正規表現エンジンを使用してPython文字列を照合する方法の例を次に示します:
>>> js_re = polyglot.eval(string="RegExp()", language="js")
>>> pattern = js_re.compile(".*(?:we have (?:a )?matching strings?(?:[!\\?] )?)(.*)")
>>> if pattern.exec("This string does not match"):
... raise SystemError("that shouldn't happen")
>>> md = pattern.exec("Look, we have matching strings! This string was matched by Graal.js")
>>> "Here is what we found: '%s'" % md[1]
"Here is what we found: 'This string was matched by Graal.js'"
このプログラムは、JavaScript正規表現オブジェクトを使用してPython文字列を照合します。Pythonは、取得したグループをJavaScriptの結果から読み取って出力します。
より複雑な例として、Rを使用してファイルを読み取り、Pythonでそのデータを処理して、Rを再度使用し、RライブラリとPythonライブラリの両方を組み合せて使用して結果のデータ・イメージを表示する方法を確認します。この例を実行するには、まず、必要なRライブラリをインストールします:
R -e 'install.packages("https://www.rforge.net/src/contrib/jpeg_0.1-8.tar.gz", repos=NULL)'
この例ではさらに、image_magix.pyを使用し、JPEGイメージ入力を操作します(このイメージで試すことができます)。これらのファイルは、次のスクリプトが配置され、実行されるのと同じディレクトリにある必要があります。
import polyglot
import sys
import time
sys.path.insert(0, ".")
from image_magix import Image
load_jpeg = polyglot.eval(string="""function(file.name) {
library(jpeg)
jimg <- readJPEG(file.name)
jimg <- jimg*255
jimg
}""", language="R")
raw_data = load_jpeg("python_demo_picture.jpg")
# the dimensions are R attributes; define function to access them
getDim = polyglot.eval(string="function(v, pos) dim(v)[[pos]]", language="R")
# Create object of Python class 'Image' with loaded JPEG data
image = Image(getDim(raw_data, 2), getDim(raw_data, 1), raw_data)
# Run Sobel filter
result = image.sobel()
draw = polyglot.eval(string="""function(processedImgObj) {
require(grDevices)
require(grid)
mx <- matrix(processedImgObj$`@data`/255, nrow=processedImgObj$`@height`, ncol=processedImgObj$`@width`)
grDevices:::awt()
grid.raster(mx, height=unit(nrow(mx),"points"))
}""", language="R")
draw(result)
time.sleep(10)
Javaホスト相互運用性API
最後に、Javaと相互運用するために(JVM上で実行している場合のみ)、java
モジュールを使用できます:
>>> import java
>>> BigInteger = java.type("java.math.BigInteger")
>>> myBigInt = BigInteger.valueOf(42)
>>> # public Java methods can just be called
>>> myBigInt.shiftLeft(128)
<JavaObject[java.math.BigInteger] at ...>
>>> # Java method names that are keywords in Python can be accessed using `getattr`
>>> getattr(myBigInt, "not")()
<JavaObject[java.math.BigInteger] at ...>
>>> byteArray = myBigInt.toByteArray()
>>> # Java arrays can act like Python lists
>>> list(byteArray)
[42]
java
パッケージの下のパッケージについては、通常のPythonインポート構文を使用することもできます:
>>> import java.util.ArrayList
>>> from java.util import ArrayList
>>>
>>> java.util.ArrayList == ArrayList
True
>>> al = ArrayList()
>>> al.add(1)
True
>>> al.add(12)
True
>>> al
[1, 12]
type
組込みメソッドに加えて、java
モジュールによって次のメソッドも公開されます:
組込み | 仕様 |
---|---|
instanceof(obj, class) |
obj がclass のインスタンスである場合、True を返します(class は外部オブジェクト・クラスである必要があります) |
is_function(obj) |
obj が、Truffle Interopを使用してラップされたJavaホスト言語関数である場合、True を返します |
is_object(obj) |
obj 引数が、Truffle Interopを使用してラップされたJavaホスト言語オブジェクトである場合、True を返します |
is_symbol(obj) |
obj 引数が、java.type によって取得されたJavaクラスのコンストラクタおよび静的メンバーを表すJavaホスト・シンボルである場合、True を返します |
>>> ArrayList = java.type('java.util.ArrayList')
>>> my_list = ArrayList()
>>> java.is_symbol(ArrayList)
True
>>> java.is_symbol(my_list)
False
>>> java.is_object(ArrayList)
True
>>> java.is_function(my_list.add)
True
>>> java.instanceof(my_list, ArrayList)
True
他のプログラミング言語との相互運用性の詳細は、『Polyglot Programming』および『Embed Languages』を参照してください。
型の動作
相互運用性プロトコルは、あらゆる種類の方法で重複し、Pythonとの相互運用に制限を与える可能性のある、様々な「型」を定義します。
相互運用性の型からPythonへ
最も重要で明らかなことは、Pythonに渡されるすべての外部オブジェクトはPython型foreign
を持つということです。つまり、相互運用性のブールであるオブジェクトをPython型bool
にするエミュレーションはありません。これは、相互運用性の型の重複がPythonの組込み型では不可能な場合があり、何が優先されるかは明確ではないためです。かわりに、foreign
型は、インタプリタ全体で使用される型変換のためのすべてのPython特殊メソッド(__add__
、__int__
、__str__
、__getitem__
などのメソッド)を定義します。これらは、相互運用性の型に基づいて適切な処理を試行します(または例外を発生させます)。
次の表に示されていない型は、現時点ではPythonでの特別な解釈はありません。
相互運用性の型 | Pythonの解釈 |
---|---|
Null | これはNoneと似ています。重要事項: 相互運用性のnull値は等価ですが、同一ではありません。これは、JavaScriptが、undefined とnull というnullに似た2つの値(同一ではない)を定義しているために行われました。 |
Boolean | Pythonではすべてのブールは整数でもあるという事実を含め、Pythonのブールと同様に動作します(trueの場合は1、falseの場合は0) |
番号 | Pythonの数値と同様に動作します。Pythonには1つの整数型と1つの浮動小数点型しかありませんが、型付き配列など一部の場所では範囲が考慮されます。 |
String | Pythonの文字列と同様に動作します。 |
Buffer | Bufferは、PythonのネイティブAPIの概念でもあります(若干異なります)。相互運用性のバッファは、データのコピーを回避するために、一部の場所(memoryview など)ではPythonのバッファと同様に扱われます。 |
Array | Arrayは、Pythonのリストのような添字アクセスで、整数およびスライスをインデックスとして使用できます。 |
Hash | Hashは、Pythonのディクショナリのような添字アクセスで、ハッシュ可能な種類のオブジェクトをキーとして使用できます。hashableはPythonセマンティクスに従っており、一般に、アイデンティティを持つすべての相互運用性型はhashableとみなされます。相互運用性オブジェクトがArrayとHashの両方である場合、添字アクセスの動作は未定義です。 |
Members | Membersは、通常のPythonの「.」表記またはgetattr などの関数を使用して読み取ることができます。 |
Iterable | Iterableは、__iter__ メソッドを使用したPythonオブジェクトと同様に扱われます。つまり、ループや、Python反復可能オブジェクトを受け入れるその他の場所で使用できます。 |
Iterator | Iteratorは、__next__ メソッドを使用したPythonオブジェクトと同様に扱われます。 |
Exception | 相互運用性の例外は、一般的なexcept 句で捕捉できます。 |
MetaObject | 相互運用性のメタ・オブジェクトは、subtypeおよびisinstance チェックで使用できます |
Executable | Executableオブジェクトは関数として実行できますが、キーワード引数とともに実行することはできません。 |
Instantiable | Instantiableオブジェクトは、実行可能オブジェクトと同様に動作します(Pythonでの処理方法と同様)。 |
Pythonから相互運用性の型へ
相互運用性の型 | Pythonの解釈 |
---|---|
Null | None のみ。 |
Boolean | Python bool のサブタイプのみ。Pythonセマンティクスとは対照的に、Python bool が同時に相互運用性の数値になることはありません。 |
番号 | int およびfloat のサブタイプのみ。 |
String | str のサブタイプのみ。 |
Array | __getitem__ および__len__ を持つ任意のオブジェクト。ただし、keys 、values およびitems も持っている場合は該当しません(dict と同様)。 |
Hash | dict のサブタイプのみ。 |
Members | 任意のPythonオブジェクト。readable/writableのルールは、Python MOPの一部でないことを確認するため、若干非定型です。 |
Iterable | __iter__ メソッドまたは__getitem__ メソッドを持つ任意のもの。 |
Iterator | __next__ メソッドを持つ任意のもの。 |
Exception | 任意のPython BaseException サブタイプ。 |
MetaObject | 任意のPython type 。 |
Executable | __call__ メソッドを持つ任意のもの。 |
Instantiable | 任意のPython type 。 |