在 Python 中,
with
语句用于确保始终调用清理代码,无论抛出异常或返回函数调用。例如:
with open("temp.txt", "w") as f:
f.write("hi")
raise ValueError("spitespite")
在这里,即使引发了异常,文件也已关闭。更好的解释是这里。
Ruby 中是否有与此结构等效的结构?或者你可以编写一个代码吗,因为 Ruby 有延续性?
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 示例几乎没有区别,但它不需要向该语言添加新语法。
Ruby 中的等效方法是将块传递给 File.open 方法。
File.open(...) do |file|
#do stuff with file
end #file is closed
这是 Ruby 使用的习惯用法,您应该熟悉它。
您可以使用块参数在 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
我只是为其他人添加更多解释;功劳应该归于他们。
确实,在 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
可以在 Ruby 中自动写入文件,如下所示:
File.write("temp.txt", "hi")
raise ValueError("spitespite")
像这样编写代码意味着不可能意外地打开文件。
您始终可以使用
try..catch..finally
块,其中 finally
部分包含要清理的代码。
编辑:抱歉,说错了:你想要
begin..rescue..ensure
。
我相信您正在寻找确保。