11


2

PythonのRubyの「with」に相当

Pythonでは、スローされる例外や関数呼び出しの戻り値に関係なく、クリーンアップコードが常に呼び出されるように、 `with`ステートメントが使用されます。 例えば:

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

ここでは、例外が発生したにもかかわらず、ファイルが閉じられます。 より良い説明は、http://effbot.org/zone/python-with-statement.htm [こちら]です。

Rubyでこの構造に相当するものはありますか? または、Rubyには継続性があるため、1つをコーディングできますか?

7 Answer


21


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の例と事実上区別できませんが、新しい構文を言語に追加する必要はないことに注意してください。


9


Rubyの同等の機能は、ブロックをFile.openメソッドに渡すことです。

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

これはRubyが使用するイディオムであり、あなたが慣れる必要があるものです。


4


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


2


他の人にいくつかの説明を追加します。クレジットは彼らに行くべきです。

確かに、Rubyでは、クリーンアップコードは他の人が言ったように、 `ensure`句にあります。しかし、物事をブロックでラップすることは、Rubyのいたるところにあり、これが最も効率的で、Rubyの精神で最も行われている方法です。 翻訳するとき、単語ごとに直接翻訳しないでください、あなたはいくつかの非常に奇妙な文を得ます。 同様に、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の方法、このようなもの(男、私はおそらくこれをすべて間違っている:D):

def controlled_executor
  begin
    do_setup
    yield
  ensure
    do_cleanup
  end
end

controlled_executor do ...
  some_code
end

明らかに、「通常の方法で呼び出される」「制御されたエグゼキューター」と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


2


次のように、Rubyでアトミックにファイルに書き込むことができます。

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

このようなコードを記述することは、誤ってファイルを開いたままにすることが不可能であることを意味します。


0


いつでも「try..catch..finally」ブロックを使用できます。「finally」セクションにはクリーンアップするコードが含まれています。

編集:申し訳ありませんが、ミスポーク:あなたは `begin..rescue..ensure`が欲しいでしょう。


0


ensureを探していると思います。