基于GraalVM Polyglot的Scala与Python运算符重载问题
问题:GraalVM Polyglot中Scala对象无法在Python中使用
+运算符 我在Scala应用中使用GraalVM的Polyglot特性执行Python代码,测试REPL中v.__add__(1)能正常返回结果:
>> v
evaluating v
evaluated got Value(7)
v.add(1)
evaluating v.add(1)
evaluated got Value(8)
但执行`v + 1`时会报错`TypeError: unsupported operand type(s) for +: 'foreign' and 'int'`,如何实现`v + 1`的正常执行? --- # 解决方案 仅通过`@targetName("__add__")`标注Scala方法无法让GraalVM的Python自动识别运算符重载,需要让Scala对象实现GraalVM的`Proxy`接口,主动暴露Python期望的特殊方法协议。 ### 修改后的Scala代码 ```scala import org.graalvm.polyglot.{Context, Source, Value} import org.graalvm.polyglot.proxy.Proxy import scala.annotation.targetName import scala.util.control.NonFatal case class Value(v: Int) extends Proxy { @targetName("__add__") def +(i: Int): Value = Value(v + i) // 实现Proxy接口,让Python能正确获取__add__方法 override def getMember(key: String): AnyRef = { key match { case "__add__" => (args: Array[AnyRef]) => args.head match { case num: Int => this + num case num: Value => Value(this.v + num.v) case _ => throw new IllegalArgumentException("Unsupported type for +") } case _ => super.getMember(key) } } override def hasMember(key: String): Boolean = { key == "__add__" || super.hasMember(key) } } class Python(context: Context, v: Value) { private val language = "python" context.getBindings(language).putMember("v", v) def eval(code: String): AnyRef = { try { val source = Source .newBuilder(language, code, "<shell>") .interactive(false) .buildLiteral() println(s"evaluating $code") val res = context.eval(source) println(s"evaluated got $res") res } catch { case NonFatal(e) => println(s"error evaluating $code") e.printStackTrace() null } } } object Python { def main(args: Array[String]): Unit = { val context = Context .newBuilder("python") .allowAllAccess(true) .build() val python = new Python(context, Value(7)) while (true) { val line = scala.io.StdIn.readLine(">>> ") python.eval(line) } } }
关键说明
- 让
Value类实现Proxy接口后,Python在解析v + 1时会自动查找__add__方法,此时getMember会返回我们定义的加法逻辑; - 扩展了支持
Value对象之间相加的场景,增强通用性; hasMember方法明确告知Python__add__方法存在,确保运算符解析流程正常触发。
修改后执行v + 1即可正常得到Value(8)的结果。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

