Ruby中等效于Python的"with"语句的是什么?
在Python中,with
语句的作用是确保一些清理的代码总是会被执行,不管有没有出现错误或者函数是否正常返回。例如:
with open("temp.txt", "w") as f:
f.write("hi")
raise ValueError("spitespite")
在这个例子中,即使出现了错误,文件也会被关闭。想要更详细的解释,可以查看这里。
在Ruby中有没有类似的用法?或者你能自己写一个吗,因为Ruby支持继续执行的功能?
7 个回答
4
你可以在Ruby中使用块参数来实现这个功能:
class Object
def with(obj)
obj.__enter__
yield
obj.__exit__
end
end
现在,你可以在另一个类中添加 __enter__
和 __exit__
方法,然后像这样使用它:
with GetSomeObject("somefile.text") do |foo|
do_something_with(foo)
end
12
在Ruby中,相当于这个操作的是把一个代码块传递给File.open方法。
File.open(...) do |file|
#do stuff with file
end #file is closed
这是Ruby使用的一种写法,你应该慢慢熟悉它。
25
Ruby 语言对匿名过程(在 Ruby 中称为 块)的支持非常简单明了。因此,它不需要新增的语言特性来实现这一点。
通常,你会写一个方法,这个方法接收一段代码块,分配资源,在这个资源的上下文中执行代码块,然后关闭资源。
大概是这样的:
def with(klass, *args)
yield r = klass.open(*args)
ensure
r.close
end
你可以这样使用它:
with File, 'temp.txt', 'w' do |f|
f.write 'hi'
raise 'spitespite'
end
不过,这种做法比较程序化。Ruby 是一种面向对象的语言,这意味着在 File
的上下文中正确执行代码块的责任应该由 File
类来承担:
File.open 'temp.txt', 'w' do |f|
f.write 'hi'
raise 'spitespite'
end
可以这样实现:
def File.open(*args)
f = new(*args)
return f unless block_given?
yield f
ensure
f.close if block_given?
end
这种模式在 Ruby 的核心库、标准库和第三方库中被很多类实现。
与 Python 的通用上下文管理器协议更接近的实现方式是:
def with(ctx)
yield ctx.setup
ensure
ctx.teardown
end
class File
def setup; self end
alias_method :teardown, :close
end
with File.open('temp.txt', 'w') do |f|
f.write 'hi'
raise 'spitespite'
end
注意,这几乎和 Python 的例子没有区别,但它并不需要为语言增加新的语法。