用Scala实现一个简单的Python的上下文管理器

Posted 鸿的学习笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Scala实现一个简单的Python的上下文管理器相关的知识,希望对你有一定的参考价值。

上下文管理器是对try/finally模式的简化,保证一段代码运行完后执行某项操作,即使那段代码被中止了,也会执行指定的操作。在这篇文章将展现函数式编程的威力,用Scala写一个简单的上下文管理器。

简单介绍下Python的with,它是属于上下文管理器协议,使用__enter__和__exit__方法实现协议,在with语句运行之前会调用__enter__方法,结束之后调用__exit__方法。最常见的例子就是关闭文件对象,这次我们也要用Scala实现下面的语法。

with open('D:\\data.txt') as f:
   data = f.read()

f.closed
Out[3]: True

在开始写之前,先了解下柯里化(Currying)的概念。柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数。先看看一个正常的oldSum函数:

scala> def oldSum(x:Int, y:Int) = x + y
oldSum: (x: Int, y: Int)Int

scala> oldSum(1,2)
res0: Int = 3

scala> def curriedSum(x:Int)(y:Int) = x + y
curriedSum: (x: Int)(y: Int)Int

scala> curriedSum(1)(2)
res1: Int = 3

curriedSum是柯里化函数,当在调用curriedSum时,实际上是连着做了两次的函数调用,可以理解为是一个嵌套函数:

scala> def first(x:Int) = (y:Int) => x + y
first: (x: Int)Int => Int

scala> val second = first(1)
second: Int => Int = <function1>

scala> second(2)
res3: Int = 3

虽然实现原理并不一致,但是可以这么理解柯里化的实现过程,并且也可以通过占位符(一个神奇的符号)去缓存第二个函数的使用。

scala> val onePlus = curriedSum(1)_
onePlus: Int => Int = <function1>

scala> onePlus(2)
res4: Int = 3

当了解柯里化后,回忆一下,前文提到了Scala里的函数是一等对象,函数本身是可以当作参数传给函数的:

scala> def twicePlus(plus:Int => Int, x:Int) = plus(plus(x))
twicePlus: (plus: Int => Int, x: Int)Int

scala> twicePlus(_+1,5)
res5: Int = 7

神奇的_符号又来了,_ + 1可以理解为(如果不是老手,_+1并没有下面的容易理解):

scala> val add=(x:Int) => x+ 1
add: Int => Int = <function1>

scala> twicePlus(add,5)
res6: Int = 7

plus:Int => Int,这里的含义是传入一个入参为Int类型的值,返回值是Int类型值函数。除了圆括号,函数的参数传入也能使用{},例如:

scala> println("hello,world")
hello,world

scala> println {"hello,world"}
hello,world

Scala在函数的入参只有一个函数时,允许你使用{}调用函数,不过仅限你的函数只接受一个入参。有了这个铺垫,下面的withFile就容易理解了。withFile是一个柯里化函数,第二个参数列表需要传入一个输入类型为BufferedSource,无返回值的函数,我们借用了{}去模仿Scala的控制结构。一个简单的上下文管理器就实现了。

object withControl{
 def main(args: Array[String]): Unit = {
   val file = Source.fromFile("D:\\data.txt")
   withFile(file) {
     writer => println(writer.length)
   }
 }

 def withFile(file: BufferedSource)(op:  BufferedSource => Unit) = {
   try{
     op(file)
   }
   finally {
     file.close()
   }
 }
}


以上是关于用Scala实现一个简单的Python的上下文管理器的主要内容,如果未能解决你的问题,请参考以下文章

Python上下文管理器

python中,用Redis构建分布式锁

scala和python之间的API兼容性?

Python:contextlib模块——上下文管理器工具

如何用python 中with 用法

用于“使用/尝试使用资源”的简单 Scala 模式(自动资源管理)