16

Python では、このwithステートメントを使用して、例外がスローされたり、関数呼び出しが返されたりしても、常にクリーンアップ コードが呼び出されるようにします。例えば:

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

ここでは、例外が発生したにもかかわらず、ファイルは閉じられています。より良い説明はこちらです。

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

4

7 に答える 7

24

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はオブジェクト指向言語です。つまり、aのコンテキストでコードのブロックを適切に実行する責任は、次のクラス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の例と実質的に区別がつかないことに注意してください。ただし、言語に新しい構文を追加する必要はありませんでした。

于 2010-10-06T18:42:15.793 に答える
12

Ruby でこれに相当するのは、ブロックを File.open メソッドに渡すことです。

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

これは Ruby が使用するイディオムであり、慣れておく必要があります。

于 2010-10-06T18:16:04.233 に答える
4

You could use Block Arguments to do this in 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:

with GetSomeObject("somefile.text") do |foo|  
    do_something_with(foo)
end  
于 2010-10-06T18:36:51.577 に答える
2

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

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

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

于 2012-08-29T12:21:21.580 に答える
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

明らかに、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
于 2010-10-06T18:31:54.617 に答える
0

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

編集: 申し訳ありませんが、ミスポーク: したいbegin..rescue..ensure.

于 2010-10-06T18:16:01.953 に答える
0

私はあなたが確実に探していると信じています。

于 2010-10-06T18:17:13.903 に答える