在Python中,with
语句用于确保始终调用清理代码,无论抛出异常还是返回函数调用。例如:
with open("temp.txt", "w") as f:
f.write("hi")
raise ValueError("spitespite")
此处,文件已关闭,即使引发了异常。更好的解释是here。
Ruby中有这个构造的等价物吗?或者你可以编写一个代码,因为Ruby有延续吗?
答案 0 :(得分:22)
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示例无法区分,但它不需要在语言中添加新语法。
答案 1 :(得分:9)
Ruby中的等价物是将块传递给File.open方法。
File.open(...) do |file|
#do stuff with file
end #file is closed
这是Ruby使用的习语,也是你应该习惯的习惯。
答案 2 :(得分:4)
您可以使用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
答案 3 :(得分: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
添加参数(以通常的方式调用),并且可以产生(在这种情况下,您还需要为块添加参数)。因此,要实现上面引用的内容,
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
答案 4 :(得分:2)
可以在Ruby中原子地写入文件,如下所示:
File.write("temp.txt", "hi")
raise ValueError("spitespite")
编写这样的代码意味着不可能意外地打开文件。
答案 5 :(得分:0)
您始终可以使用try..catch..finally
块,其中finally
部分包含要清理的代码。
编辑:对不起,misspoke:你想要begin..rescue..ensure
。
答案 6 :(得分:0)
我相信您正在寻找ensure。