相当于Python中Ruby中的“with”

问题描述 投票:0回答:7

在 Python 中,

with
语句用于确保始终调用清理代码,无论抛出异常或返回函数调用。例如:

with open("temp.txt", "w") as f:
    f.write("hi")
    raise ValueError("spitespite")

在这里,即使引发了异常,文件也已关闭。更好的解释是这里

Ruby 中是否有与此结构等效的结构?或者你可以编写一个代码吗,因为 Ruby 有延续性?

python ruby language-features with-statement control-flow
7个回答
25
投票

Ruby 在语法上对匿名过程提供轻量级支持(在 Ruby 中称为 blocks)。因此,它不需要新的语言功能。

因此,您通常要做的就是编写一个方法,该方法接受一段代码,分配资源,在该资源的上下文中执行该代码块,然后关闭该资源。

类似这样的:

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 示例几乎没有区别,但它不需要向该语言添加新语法。


12
投票

Ruby 中的等效方法是将块传递给 File.open 方法。

File.open(...) do |file|
  #do stuff with file
end  #file is closed

这是 Ruby 使用的习惯用法,您应该熟悉它。


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  

2
投票

我只是为其他人添加更多解释;功劳应该归于他们。

确实,在 Ruby 中,清理代码正如其他人所说,在

ensure
子句中;但是在 Ruby 中,将事物包装在块中是普遍存在的,而且这是最有效且最符合 Ruby 精神的完成方式。翻译的时候,不要直接逐字翻译,你会得到一些很奇怪的句子。同样,不要期望 Python 中的所有内容都与 Ruby 具有一一对应关系。

从您发布的链接:

class controlled_execution:
    def __enter__(self):
        set things up
        return thing
    def __exit__(self, type, value, traceback):
        tear things down

with controlled_execution() as thing:
     some code

Ruby 方式,像这样(伙计,我可能做错了:D):

def controlled_executor
  begin
    do_setup
    yield
  ensure
    do_cleanup
  end
end

controlled_executor do ...
  some_code
end

显然,您可以向

controlled executor
(以通常的方式调用)和yield(在这种情况下,您还需要向块添加参数)添加参数。因此,为了实现您上面引用的内容,

class File
  def my_open(file, mode="r")
    handle = open(file, mode)
    begin
      yield handle
    ensure
      handle.close
    end
  end
end

File.my_open("temp.txt", "w") do |f|
  f.write("hi")
  raise Exception.new("spitesprite")
end

2
投票

可以在 Ruby 中自动写入文件,如下所示:

File.write("temp.txt", "hi")
raise ValueError("spitespite")

像这样编写代码意味着不可能意外地打开文件。


0
投票

您始终可以使用

try..catch..finally
块,其中
finally
部分包含要清理的代码。

编辑:抱歉,说错了:你想要

begin..rescue..ensure


0
投票

我相信您正在寻找确保

© www.soinside.com 2019 - 2024. All rights reserved.