RubyのPythonの「With」に相当します
-
28-09-2019 - |
質問
Pythonで、 with
ステートメントは、例外がスローされたり、関数呼び出しが返されたりするにもかかわらず、クリーンアップコードが常に呼び出されるようにするために使用されます。例えば:
with open("temp.txt", "w") as f:
f.write("hi")
raise ValueError("spitespite")
ここでは、例外が提起されていても、ファイルは閉じられています。より良い説明です ここ.
Rubyのこの構造に相当するものはありますか?または、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 Coreライブラリ、標準ライブラリ、サードパーティライブラリの多くのクラスによって実装される一般的なパターンです。
ジェネリック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
句;しかし、ブロックで物を包むことはルビーで遍在しており、これがルビーの精神で最も効率的かつ最も行われる方法です。翻訳するときは、単語用語を直接翻訳しないでください。非常に奇妙な文章が得られます。同様に、PythonからすべてがRubyに1対1の対応することを期待しないでください。
投稿したリンクから:
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、このようなもの(男、私はおそらくこれをすべて間違っている: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
Rubyで原子的にファイルに書き込むことができます。
File.write("temp.txt", "hi")
raise ValueError("spitespite")
このようなコードを書くことは、ファイルを誤って開いたままにすることが不可能であることを意味します。
いつでも使用できます try..catch..finally
ブロック、ここで finally
セクションには、クリーンアップするコードが含まれています。
編集:申し訳ありませんが、ミスポーク:あなたは欲しいです begin..rescue..ensure
.
私はあなたが探していると信じています 確認.