Ruby中等效于Python的"with"语句的是什么?

16 投票
7 回答
6293 浏览
提问于 2025-04-16 05:05

在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 的例子没有区别,但它并不需要为语言增加新的语法。

撰写回答