equivalente di Python “con” in Ruby
-
28-09-2019 - |
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?
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 .