如何使用 Py4J 在 Python 中实例化 Scala case 对象?

How can I instantiate a Scala case object in Python using Py4J?

我有一个这样定义的 Scala case 对象:

object DurationUnitsOfMeasure {
  sealed abstract class DurationUnitOfMeasure(val name : String)
  {
    override def toString : String = name
    lazy val initial: Char = name.charAt(2).toLower
  }
  case object Day extends DurationUnitOfMeasure("__DAY__")
  case object Week extends DurationUnitOfMeasure("__WEEK__")
  case object Month extends DurationUnitOfMeasure("__MONTH__")

  val durationUnitsOfMeasure : Seq[DurationUnitOfMeasure] = Seq(Day, Week, Month)
}

我正在编写的一些代码使用它来与 Spark 交互。我还想与来自 Python 的代码进行交互,我已经使用 Py4J 成功完成了这些代码,但是我现在正处于我想要从 Python/PySpark 实例化该 case 对象的实例的地步,我可以'不知道该怎么做。

我在 https://github.com/awslabs/deequ/issues/109#issuecomment-504220206 找到了一个有用的参考,它教我使用 javap 找到 DurationUnitsOfMeasure

的 class 结构
$ javap -classpath ../target/scala-2.11/foo_2.11-0.1-SNAPSHOT.jar com/package/DurationUnitsOfMeasure
Compiled from "File.scala"
public final class com.package.DurationUnitsOfMeasure {
  public static scala.collection.Seq<com.package.DurationUnitsOfMeasure$DurationUnitOfMeasure> durationUnitsOfMeasure();
}

这反过来又促使我编写了这个 python 代码:

# self.spark is an instance of SparkSession
jDurationsUnitsOfMeasure = getattr(
            self.spark._sc._jvm.com.package.DurationUnitsOfMeasure,
            "durationUnitsOfMeasure")

jDurationsUnitsOfMeasure 是一个 <py4j.java_gateway.JavaMember object at 0x7fc0dbb14850,我可以使用通常的 python 方法来查询,例如 dir():

(Pdb) dir(jDurationsUnitsOfMeasure)
['call', 'class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', '_build_args', '_gateway_doc', '_get_args', 'command_header', 'container', 'converters', 'gateway_client', 'name', 'pool', 'stream', 'target_id']

但我不知道如何做我想做的事情,即实例化 DurationUnitsOfMeasure.Day 的实例。我试过这个:

jDurationsUnitsOfMeasureDay = getattr(
            self.spark._sc._jvm.com.package.DurationUnitsOfMeasure,
            "durationUnitsOfMeasure$Day")

但这只是被错误炸毁了:

py4j.protocol.Py4JError: com.package.DurationUnitsOfMeasure.durationUnitsOfMeasure$Day does not exist in the JVM

感觉自己离Python实例化DurationUnitsOfMeasure.Day已经不远了,但是还没解决。任何建议将不胜感激。

原来我把它复杂化了。这有效:

jDurationUnitsOfMeasure = self.spark._sc._jvm.scala.collection.JavaConversions.seqAsJavaList(
 self.spark._sc._jvm.com.package.DurationUnitsOfMeasure.durationUnitsOfMeasure())

那个 returns 一个 py4j.java_collections.JavaList 存在,因此可以将其视为一个好的 ol' Python 列表,因此我可以像处理任何其他列表一样操作它 Python 列表(我更喜欢列表理解)。