相当于Ruby中的Python“with”

时间:2022-05-23 05:30:19

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:

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

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?

在Ruby中有这个构造的等价物吗?或者你可以编写一个代码,因为Ruby有延续吗?

7 个解决方案

#1


20  

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

Ruby对文字匿名过程(在Ruby中称为块)具有语法轻量级支持。因此,它不需要新的语言功能。

(In general, it is a bad sign, if you need to add language features. You should be able to implement everything in a library, otherwise it is a sign of bad language design.)

(一般来说,如果你需要添加语言功能,这是一个不好的标志。你应该能够在库中实现所有内容,否则它是语言设计不良的标志。)

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:

但是,这是一种非常程序化的方法。 Ruby是一种面向对象的语言,这意味着在File的上下文中正确执行代码块的责任应该属于File类:

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.

这是一个通用模式,由Ruby核心库,标准库和第三方库中的许多类实现。


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

与通用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

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

请注意,这几乎与Python示例无法区分,但它不需要为该语言添加新语法。

#2


8  

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

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

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.

这是Ruby使用的成语,也是你应该习惯的成语。

#3


4  

You could use Block Arguments to do this in Ruby:

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

现在,您可以将__enter__和__exit__方法添加到另一个类并使用它如下:

with GetSomeObject("somefile.text") do |foo|  
    do_something_with(foo)
end  

#4


2  

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.

实际上,在Ruby中,清理代码与其他人所说的一样,在ensure子句中;但是在Ruby中无处不在包装内容,这就是它如何最有效地完成,并且最具有Ruby精神。翻译时,不要直接逐字翻译,你会得到一些非常奇怪的句子。同样,不要指望Python中的所有内容都与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 ):

Ruby方式,类似这样的东西(伙计,我可能做错了这个: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

#5


2  

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

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

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

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

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

#6


0  

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

你总是可以使用try..catch..finally块,其中finally部分包含要清理的代码。

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

编辑:抱歉,misspoke:你想要begin..rescue..ensure。

#7


0  

I believe you are looking for ensure.

我相信你在寻求确保。

#1


20  

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

Ruby对文字匿名过程(在Ruby中称为块)具有语法轻量级支持。因此,它不需要新的语言功能。

(In general, it is a bad sign, if you need to add language features. You should be able to implement everything in a library, otherwise it is a sign of bad language design.)

(一般来说,如果你需要添加语言功能,这是一个不好的标志。你应该能够在库中实现所有内容,否则它是语言设计不良的标志。)

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:

但是,这是一种非常程序化的方法。 Ruby是一种面向对象的语言,这意味着在File的上下文中正确执行代码块的责任应该属于File类:

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.

这是一个通用模式,由Ruby核心库,标准库和第三方库中的许多类实现。


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

与通用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

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

请注意,这几乎与Python示例无法区分,但它不需要为该语言添加新语法。

#2


8  

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

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

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.

这是Ruby使用的成语,也是你应该习惯的成语。

#3


4  

You could use Block Arguments to do this in Ruby:

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

现在,您可以将__enter__和__exit__方法添加到另一个类并使用它如下:

with GetSomeObject("somefile.text") do |foo|  
    do_something_with(foo)
end  

#4


2  

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.

实际上,在Ruby中,清理代码与其他人所说的一样,在ensure子句中;但是在Ruby中无处不在包装内容,这就是它如何最有效地完成,并且最具有Ruby精神。翻译时,不要直接逐字翻译,你会得到一些非常奇怪的句子。同样,不要指望Python中的所有内容都与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 ):

Ruby方式,类似这样的东西(伙计,我可能做错了这个: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

#5


2  

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

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

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

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

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

#6


0  

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

你总是可以使用try..catch..finally块,其中finally部分包含要清理的代码。

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

编辑:抱歉,misspoke:你想要begin..rescue..ensure。

#7


0  

I believe you are looking for ensure.

我相信你在寻求确保。