- 面向 JDK 23 的 GraalVM(最新)
- 面向 JDK 24 的 GraalVM(抢先体验)
- 面向 JDK 21 的 GraalVM
- 面向 JDK 17 的 GraalVM
- 存档
- 开发版本
面向 JVM 的现代 Python
对于 Python 2 版本(现已停用),Jython 是连接 Python 和 Java 的事实标准方法。大多数使用 Java 集成的现有 Jython 代码将基于稳定的 Jython 版本,但是,这些版本仅在 Python 2.x 版本中可用。相比之下,GraalPy 与 Python 3.x 兼容,并且不提供与早期 2.x 版本的 Jython 的完全兼容性。
要将代码从 Python 2 迁移到 Python 3,请遵循来自 Python 社区的官方指南。一旦您的 Jython 代码与 Python 3 兼容,请遵循本指南以解决 GraalPy 和 Jython 之间的其他差异。
GraalPy 对Java 互操作性 的一流支持使从 Python 使用 Java 库变得尽可能容易,并为 Java 代码提供了超出其他 Graal 语言(在Truffle 框架 上实现的语言)的通用互操作性支持的特殊功能。
并非 Jython 的所有功能都在 GraalPy 上受支持。一些功能是受支持的,但默认情况下已禁用,因为它们会对运行时性能产生负面影响。在迁移期间,您可以使用命令行选项:--python.EmulateJython
启用这些功能。但是,我们建议您迁移到这些功能之外,以实现最佳性能。
迁移 Jython 脚本 #
要将简单的 Jython 脚本从 Jython 迁移到 GraalPy,请使用基于 GraalPy JVM 的运行时。(有关更多信息,请参阅可用的GraalPy 发行版)。
导入 Java 包 #
Jython 的 Java 集成的某些功能默认情况下在 GraalPy 上已启用。以下是一个示例
>>> import java.awt as awt
>>> win = awt.Frame()
>>> win.setSize(200, 200)
>>> win.setTitle("Hello from Python!")
>>> win.getSize().toString()
'java.awt.Dimension[width=200,height=200]'
>>> win.show()
此示例在 Jython 和 GraalPy 上运行时产生相同的结果。但是,当在 GraalPy 上运行示例时,只能直接导入 java
命名空间中的包。要导入 java
命名空间之外的包中的类,请使用 --python.EmulateJython
选项。
注意:将 GraalPy 嵌入模块化应用程序时,您可能需要根据 JSR 376 添加对所需模块的导出。
此外,并非在所有情况下都可以将 Java 包导入为 Python 模块。例如,这将起作用
import java.lang as lang
但是,这将不起作用
import javax.swing as swing
from javax.swing import *
而是直接导入其中一个类
import javax.swing.Window as Window
基本对象使用 #
使用传统的 Python 语法来构造和使用 Java 对象和类。Java 对象的方法也可以作为一等公民对象(绑定到其实例)检索和引用,与 Python 方法相同。例如
>>> from java.util import Random
>>> rg = Random(99)
>>> rg.nextInt()
1491444859
>>> boundNextInt = rg.nextInt
>>> boundNextInt()
1672896916
Java 到 Python 类型:自动转换 #
方法重载是通过将 Python 参数以最佳方式匹配到可用参数类型来解析的。在转换数据时也采用这种方法。这里的目标是使从 Python 使用 Java 尽可能顺利。GraalPy 采用的匹配方法类似于 Jython,但 GraalPy 使用更动态的方法来匹配——模拟 int
或 float
的 Python 类型也被转换为相应的 Java 类型。这使您能够,例如,在元素适合这些 Java 基本类型时,将 Pandas 框架用作 double[][]
或 NumPy 数组元素用作 int[]
。
Java 类型 | Python 类型 |
---|---|
null |
None |
boolean |
bool |
byte , short , int , long |
int , 任何具有 __int__ 方法的对象 |
float |
float , 任何具有 __float__ 方法的对象 |
char |
长度为 1 的 str |
java.lang.String |
str |
byte[] |
bytes , bytearray , 包装的 Java array , 仅包含相应类型的 Python 列表 |
Java 数组 | 包装的 Java array 或仅包含相应类型的 Python list |
Java 对象 | 相应类型的包装的 Java 对象 |
java.lang.Object |
任何对象 |
特殊 Jython 模块:jarray
#
GraalPy 实现 jarray
模块(用于创建基本 Java 数组)以实现兼容性。例如
>>> import jarray
>>> jarray.array([1,2,3], 'i')
请注意,它的使用等效于使用 java.type
函数构造数组类型,然后填充数组,如下所示
>>> import java
>>> java.type("int[]")(10)
创建 Java 数组的代码也可以使用 Python 类型。但是,隐式地,这可能会产生数组数据的副本,这在使用 Java 数组作为输出参数时可能会具有欺骗性
>>> i = java.io.ByteArrayInputStream(b"foobar")
>>> buf = [0, 0, 0]
>>> i.read(buf) # buf is automatically converted to a byte[] array
3
>>> buf
[0, 0, 0] # the converted byte[] array is lost
>>> jbuf = java.type("byte[]")(3)
>>> i.read(jbuf)
3
>>> jbuf
[98, 97, 122]
来自 Java 的异常 #
要捕获 Java 异常,请使用 --python.EmulateJython
选项。
注意:捕获 Java 异常会产生性能损失。
例如
>>> import java
>>> v = java.util.Vector()
>>> try:
... x = v.elementAt(7)
... except java.lang.ArrayIndexOutOfBoundsException as e:
... print(e.getMessage())
...
7 >= 0
Java 集合 #
-
实现
java.util.Collection
接口的 Java 数组和集合可以使用[]
语法访问。空集合在布尔转换中被认为是false
。集合的长度通过len
内置函数公开。例如>>> from java.util import ArrayList >>> l = ArrayList() >>> l.add("foo") True >>> l.add("baz") True >>> l[0] 'foo' >>> l[1] = "bar" >>> del l[1] >>> len(l) 1 >>> bool(l) True >>> del l[0] >>> bool(l) False
-
实现
java.lang.Iterable
接口的 Java 可迭代对象可以使用for
循环或iter
内置函数进行迭代,并且被期望可迭代对象的所有内置函数接受。例如>>> [x for x in l] ['foo', 'bar'] >>> i = iter(l) >>> next(i) 'foo' >>> next(i) 'bar' >>> next(i) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> set(l) {'foo', 'bar'}
-
迭代器也可以迭代。例如
>>> from java.util import ArrayList >>> l = ArrayList() >>> l.add("foo") True >>> i = l.iterator() # Calls the Java iterator methods >>> next(i) 'foo'
-
实现
java.util.Map
接口的映射集合可以使用[]
符号访问。空映射在布尔转换中被认为是false
。映射的迭代将产生其键,与dict
一致。例如>>> from java.util import HashMap >>> m = HashMap() >>> m['foo'] = 5 >>> m['foo'] 5 >>> m['bar'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: bar >>> [k for k in m] ['foo'] >>> bool(m) True >>> del m['foo'] >>> bool(m) False
从 Java 继承 #
从 Java 类(或实现 Java 接口)继承在语法上与 Jython 存在一些差异。要创建一个从 Java 类继承(或实现 Java 接口)的类,请使用传统的 Python class
语句:当声明的方法名称匹配时,声明的方法会覆盖(实现)超类(接口)方法。要调用超类方法,请使用特殊属性 self.__super__
。创建的对象的行为不像 Python 对象,而是像外部 Java 对象一样。可以使用其 this
属性访问其 Python 级别的成员。例如
import atexit
from java.util.logging import Logger, Handler
class MyHandler(Handler):
def __init__(self):
self.logged = []
def publish(self, record):
self.logged.append(record)
logger = Logger.getLogger("mylog")
logger.setUseParentHandlers(False)
handler = MyHandler()
logger.addHandler(handler)
# Make sure the handler is not used after the Python context has been closed
atexit.register(lambda: logger.removeHandler(handler))
logger.info("Hi")
logger.warning("Bye")
# The python attributes/methods of the object are accessed through 'this' attribute
for record in handler.this.logged:
print(f'Python captured message "{record.getMessage()}" at level {record.getLevel().getName()}')
将 Python 嵌入 Java #
使用 Jython 的另一种方法是将其嵌入到 Java 应用程序中。有两种选择可以进行这种嵌入。
-
使用 Jython 提供的
PythonInterpreter
对象。以这种方式使用 Jython 的现有代码直接依赖于 Jython 包(例如,在 Maven 配置中),因为 Java 代码对 Jython 内部类有引用。这些类在 GraalVM 中不存在,并且没有公开等效的类。要从这种用法迁移,请切换到GraalVM SDK。使用此 SDK,不会公开特定于 Python 的任何 API,所有操作都通过 GraalVM API 完成,并具有对 Python 运行时的最大可配置性。请参阅入门 文档以准备设置。 -
通过JSR 223 将 Jython 嵌入 Java,方法是使用
javax.script
包的类,特别是通过ScriptEngine
类。我们不推荐这种方法,因为ScriptEngine
API 不适合 GraalPy 的选项和功能。但是,为了迁移现有代码,我们提供了一个示例 ScriptEngine 实现,您可以将其内联到您的项目中。有关详细信息,请参阅嵌入参考手册。