- 适用于 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 是使用互操作包装的 Java 宿主语言函数,则返回 True |
is_object(obj) |
如果 obj 是使用互操作包装的 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 脚本中与其他动态语言交互 #
通过 polyglot API 可以实现从 Python 脚本到其他语言的更通用、非 JVM 特定的交互。这包括通过 Truffle 框架 支持的所有动态语言交互,包括 JavaScript 和 Ruby。
安装其他动态语言 #
其他语言可以通过在与 GraalPy 相同的方式使用各自的 Maven 依赖项来包含。例如,如果您已经使用 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 范围导入全局值
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 的交互方式有限制。
互操作类型到 Python #
最重要的是:传递到 Python 的所有外部对象都具有 Python 类型 foreign
。没有(例如)将类型为“boolean”的互操作对象的模拟为具有 Python 类型 bool
。这是因为互操作类型可以以 Python 内置类型无法实现的方式重叠,而且我们还没有定义哪种类型应该优先考虑以及在这种情况下应该优先考虑哪种类型。然而,我们预计将来会改变这一点。目前,foreign
类型定义了用于整个解释器中的类型转换的 Python 所有特殊方法(例如 __add__
、__int__
、__str__
、__getitem__
等等),并且这些方法会根据互操作类型尝试“执行正确的事情”(或者引发异常)。
下面表格中未列出的类型在 Python 中没有特殊解释。
互操作类型 | Python 解释 |
---|---|
null |
null 类似于 None 。重要的是要知道:互操作 null 值与 None 完全相同。JavaScript 定义了两个“类似 null”的值;undefined 和 null ,它们不相同,但当传递给 Python 时,它们会被视为相同。 |
boolean |
boolean 的行为与 Python 布尔值相同,包括 Python 中所有布尔值也是整数(分别为真和假的 1 和 0)这一事实。 |
number |
number 的行为与 Python 数字相同。Python 只有一个整数类型和一个浮点类型,但范围在某些地方导入,例如类型化数组。 |
string |
与 Python 字符串的行为相同。 |
buffer |
缓冲区也是 Python 原生 API 中的一个概念(尽管略有不同)。互操作缓冲区在某些地方(如 memoryview )与 Python 缓冲区以相同的方式处理,以避免数据副本。 |
array |
array 可以使用下标访问,就像 Python 列表一样,使用整数和切片作为索引。 |
hash |
hash 可以使用下标访问,就像 Python 字典一样,使用任何“可散列”对象作为键。“可散列”遵循 Python 语义:通常,每个具有标识的互操作类型都被视为“可散列”。请注意,如果互操作对象是 Array 和 Hash 类型,则下标访问的行为是未定义的。 |
members |
类型为 members 的对象可以使用传统的 Python . 符号或 getattr 及其相关函数读取。 |
iterable |
iterable 的处理方式与任何具有 __iter__ 方法的 Python 对象相同。也就是说,它可以在循环和其他接受 Python 可迭代对象的地方使用。 |
iterator |
iterator 的处理方式与任何具有 __next__ 方法的 Python 对象相同。 |
exception |
exception 可以在通用的 except 子句中捕获。 |
MetaObject |
元对象可以在子类型和 isinstance 检查中使用。 |
executable |
executable 对象可以作为函数执行,但永远不会使用关键字参数执行。 |
instantiable |
instantiable 对象可以像 Python 类型一样调用,但永远不会使用关键字参数调用。 |
Python 到互操作类型 #
互操作类型 | 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* 命名约定,即 interop fitsInLong
消息变为 fits_in_long
,依此类推。每个消息都可以扩展为 **纯 Python 函数**(不允许默认关键字参数、自由变量和单元变量)或 **布尔常量**。下表描述了支持的 interop 消息。
消息 | 扩展参数名称 | 预期返回类型 |
---|---|---|
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 可用于为现有类型注册 interop 行为。
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
装饰器可能更方便。Interop 消息扩展是通过装饰类的 **静态** 方法实现的。静态方法的名称与 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;
}
}
}