Domanda

In Python, l'istruzione with viene utilizzata per fare in modo che il codice di clean-up sempre viene chiamato, a prescindere di eccezioni gettati o chiamate di funzione di ritorno. Ad esempio:

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

Qui, il file viene chiuso, anche se un'eccezione è stata sollevata. Una spiegazione migliore è qui .

C'è un equivalente per questo costrutto in Ruby? O si può codice uno, dal momento che Ruby ha continuazioni?

È stato utile?

Soluzione

Ruby ha il supporto sintatticamente leggero per le procedure anonime letterali (chiamato blocchi in Ruby). Pertanto, non ha bisogno di una nuova funzionalità lingua.

(In generale, è un brutto segno, se è necessario aggiungere caratteristiche del linguaggio. Si dovrebbe essere in grado di implementare tutto in una libreria, altrimenti è un segno del design linguaggio scurrile.)

Allora, che cosa si fa normalmente, è quello di scrivere un metodo che prende un blocco di codice, alloca le risorse, esegue il blocco di codice nel contesto di tale risorsa e quindi chiude la risorsa.

Qualcosa di simile a questo:

def with(klass, *args)
  yield r = klass.open(*args)
ensure
  r.close
end

Si può usare in questo modo:

with File, 'temp.txt', 'w' do |f|
  f.write 'hi'
  raise 'spitespite'
end

Tuttavia, questo è un modo molto procedurale per fare questo. Ruby è un linguaggio orientato agli oggetti, il che significa che la responsabilità di eseguire correttamente un blocco di codice nel contesto di una File dovrebbe appartenere alla classe File:

File.open 'temp.txt', 'w' do |f|
  f.write 'hi'
  raise 'spitespite'
end

Questo potrebbe essere implementato qualcosa di simile:

def File.open(*args)
  f = new(*args)
  return f unless block_given?
  yield f
ensure
  f.close if block_given?
end

Questo è un modello generale che viene implementato da un sacco di classi nella libreria di base di Ruby, librerie standard e le librerie di terze parti.


Un altro chiudi corrispondenza con il protocollo generico contesto gestore pitone sarebbe:

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

Si noti che questo è praticamente indistinguibile da l'esempio di Python, ma non ha richiesto l'aggiunta di nuova sintassi della lingua.

Altri suggerimenti

L'equivalente in Ruby sarebbe passare un blocco al metodo File.open.

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

Questo è il linguaggio che usi di Ruby e uno che si dovrebbe prendere confidenza con.

Si potrebbe utilizzare Block Argomenti per fare questo in Ruby:

class Object  
    def with(obj)  
        obj.__enter__  
        yield  
        obj.__exit__  
    end  
end

Ora, si potrebbe aggiungere __enter__ e __exit__ metodi per un'altra classe e usarlo in questo modo:

with GetSomeObject("somefile.text") do |foo|  
    do_something_with(foo)
end  

mi limiterò a aggiungere un po 'di più spiegazioni per gli altri; credito dovrebbe andare da loro.

In effetti, in Ruby, codice pulito-up è come altri hanno detto, nella clausola ensure; ma avvolgendo le cose in blocchi è onnipresente in Ruby, e questo è come è fatto in modo più efficiente e più nello spirito di Ruby. Quando si traduce, non si traducono direttamente parola per parola, si otterrà alcune frasi molto strane. Allo stesso modo, non aspettatevi di tutto, dai Python per avere uno-a-uno corrispondenza con Ruby.

Dal link che hai postato:

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

modo Ruby, qualcosa di simile (l'uomo, probabilmente sto facendo questo tutto sbagliato: D):

def controlled_executor
  begin
    do_setup
    yield
  ensure
    do_cleanup
  end
end

controlled_executor do ...
  some_code
end

Ovviamente, è possibile aggiungere argomenti sia controlled executor (da chiamare nel modo usuale), e la resa (nel qual caso è necessario aggiungere argomenti al blocco pure). Così, per attuare ciò che hai citato sopra,

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

E 'possibile scrivere in un file atomicamente in Ruby, in questo modo:

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

La scrittura di codice come questo significa che è impossibile lasciare accidentalmente un file aperto.

Si può sempre utilizzare un blocco try..catch..finally, dove la sezione finally contiene il codice per ripulire.

Edit: sorry, misspoke:. Che ci si vuole begin..rescue..ensure

Credo che siete alla ricerca di garantire .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top