使用Graalvm Polyglot与Scala和Python进行运算符重载

bqf10yzr  于 5个月前  发布在  Scala
关注(0)|答案(1)|浏览(77)

我正在尝试Graalvm非常酷的Polyglot特性,这样我就可以从Scala应用程序中评估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'
下面是测试REPL代码:

import org.graalvm.polyglot.{Context, Source}

import scala.annotation.targetName
import scala.util.control.NonFatal

case class Value(v: Int) {
  @targetName("__add__")
  def +(i: Int): Value = Value(v + i)
}

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)
    }
  }
}

aiqt4smr

aiqt4smr1#

在这一点上,GraalPy不允许像这样覆盖Python的“魔法”方法。“Foreign”对象获取类“foreign”,它以一种方式defines一些“魔法”方法,它们调用适当的低级Truffle interop messages。你可以看到它们像这样:

>>> dir(type(x))
['__add__', '__and__', '__bases__', '__bool__', '__call__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__divmod__', '__doc__', '__eq__', '__floordiv__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__instancecheck__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__next__', '__or__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmul__', '__ror__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__truffle_richcompare__', '__xor__']

字符串
问题是你的对象的Python类型不是Java/Scala类,而是“foreign”。你定义的方法和字段是属于对象示例的Python属性,但是像+这样的操作的“魔术”方法是在type =>“foreign”类型上查找的。参见https://github.com/oracle/graalpython/issues/249
目前,虽然你可以在Scalar/Java端实现较低级别的Truffle互操作消息,但我建议你尝试从“另一个”端来实现。你可以定义Python类来 Package Scala/Java对象,并适当地实现“神奇”方法:

class ScalaWrapper:
    def __init__(self): self.delaget = delegate
    def __add__(self, other): delegate.__add__(other)


你可以从Scala/Java中按照以下沿着示例化它:

Value wrapperInstance = context.getBindings("python").getMember("ScalaWrapper").newInstance(myScalaObjToBeWrapped);


也可以在Python中示例化它。

相关问题