Scala中的Python样式装饰器

时间:2015-03-27 22:18:16

标签: python scala decorator

在Python中,我可以这样做:

def wrap(f):
    def wrapper(*args, **kwargs):
        print "args: ", args, kwargs
        res = f(*args, **kwargs)
        print "result: ", res
        return res
    return wrapper

这让我可以包装任何函数,无论它们采用什么参数。例如:

In [8]: def f(thing):
    print "in f:", thing
    return 3

In [9]: wrapped_f = wrap(f)

In [10]: wrapped_f(2)
args:  (2,) {}
in f: 2
result:  3
Out[10]: 3

在Scala中有没有办法做类似的事情(编写一个可以应用于任何函数而不管其输入/输出类型的包装器)?

2 个答案:

答案 0 :(得分:2)

这里有一个基本问题:在Scala中你必须知道函数应该得到什么参数并实际传递它们,以便编译器可以确保类型匹配。

说有def f(a: List[Int], b: String) = ...def g(args: Any*) = f(args)。这不会编译! (Any*表示任何类型的任何数量的对象)。问题是Any*仍然只是一个单独的参数,实际上被转换为一种Array

为了使这一点更加清晰,您可以想到一个示例情况:您已使用某个函数wrap(f)调用f(a: String, b: String)。然后你有了包装器的输出,它会以某种方式接受任何数量的任何参数,你可以调用wrapper_f(List(1), "a")。在这种情况下,wrapper_f(...)调用应该是正确的,但在包装器内部,包装函数具有完全不同的参数列表,该列表不能接受List[Int]String。因此,您将在运行时获得“类型错误”,这应该(通常)在静态类型编程语言中(或者至少在Scala中)是不可能的。

答案 1 :(得分:2)

你当然可以用宏来做这件事。您可以将方法调用转换为具有部分应用程序的函数:

object Foo {
  def bar(i: Int): Int = i + 1
}

val fn = Foo.bar _

defined object Foo
fn: Int => Int = <function1>

现在你有一个对象,在这种类型为Function1[Int, Int]的情况下,你可以将它传递给Scala宏,就像这样(未经测试):

object DecoratorMacros {

  import reflect.macros.blackbox
  import language.experimental.macros

  def decorate[A <: Function](fn: A): [A] = macro decorate_impl[A]

  def decorate_impl[A: c.WeakTypeTag](c: blackbox.Context) = {
    import c.universe._
    val type = weakTypeOf[A]
    ...
  }
}

在宏的主体中,您可以检查fn: A的整个类型签名,其中包含参数。然后,您可以编写代码来执行所需的副作用,并返回一个可以调用的函数。像这样:

DecoratorMacros.decorate(Foo.bar _)(42)

宏很复杂,但如果你认为这是一条你想要走下去的道路,我可以详细说明。