Эквивалент питона "с" в Руби
-
28-09-2019 - |
Вопрос
В Python, with
Заявление используется, чтобы убедиться, что код очистки всегда вызывается, независимо от выдачи исключений или функциональных вызовов. Например:
with open("temp.txt", "w") as f:
f.write("hi")
raise ValueError("spitespite")
Здесь файл закрыт, хотя исключение было поднято. Лучшее объяснение здесь.
Есть ли эквивалент для этой конструкции в Руби? Или вы можете кодировать один, так как 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
Это общий шаблон, который реализуется множеством классов в библиотеке Cry Core, стандартных библиотек и сторонних библиотек.
Более тесная переписка с генерическим протоколом Python Context Manager будет:
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(...) 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, Clean-Up Code как другие сказали, в ensure
пункт; Но обертывание в блоках повсеместно повсеместно в рубине, и вот как это делается наиболее эффективно и большинство в духе Руби. При переводе не переводите непосредственно для Word-Word, вы получите несколько очень странных предложений. Аналогичным образом, не ожидайте, что у Python не ожидайте, чтобы переписываться на один к одному рубиному.
Из ссылки вы разместили:
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
Рубиный путь, что-то вроде этого (человек, я, вероятно, делаю это все неправильно: 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
Раздел содержит код для очистки.
Редактировать: Извините, MissPoke: вы хотите begin..rescue..ensure
.
Я верю, что вы ищете гарантировать.