Вопрос

В 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.

Я верю, что вы ищете гарантировать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top