- 適用於 JDK 23 的 GraalVM (最新)
- 適用於 JDK 24 的 GraalVM (搶先體驗)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發版本
互通性
除了主要建議在您的 Java 應用程式中使用之外,GraalPy 還可以與其他 Graal 語言(在 Truffle 框架上實作的語言)互通。這表示您可以直接從您的 Python 腳本中使用這些其他語言提供的物件和函式。
從 Python 腳本與 Java 互動 #
Java 是 JVM 的主機語言,並執行 GraalPy 直譯器本身。若要從 Python 腳本與 Java 互通,請使用 java
模組
import java
BigInteger = java.type("java.math.BigInteger")
myBigInt = BigInteger.valueOf(42)
# a public Java methods can just be called
myBigInt.shiftLeft(128) # returns a <JavaObject[java.math.BigInteger] at ...>
# Java method names that are keywords in Python must be accessed using `getattr`
getattr(myBigInt, "not")() # returns a <JavaObject[java.math.BigInteger] at ...>
byteArray = myBigInt.toByteArray()
# Java arrays can act like Python lists
assert len(byteArray) == 1 and byteArray[0] == 42
若要從 java
命名空間匯入套件,您也可以使用傳統的 Python 匯入語法
import java.util.ArrayList
from java.util import ArrayList
assert java.util.ArrayList == ArrayList
al = ArrayList()
al.add(1)
al.add(12)
assert list(al) == [1, 12]
除了 type
內建方法之外,java
模組還公開以下方法
內建 | 規格 |
---|---|
instanceof(obj, class) |
如果 obj 是 class 的執行個體,則傳回 True (class 必須是外部物件類別) |
is_function(obj) |
如果 obj 是使用 interop 包裝的 Java 主機語言函式,則傳回 True |
is_object(obj) |
如果 obj 是使用 interop 包裝的 Java 主機語言物件,則傳回 True |
is_symbol(obj) |
如果 obj 是 Java 主機符號,表示 Java 類別的建構函式和靜態成員 (如 java.type 取得的),則傳回 True |
ArrayList = java.type('java.util.ArrayList')
my_list = ArrayList()
assert java.is_symbol(ArrayList)
assert not java.is_symbol(my_list)
assert java.is_object(ArrayList)
assert java.is_function(my_list.add)
assert java.instanceof(my_list, ArrayList)
如需有關與其他程式設計語言互通的詳細資訊,請參閱 多語言程式設計 和 嵌入語言。
從 Python 腳本與其他動態語言互動 #
更一般而言,從 Python 腳本與其他語言進行非 JVM 特定互動是透過 polyglot API 來完成的。這包括透過 Truffle 框架 支援的所有與動態語言的互動,包括 JavaScript 和 Ruby。
安裝其他動態語言 #
可以透過使用它們各自的 Maven 相依性來包含其他語言,方法與 GraalPy 相同。例如,如果您已經使用 GraalPy 設定了 Maven 專案,請新增以下相依性以取得 JavaScript 的存取權
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js</artifactId>
<version>24.1.0</version>
</dependency>
範例 #
- 匯入
polyglot
模組以與其他語言互動import polyglot
- 評估另一種語言的內嵌程式碼
assert polyglot.eval(string="1 + 1", language="js") == 2
- 評估檔案中的程式碼
with open("./my_js_file.js", "w") as f: f.write("Polyglot.export('JSMath', Math)") polyglot.eval(path="./my_js_file.js", language="js")
- 從 polyglot 範圍匯入 glocal 值
Math = polyglot.import_value("JSMath")
此全域值應如預期般運作
- 存取屬性會從 polyglot 成員命名空間讀取
assert Math.E == 2.718281828459045
- 在結果上呼叫方法會嘗試執行直接的
invoke
,並會回復為讀取成員並嘗試執行它。assert Math.toString() == "[object Math]"
- 支援使用字串和數字存取項目。
assert Math["PI"] == 3.141592653589793
- 存取屬性會從 polyglot 成員命名空間讀取
- 使用 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") assert "Graal.js" in md[1]
此程式使用 JavaScript 正規表示式物件比對 Python 字串。Python 從 JavaScript 結果讀取擷取的群組,並檢查其中是否有子字串。
將 Python 物件匯出至其他語言 #
可以使用 polyglot
模組將 Python 物件公開給 JVM 語言和其他 Graal 語言(在 Truffle 框架上實作的語言)。
- 您可以從 Python 將一些物件匯出到其他語言,以便它們可以匯入它
import ssl polyglot.export_value(value=ssl, name="python_ssl")
然後在 (例如) 從 JavaScript 程式碼中使用它
Polyglot.import('python_ssl).get_server_certificate(["oracle.com", 443])
- 您可以裝飾 Python 函式以按名稱匯出它
@polyglot.export_value def python_method(): return "Hello from Python!"
然後 (例如) 從 Java 程式碼中使用它
import org.graalvm.polyglot.*; class Main { public static void main(String[] args) { try (var context = Context.create()) { context.eval(Source.newBuilder("python", "file:///python_script.py").build()); String result = context. getPolyglotBindings(). getMember("python_method"). execute(). asString(); assert result.equals("Hello from Python!"); } } }
Python 與其他語言之間的類型對應 #
互通協定定義了不同的「類型」,這些類型可以以各種方式重疊,並對它們如何與 Python 互動有著限制。
Interop 類型到 Python #
最重要且最先說明的是:傳遞到 Python 的所有外部物件都具有 Python 類型 foreign
。沒有將 (例如) 屬於互通類型「布林值」的物件模擬為具有 Python 類型 bool
。這是因為互通類型可以以 Python 內建類型無法做到的方式重疊,而且我們尚未定義哪個類型應優先處理這種情況。不過,我們預期未來會改變這一點。目前,foreign
類型定義了所有 Python 特殊方法,用於在直譯器中使用的類型轉換 (例如 __add__
、__int__
、__str__
、__getitem__
等方法),並且這些方法會嘗試根據互通類型「正確執行」(或引發例外狀況)。
下表中未列出的類型在 Python 中沒有特殊的解釋。
Interop 類型 | Python 解釋 |
---|---|
null |
null 就像 None 。請務必知道:互通 null 值都與 None 相同。JavaScript 定義兩個「類似 null」的值:undefined 和 null ,它們 不 相同,但傳遞到 Python 時,它們會被視為相同。 |
boolean |
boolean 的行為與 Python 布林值類似,包括在 Python 中,所有布林值也是整數 (true 為 1,false 為 0)。 |
number |
number 的行為與 Python 數字類似。Python 只有一個整數和一個浮點類型,但範圍會以某些方式匯入,例如類型化的陣列。 |
string |
行為與 Python 字串相同。 |
buffer |
緩衝區也是 Python 原生 API 中的概念 (儘管略有不同)。在某些情況下 (例如 memoryview ),互通緩衝區的處理方式與 Python 緩衝區相同,以避免資料複本。 |
array |
array 可以與 Python 清單相同的方式使用下標存取,並使用整數和切片作為索引。 |
hash |
hash 可以與 Python 字典相同的方式使用下標存取,並使用任何「可雜湊」的物件作為索引。「可雜湊」遵循 Python 語意:一般而言,每個具有身分識別的互通類型都被視為「可雜湊」。請注意,如果互通物件屬於 Array 和 Hash 類型,則下標存取的行為未定義。 |
members |
可以使用傳統的 Python . 標記法或 getattr 和相關函式讀取 members 類型的物件。 |
iterable |
iterable 的處理方式與任何具有 __iter__ 方法的 Python 物件相同。也就是說,它可以循環使用,以及在其他接受 Python 可疊代項目的位置中使用。 |
iterator |
iterator 的處理方式與任何具有 __next__ 方法的 Python 物件相同。 |
exception |
可以在泛型的 except 子句中攔截 exception 。 |
MetaObject |
元物件可以用於子類型和 isinstance 檢查。 |
executable |
executable 物件可以作為函式執行,但永遠不能使用關鍵字引數。 |
instantiable |
instantiable 物件可以像 Python 類型一樣呼叫,但永遠不能使用關鍵字引數。 |
Python 到 Interop 類型 #
Interop 類型 | Python 解釋 |
---|---|
null |
只有 None 。 |
boolean |
只有 Python bool 的子類型。請注意,與 Python 語意相反,Python bool 絕不 同時也是互通數字。 |
number |
只有 int 和 float 的子類型。 |
string |
只有 str 的子類型。 |
array |
任何具有 __getitem__ 和 __len__ 方法的物件,但如果它同時具有 keys 、values 和 items 方法 (與 dict 相同),則不會。 |
hash |
只有 dict 的子類型。 |
members |
任何 Python 物件。請注意,可讀/可寫的規則有點臨時,因為檢查它並不是 Python MOP 的一部分。 |
iterable |
任何具有 __iter__ 或 __getitem__ 方法的 Python 物件。 |
iterator |
任何具有 __next__ 方法的 Python 物件。 |
exception |
任何 Python BaseException 子類型。 |
MetaObject |
任何 Python type 。 |
executable |
任何具有 __call__ 方法的 Python 物件。 |
instantiable |
任何 Python type 。 |
互通性擴充 API #
可以透過 polyglot
模組中定義的簡單 API 直接從 Python 擴充互通協定。此 API 的目的是讓自訂/使用者定義類型能夠參與互通生態系統。這對於預設與互通協定不相容的外部類型特別有用。這方面的一個範例是 numpy
數值類型 (例如 numpy.int32
),互通協定預設不支援這些類型。
API #
函式 | 描述 |
---|---|
register_interop_behavior | 以接收器類型作為第一個引數。其餘的關鍵字引數對應於各自的互通訊息。並非所有互通訊息都受到支援。 |
get_registered_interop_behavior | 以接收器類型作為第一個引數。傳回給定類型的擴充互通訊息清單。 |
@interop_behavior | 類別裝飾器,僅以接收器類型作為引數。互通訊息是透過已裝飾類別 (供應商) 中定義的靜態方法來擴充。 |
支援的訊息
大多數(有一些例外)的互通訊息都由互通行為擴展 API 支援,如下表所示。register_interop_behavior
關鍵字引數的命名慣例遵循 snake_case 命名慣例,例如,互通訊息 fitsInLong
會變成 fits_in_long
,以此類推。每個訊息都可以使用一個純 Python 函式(不允許預設關鍵字引數、自由變數和 cell 變數)或一個布林常數來擴展。下表描述了支援的互通訊息
訊息 | 擴展引數名稱 | 預期回傳類型 |
---|---|---|
isBoolean | is_boolean | bool |
isDate | is_date | bool |
isDuration | is_duration | bool |
isIterator | is_iterator | bool |
isNumber | is_number | bool |
isString | is_string | bool |
isTime | is_time | bool |
isTimeZone | is_time_zone | bool |
isExecutable | is_executable | bool |
fitsInBigInteger | fits_in_big_integer | bool |
fitsInByte | fits_in_byte | bool |
fitsInDouble | fits_in_double | bool |
fitsInFloat | fits_in_float | bool |
fitsInInt | fits_in_int | bool |
fitsInLong | fits_in_long | bool |
fitsInShort | fits_in_short | bool |
asBigInteger | as_big_integer | int |
asBoolean | as_boolean | bool |
asByte | as_byte | int |
asDate | as_date | 包含以下元素的 3 元組:(year : int, month : int, day : int) |
asDouble | as_double | float |
asDuration | as_duration | 包含以下元素的 2 元組:(seconds : long, nano_adjustment : long) |
asFloat | as_float | float |
asInt | as_int | int |
asLong | as_long | int |
asShort | as_short | int |
asString | as_string | str |
asTime | as_time | 包含以下元素的 4 元組:(hour : int, minute : int, second : int, microsecond : int) |
asTimeZone | as_time_zone | 字串(時區)或整數(以秒為單位的 UTC 時差) |
execute | execute | object |
readArrayElement | read_array_element | object |
getArraySize | get_array_size | int |
hasArrayElements | has_array_elements | bool |
isArrayElementReadable | is_array_element_readable | bool |
isArrayElementModifiable | is_array_element_modifiable | bool |
isArrayElementInsertable | is_array_element_insertable | bool |
isArrayElementRemovable | is_array_element_removable | bool |
removeArrayElement | remove_array_element | NoneType |
writeArrayElement | write_array_element | NoneType |
hasIterator | has_iterator | bool |
hasIteratorNextElement | has_iterator_next_element | bool |
getIterator | get_iterator | 一個 Python 迭代器 |
getIteratorNextElement | get_iterator_next_element | object |
hasHashEntries | has_hash_entries | bool |
getHashEntriesIterator | get_hash_entries_iterator | 一個 Python 迭代器 |
getHashKeysIterator | get_hash_keys_iterator | 一個 Python 迭代器 |
getHashSize | get_hash_size | int |
getHashValuesIterator | get_hash_values_iterator | 一個 Python 迭代器 |
isHashEntryReadable | is_hash_entry_readable | bool |
isHashEntryModifiable | is_hash_entry_modifiable | bool |
isHashEntryInsertable | is_hash_entry_insertable | bool |
isHashEntryRemovable | is_hash_entry_removable | bool |
readHashValue | read_hash_value | object |
writeHashEntry | write_hash_entry | NoneType |
removeHashEntry | remove_hash_entry | NoneType |
使用範例 #
有一個簡單的 register_interop_behavior
API 可用於為現有類型註冊互通行為
import polyglot
import numpy
polyglot.register_interop_behavior(numpy.int32,
is_number=True,
fitsInByte=lambda v: -128 <= v < 128,
fitsInShort=lambda v: -0x8000 <= v < 0x8000
fitsInInt=True,
fitsInLong=True,
fitsInBigInteger=True,
asByte=int,
asShort=int,
asInt=int,
asLong=int,
asBigInteger=int,
)
當宣告更多行為時,@interop_behavior
裝飾器可能更方便。互通訊息擴展是透過裝飾類的靜態方法實現的。靜態方法的名稱與 register_interop_behavior
預期的關鍵字名稱相同。
from polyglot import interop_behavior
import numpy
@interop_behavior(numpy.float64)
class Int8InteropBehaviorSupplier:
@staticmethod
def is_number(_):
return True
@staticmethod
def fitsInDouble(_):
return True
@staticmethod
def asDouble(v):
return float(v)
然後,這兩個類別在嵌入時可以如預期般運作
import java.nio.file.Files;
import java.nio.file.Path;
import org.graalvm.polyglot.Context;
class Main {
public static void main(String[] args) {
try (var context = Context.create()) {
context.eval("python", Files.readString(Path.of("path/to/interop/behavior/script.py")));
assert context.eval("python", "numpy.float64(12)").asDouble() == 12.0;
assert context.eval("python", "numpy.int32(12)").asByte() == 12;
}
}
}