相当于 Python 中的“with”在红宝石中

发布于 2024-09-26 05:35:24 字数 398 浏览 7 评论 0原文

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

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

这里,即使引发了异常,文件也被关闭。更好的解释是这里< /a>.

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

In Python, the with statement is used to make sure that clean-up code always gets called, regardless of exceptions being thrown or function calls returning. For example:

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

Here, the file is closed, even though an exception was raised. A better explanation is here.

Is there an equivalent for this construct in Ruby? Or can you code one up, since Ruby has continuations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

冰之心 2024-10-03 05:35: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 示例没有区别,但它不需要向该语言添加新语法。

Ruby has syntactically lightweight support for literal anonymous procedures (called blocks in Ruby). Therefore, it doesn't need a new language feature for this.

So, what you normally do, is to write a method which takes a block of code, allocates the resource, executes the block of code in the context of that resource and then closes the resource.

Something like this:

def with(klass, *args)
  yield r = klass.open(*args)
ensure
  r.close
end

You could use it like this:

with File, 'temp.txt', 'w' do |f|
  f.write 'hi'
  raise 'spitespite'
end

However, this is a very procedural way to do this. Ruby is an object-oriented language, which means that the responsibility of properly executing a block of code in the context of a File should belong to the File class:

File.open 'temp.txt', 'w' do |f|
  f.write 'hi'
  raise 'spitespite'
end

This could be implemented something like this:

def File.open(*args)
  f = new(*args)
  return f unless block_given?
  yield f
ensure
  f.close if block_given?
end

This is a general pattern that is implemented by lots of classes in the Ruby core library, standard libraries and third-party libraries.


A more close correspondence to the generic Python context manager protocol would be:

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

Note that this is virtually indistinguishable from the Python example, but it didn't require the addition of new syntax to the language.

恰似旧人归 2024-10-03 05:35:25

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

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

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

The equivalent in Ruby would be to pass a block to the File.open method.

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

This is the idiom that Ruby uses and one that you should get comfortable with.

剪不断理还乱 2024-10-03 05:35:25

您可以使用 Block Arguments 在 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  

You could use Block Arguments to do this in Ruby:

class Object  
    def with(obj)  
        obj.__enter__  
        yield  
        obj.__exit__  
    end  
end

Now, you could add __enter__ and __exit__ methods to another class and use it like this:

with GetSomeObject("somefile.text") do |foo|  
    do_something_with(foo)
end  
随风而去 2024-10-03 05:35:25

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

事实上,在 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

显然,您可以向两个受控执行器添加参数(在通常的方式),并产生(在这种情况下,您还需要向块添加参数)。因此,为了实现您上面引用的内容,

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

I'll just add some more explanations for others; credit should go to them.

Indeed, in Ruby, clean-up code is as others said, in ensure clause; but wrapping things in blocks is ubiquitous in Ruby, and this is how it is done most efficiently and most in spirit of Ruby. When translating, don't translate directly word-for-word, you will get some very strange sentences. Similarly, don't expect everything from Python to have one-to-one correspondence to Ruby.

From the link you posted:

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 way, something like this (man, I'm probably doing this all wrong :D ):

def controlled_executor
  begin
    do_setup
    yield
  ensure
    do_cleanup
  end
end

controlled_executor do ...
  some_code
end

Obviously, you can add arguments to both controlled executor (to be called in a usual fashion), and to yield (in which case you need to add arguments to the block as well). Thus, to implement what you quoted above,

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
就是爱搞怪 2024-10-03 05:35:25

在 Ruby 中可以原子地写入文件,如下所示:

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

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

It's possible to write to a file atomically in Ruby, like so:

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

Writing code like this means that it is impossible to accidentally leave a file open.

是伱的 2024-10-03 05:35:25

您始终可以使用 try..catch..finally 块,其中 finally 部分包含要清理的代码。

编辑:抱歉,说错了:你想要begin..rescue..ensure

You could always use a try..catch..finally block, where the finally section contains code to clean up.

Edit: sorry, misspoke: you'd want begin..rescue..ensure.

沦落红尘 2024-10-03 05:35:25

我相信您正在寻找ensure

I believe you are looking for ensure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文